mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge branch 'master' of github.com:github/codeql into SharedDataflow
To avoid CodeScan check failing
This commit is contained in:
4
.github/codeql/codeql-config.yml
vendored
Normal file
4
.github/codeql/codeql-config.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
name: "CodeQL config"
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
52
.github/workflows/codeql-analysis.yml
vendored
Normal file
52
.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
name: "Code scanning - action"
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
schedule:
|
||||
- cron: '0 9 * * 1'
|
||||
|
||||
jobs:
|
||||
CodeQL-Build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
|
||||
# If this run was triggered by a pull request event, then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
- run: git checkout HEAD^2
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
with:
|
||||
languages: csharp
|
||||
config-file: ./.github/codeql/codeql-config.yml
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
@@ -46,6 +46,7 @@
|
||||
| Hard-coded credentials (`js/hardcoded-credentials`) | More results | This query now recognizes hard-coded credentials sent via HTTP authorization headers. |
|
||||
| Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | More results | This query now recognizes additional url scheme checks. |
|
||||
| Misspelled variable name (`js/misspelled-variable-name`) | Message changed | The message for this query now correctly identifies the misspelled variable in additional cases. |
|
||||
| Non-linear pattern (`js/non-linear-pattern`) | Fewer duplicates and message changed | This query now generates fewer duplicate alerts and has a clearer explanation in case of type annotations used in a pattern. |
|
||||
| Prototype pollution in utility function (`js/prototype-pollution-utility`) | More results | This query now recognizes additional utility functions as vulnerable to prototype polution. |
|
||||
| Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional command execution calls. |
|
||||
| Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional file system calls. |
|
||||
|
||||
@@ -101,10 +101,18 @@
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/UseSoundEscapeAnalysis.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/UseSoundEscapeAnalysis.qll"
|
||||
],
|
||||
"IR IRFunctionBase": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/IRFunctionBase.qll"
|
||||
],
|
||||
"IR Operand Tag": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll"
|
||||
],
|
||||
"IR TInstruction":[
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll"
|
||||
],
|
||||
"IR TIRVariable":[
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/TIRVariable.qll"
|
||||
@@ -182,6 +190,11 @@
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRBlockImports.qll"
|
||||
],
|
||||
"C++ IR IRFunctionImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll"
|
||||
],
|
||||
"C++ IR IRVariableImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRVariableImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll",
|
||||
@@ -292,6 +305,10 @@
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRBlockImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll"
|
||||
],
|
||||
"C# IR IRFunctionImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRFunctionImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll"
|
||||
],
|
||||
"C# IR IRVariableImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRVariableImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll"
|
||||
|
||||
@@ -23,7 +23,7 @@ import semmle.code.cpp.ir.ValueNumbering
|
||||
class NullInstruction extends ConstantValueInstruction {
|
||||
NullInstruction() {
|
||||
this.getValue() = "0" and
|
||||
this.getResultType().getUnspecifiedType() instanceof PointerType
|
||||
this.getResultIRType() instanceof IRAddressType
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ predicate explicitNullTestOfInstruction(Instruction checked, Instruction bool) {
|
||||
bool =
|
||||
any(ConvertInstruction convert |
|
||||
checked = convert.getUnary() and
|
||||
convert.getResultType() instanceof BoolType and
|
||||
checked.getResultType() instanceof PointerType
|
||||
convert.getResultIRType() instanceof IRBooleanType and
|
||||
checked.getResultIRType() instanceof IRAddressType
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,3 +2,5 @@
|
||||
- qlpack: codeql-cpp
|
||||
- apply: code-scanning-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
- apply: codeql-suites/exclude-slow-queries.yml
|
||||
from: codeql-cpp
|
||||
|
||||
@@ -2,16 +2,8 @@
|
||||
- qlpack: codeql-cpp
|
||||
- apply: lgtm-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
# These queries are infeasible to compute on large projects:
|
||||
- exclude:
|
||||
query path:
|
||||
- Security/CWE/CWE-497/ExposedSystemData.ql
|
||||
- Critical/DescriptorMayNotBeClosed.ql
|
||||
- Critical/DescriptorNeverClosed.ql
|
||||
- Critical/FileMayNotBeClosed.ql
|
||||
- Critical/FileNeverClosed.ql
|
||||
- Critical/MemoryMayNotBeFreed.ql
|
||||
- Critical/MemoryNeverFreed.ql
|
||||
- apply: codeql-suites/exclude-slow-queries.yml
|
||||
from: codeql-cpp
|
||||
# These are only for IDE use.
|
||||
- exclude:
|
||||
tags contain:
|
||||
|
||||
6
cpp/ql/src/codeql-suites/cpp-security-and-quality.qls
Normal file
6
cpp/ql/src/codeql-suites/cpp-security-and-quality.qls
Normal file
@@ -0,0 +1,6 @@
|
||||
- description: Security-and-quality queries for C and C++
|
||||
- qlpack: codeql-cpp
|
||||
- apply: security-and-quality-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
- apply: codeql-suites/exclude-slow-queries.yml
|
||||
from: codeql-cpp
|
||||
6
cpp/ql/src/codeql-suites/cpp-security-extended.qls
Normal file
6
cpp/ql/src/codeql-suites/cpp-security-extended.qls
Normal file
@@ -0,0 +1,6 @@
|
||||
- description: Security-extended queries for C and C++
|
||||
- qlpack: codeql-cpp
|
||||
- apply: security-extended-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
- apply: codeql-suites/exclude-slow-queries.yml
|
||||
from: codeql-cpp
|
||||
11
cpp/ql/src/codeql-suites/exclude-slow-queries.yml
Normal file
11
cpp/ql/src/codeql-suites/exclude-slow-queries.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
- description: C/C++ queries which are infeasible to compute on large projects
|
||||
# These queries are infeasible to compute on large projects:
|
||||
- exclude:
|
||||
query path:
|
||||
- Security/CWE/CWE-497/ExposedSystemData.ql
|
||||
- Critical/DescriptorMayNotBeClosed.ql
|
||||
- Critical/DescriptorNeverClosed.ql
|
||||
- Critical/FileMayNotBeClosed.ql
|
||||
- Critical/FileNeverClosed.ql
|
||||
- Critical/MemoryMayNotBeFreed.ql
|
||||
- Critical/MemoryNeverFreed.ql
|
||||
@@ -32,6 +32,7 @@ import semmle.code.cpp.Enum
|
||||
import semmle.code.cpp.Member
|
||||
import semmle.code.cpp.Field
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.MemberFunction
|
||||
import semmle.code.cpp.Parameter
|
||||
import semmle.code.cpp.Variable
|
||||
import semmle.code.cpp.Initializer
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/**
|
||||
* Provides classes for working with functions, including C++ constructors, destructors,
|
||||
* user-defined operators, and template functions.
|
||||
* Provides classes for working with functions, including template functions.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Location
|
||||
@@ -703,429 +702,6 @@ class TopLevelFunction extends Function {
|
||||
override string getCanonicalQLClass() { result = "TopLevelFunction" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ function declared as a member of a class [N4140 9.3]. This includes
|
||||
* static member functions. For example the functions `MyStaticMemberFunction`
|
||||
* and `MyMemberFunction` in:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* void MyMemberFunction() {
|
||||
* DoSomething();
|
||||
* }
|
||||
*
|
||||
* static void MyStaticMemberFunction() {
|
||||
* DoSomething();
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class MemberFunction extends Function {
|
||||
MemberFunction() { this.isMember() }
|
||||
|
||||
override string getCanonicalQLClass() {
|
||||
not this instanceof CopyAssignmentOperator and
|
||||
not this instanceof MoveAssignmentOperator and
|
||||
result = "MemberFunction"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of parameters of this function, including any implicit
|
||||
* `this` parameter.
|
||||
*/
|
||||
override int getEffectiveNumberOfParameters() {
|
||||
if isStatic() then result = getNumberOfParameters() else result = getNumberOfParameters() + 1
|
||||
}
|
||||
|
||||
/** Holds if this member is private. */
|
||||
predicate isPrivate() { this.hasSpecifier("private") }
|
||||
|
||||
/** Holds if this member is protected. */
|
||||
predicate isProtected() { this.hasSpecifier("protected") }
|
||||
|
||||
/** Holds if this member is public. */
|
||||
predicate isPublic() { this.hasSpecifier("public") }
|
||||
|
||||
/** Holds if this function overrides that function. */
|
||||
predicate overrides(MemberFunction that) {
|
||||
overrides(underlyingElement(this), unresolveElement(that))
|
||||
}
|
||||
|
||||
/** Gets a directly overridden function. */
|
||||
MemberFunction getAnOverriddenFunction() { this.overrides(result) }
|
||||
|
||||
/** Gets a directly overriding function. */
|
||||
MemberFunction getAnOverridingFunction() { result.overrides(this) }
|
||||
|
||||
/**
|
||||
* Gets the declaration entry for this member function that is within the
|
||||
* class body.
|
||||
*/
|
||||
FunctionDeclarationEntry getClassBodyDeclarationEntry() {
|
||||
if strictcount(getADeclarationEntry()) = 1
|
||||
then result = getDefinition()
|
||||
else (
|
||||
result = getADeclarationEntry() and result != getDefinition()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ virtual function. For example the two functions called
|
||||
* `myVirtualFunction` in the following code are each a
|
||||
* `VirtualFunction`:
|
||||
* ```
|
||||
* class A {
|
||||
* public:
|
||||
* virtual void myVirtualFunction() = 0;
|
||||
* };
|
||||
*
|
||||
* class B: public A {
|
||||
* public:
|
||||
* virtual void myVirtualFunction() {
|
||||
* doSomething();
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class VirtualFunction extends MemberFunction {
|
||||
VirtualFunction() { this.hasSpecifier("virtual") or purefunctions(underlyingElement(this)) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "VirtualFunction" }
|
||||
|
||||
/** Holds if this virtual function is pure. */
|
||||
predicate isPure() { this instanceof PureVirtualFunction }
|
||||
|
||||
/**
|
||||
* Holds if this function was declared with the `override` specifier
|
||||
* [N4140 10.3].
|
||||
*/
|
||||
predicate isOverrideExplicit() { this.hasSpecifier("override") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ pure virtual function [N4140 10.4]. For example the first function
|
||||
* called `myVirtualFunction` in the following code:
|
||||
* ```
|
||||
* class A {
|
||||
* public:
|
||||
* virtual void myVirtualFunction() = 0;
|
||||
* };
|
||||
*
|
||||
* class B: public A {
|
||||
* public:
|
||||
* virtual void myVirtualFunction() {
|
||||
* doSomething();
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class PureVirtualFunction extends VirtualFunction {
|
||||
PureVirtualFunction() { purefunctions(underlyingElement(this)) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "PureVirtualFunction" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A const C++ member function [N4140 9.3.1/4]. A const function has the
|
||||
* `const` specifier and does not modify the state of its class. For example
|
||||
* the member function `day` in the following code:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* ...
|
||||
*
|
||||
* int day() const {
|
||||
* return d;
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class ConstMemberFunction extends MemberFunction {
|
||||
ConstMemberFunction() { this.hasSpecifier("const") }
|
||||
|
||||
override string getCanonicalQLClass() { result = "ConstMemberFunction" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ constructor [N4140 12.1]. For example the function `MyClass` in the
|
||||
* following code is a constructor:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass() {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class Constructor extends MemberFunction {
|
||||
Constructor() { functions(underlyingElement(this), _, 2) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "Constructor" }
|
||||
|
||||
/**
|
||||
* Holds if this constructor serves as a default constructor.
|
||||
*
|
||||
* This holds for constructors with zero formal parameters. It also holds
|
||||
* for constructors which have a non-zero number of formal parameters,
|
||||
* provided that every parameter has a default value.
|
||||
*/
|
||||
predicate isDefault() { forall(Parameter p | p = this.getAParameter() | p.hasInitializer()) }
|
||||
|
||||
/**
|
||||
* Gets an entry in the constructor's initializer list, or a
|
||||
* compiler-generated action which initializes a base class or member
|
||||
* variable.
|
||||
*/
|
||||
ConstructorInit getAnInitializer() { result = getInitializer(_) }
|
||||
|
||||
/**
|
||||
* Gets an entry in the constructor's initializer list, or a
|
||||
* compiler-generated action which initializes a base class or member
|
||||
* variable. The index specifies the order in which the initializer is
|
||||
* to be evaluated.
|
||||
*/
|
||||
ConstructorInit getInitializer(int i) {
|
||||
exprparents(unresolveElement(result), i, underlyingElement(this))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that defines an implicit conversion.
|
||||
*/
|
||||
abstract class ImplicitConversionFunction extends MemberFunction {
|
||||
/** Gets the type this `ImplicitConversionFunction` takes as input. */
|
||||
abstract Type getSourceType();
|
||||
|
||||
/** Gets the type this `ImplicitConversionFunction` converts to. */
|
||||
abstract Type getDestType();
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ constructor that also defines an implicit conversion. For example the
|
||||
* function `MyClass` in the following code is a `ConversionConstructor`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass(const MyOtherClass &from) {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class ConversionConstructor extends Constructor, ImplicitConversionFunction {
|
||||
ConversionConstructor() {
|
||||
strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
|
||||
not hasSpecifier("explicit") and
|
||||
not this instanceof CopyConstructor
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() {
|
||||
not this instanceof MoveConstructor and result = "ConversionConstructor"
|
||||
}
|
||||
|
||||
/** Gets the type this `ConversionConstructor` takes as input. */
|
||||
override Type getSourceType() { result = this.getParameter(0).getType() }
|
||||
|
||||
/** Gets the type this `ConversionConstructor` is a constructor of. */
|
||||
override Type getDestType() { result = this.getDeclaringType() }
|
||||
}
|
||||
|
||||
private predicate hasCopySignature(MemberFunction f) {
|
||||
f.getParameter(0).getUnspecifiedType().(LValueReferenceType).getBaseType() = f.getDeclaringType()
|
||||
}
|
||||
|
||||
private predicate hasMoveSignature(MemberFunction f) {
|
||||
f.getParameter(0).getUnspecifiedType().(RValueReferenceType).getBaseType() = f.getDeclaringType()
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ copy constructor [N4140 12.8]. For example the function `MyClass` in
|
||||
* the following code is a `CopyConstructor`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass(const MyClass &from) {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* As per the standard, a copy constructor of class `T` is a non-template
|
||||
* constructor whose first parameter has type `T&`, `const T&`, `volatile
|
||||
* T&`, or `const volatile T&`, and either there are no other parameters,
|
||||
* or the rest of the parameters all have default values.
|
||||
*
|
||||
* For template classes, it can generally not be determined until instantiation
|
||||
* whether a constructor is a copy constructor. For such classes, `CopyConstructor`
|
||||
* over-approximates the set of copy constructors; if an under-approximation is
|
||||
* desired instead, see the member predicate
|
||||
* `mayNotBeCopyConstructorInInstantiation`.
|
||||
*/
|
||||
class CopyConstructor extends Constructor {
|
||||
CopyConstructor() {
|
||||
hasCopySignature(this) and
|
||||
(
|
||||
// The rest of the parameters all have default values
|
||||
forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer())
|
||||
or
|
||||
// or this is a template class, in which case the default values have
|
||||
// not been extracted even if they exist. In that case, we assume that
|
||||
// there are default values present since that is the most common case
|
||||
// in real-world code.
|
||||
getDeclaringType() instanceof TemplateClass
|
||||
) and
|
||||
not exists(getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "CopyConstructor" }
|
||||
|
||||
/**
|
||||
* Holds if we cannot determine that this constructor will become a copy
|
||||
* constructor in all instantiations. Depending on template parameters of the
|
||||
* enclosing class, this may become an ordinary constructor or a copy
|
||||
* constructor.
|
||||
*/
|
||||
predicate mayNotBeCopyConstructorInInstantiation() {
|
||||
// In general, default arguments of template classes can only be
|
||||
// type-checked for each template instantiation; if an argument in an
|
||||
// instantiation fails to type-check then the corresponding parameter has
|
||||
// no default argument in the instantiation.
|
||||
getDeclaringType() instanceof TemplateClass and
|
||||
getNumberOfParameters() > 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ move constructor [N4140 12.8]. For example the function `MyClass` in
|
||||
* the following code is a `MoveConstructor`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass(MyClass &&from) {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* As per the standard, a move constructor of class `T` is a non-template
|
||||
* constructor whose first parameter is `T&&`, `const T&&`, `volatile T&&`,
|
||||
* or `const volatile T&&`, and either there are no other parameters, or
|
||||
* the rest of the parameters all have default values.
|
||||
*
|
||||
* For template classes, it can generally not be determined until instantiation
|
||||
* whether a constructor is a move constructor. For such classes, `MoveConstructor`
|
||||
* over-approximates the set of move constructors; if an under-approximation is
|
||||
* desired instead, see the member predicate
|
||||
* `mayNotBeMoveConstructorInInstantiation`.
|
||||
*/
|
||||
class MoveConstructor extends Constructor {
|
||||
MoveConstructor() {
|
||||
hasMoveSignature(this) and
|
||||
(
|
||||
// The rest of the parameters all have default values
|
||||
forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer())
|
||||
or
|
||||
// or this is a template class, in which case the default values have
|
||||
// not been extracted even if they exist. In that case, we assume that
|
||||
// there are default values present since that is the most common case
|
||||
// in real-world code.
|
||||
getDeclaringType() instanceof TemplateClass
|
||||
) and
|
||||
not exists(getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "MoveConstructor" }
|
||||
|
||||
/**
|
||||
* Holds if we cannot determine that this constructor will become a move
|
||||
* constructor in all instantiations. Depending on template parameters of the
|
||||
* enclosing class, this may become an ordinary constructor or a move
|
||||
* constructor.
|
||||
*/
|
||||
predicate mayNotBeMoveConstructorInInstantiation() {
|
||||
// In general, default arguments of template classes can only be
|
||||
// type-checked for each template instantiation; if an argument in an
|
||||
// instantiation fails to type-check then the corresponding parameter has
|
||||
// no default argument in the instantiation.
|
||||
getDeclaringType() instanceof TemplateClass and
|
||||
getNumberOfParameters() > 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ constructor that takes no arguments ('default' constructor). This
|
||||
* is the constructor that is invoked when no initializer is given. For
|
||||
* example the function `MyClass` in the following code is a
|
||||
* `NoArgConstructor`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass() {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class NoArgConstructor extends Constructor {
|
||||
NoArgConstructor() { this.getNumberOfParameters() = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ destructor [N4140 12.4]. For example the function `~MyClass` in the
|
||||
* following code is a destructor:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* ~MyClass() {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class Destructor extends MemberFunction {
|
||||
Destructor() { functions(underlyingElement(this), _, 3) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "Destructor" }
|
||||
|
||||
/**
|
||||
* Gets a compiler-generated action which destructs a base class or member
|
||||
* variable.
|
||||
*/
|
||||
DestructorDestruction getADestruction() { result = getDestruction(_) }
|
||||
|
||||
/**
|
||||
* Gets a compiler-generated action which destructs a base class or member
|
||||
* variable. The index specifies the order in which the destruction should
|
||||
* be evaluated.
|
||||
*/
|
||||
DestructorDestruction getDestruction(int i) {
|
||||
exprparents(unresolveElement(result), i, underlyingElement(this))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ conversion operator [N4140 12.3.2]. For example the function
|
||||
* `operator int` in the following code is a `ConversionOperator`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* operator int();
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class ConversionOperator extends MemberFunction, ImplicitConversionFunction {
|
||||
ConversionOperator() { functions(underlyingElement(this), _, 4) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "ConversionOperator" }
|
||||
|
||||
override Type getSourceType() { result = this.getDeclaringType() }
|
||||
|
||||
override Type getDestType() { result = this.getType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ user-defined operator [N4140 13.5].
|
||||
*/
|
||||
@@ -1137,64 +713,6 @@ class Operator extends Function {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ copy assignment operator [N4140 12.8]. For example the function
|
||||
* `operator=` in the following code is a `CopyAssignmentOperator`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass &operator=(const MyClass &other);
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* As per the standard, a copy assignment operator of class `T` is a
|
||||
* non-template non-static member function with the name `operator=` that
|
||||
* takes exactly one parameter of type `T`, `T&`, `const T&`, `volatile
|
||||
* T&`, or `const volatile T&`.
|
||||
*/
|
||||
class CopyAssignmentOperator extends Operator {
|
||||
CopyAssignmentOperator() {
|
||||
hasName("operator=") and
|
||||
(
|
||||
hasCopySignature(this)
|
||||
or
|
||||
// Unlike CopyConstructor, this member allows a non-reference
|
||||
// parameter.
|
||||
getParameter(0).getUnspecifiedType() = getDeclaringType()
|
||||
) and
|
||||
not exists(this.getParameter(1)) and
|
||||
not exists(getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "CopyAssignmentOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ move assignment operator [N4140 12.8]. For example the function
|
||||
* `operator=` in the following code is a `MoveAssignmentOperator`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass &operator=(MyClass &&other);
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* As per the standard, a move assignment operator of class `T` is a
|
||||
* non-template non-static member function with the name `operator=` that
|
||||
* takes exactly one parameter of type `T&&`, `const T&&`, `volatile T&&`,
|
||||
* or `const volatile T&&`.
|
||||
*/
|
||||
class MoveAssignmentOperator extends Operator {
|
||||
MoveAssignmentOperator() {
|
||||
hasName("operator=") and
|
||||
hasMoveSignature(this) and
|
||||
not exists(this.getParameter(1)) and
|
||||
not exists(getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "MoveAssignmentOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ function which has a non-empty template argument list. For example
|
||||
* the function `myTemplateFunction` in the following code:
|
||||
|
||||
487
cpp/ql/src/semmle/code/cpp/MemberFunction.qll
Normal file
487
cpp/ql/src/semmle/code/cpp/MemberFunction.qll
Normal file
@@ -0,0 +1,487 @@
|
||||
/**
|
||||
* Provides classes for working with C++ member functions, constructors, destructors,
|
||||
* and user-defined operators.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* A C++ function declared as a member of a class [N4140 9.3]. This includes
|
||||
* static member functions. For example the functions `MyStaticMemberFunction`
|
||||
* and `MyMemberFunction` in:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* void MyMemberFunction() {
|
||||
* DoSomething();
|
||||
* }
|
||||
*
|
||||
* static void MyStaticMemberFunction() {
|
||||
* DoSomething();
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class MemberFunction extends Function {
|
||||
MemberFunction() { this.isMember() }
|
||||
|
||||
override string getCanonicalQLClass() {
|
||||
not this instanceof CopyAssignmentOperator and
|
||||
not this instanceof MoveAssignmentOperator and
|
||||
result = "MemberFunction"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of parameters of this function, including any implicit
|
||||
* `this` parameter.
|
||||
*/
|
||||
override int getEffectiveNumberOfParameters() {
|
||||
if isStatic() then result = getNumberOfParameters() else result = getNumberOfParameters() + 1
|
||||
}
|
||||
|
||||
/** Holds if this member is private. */
|
||||
predicate isPrivate() { this.hasSpecifier("private") }
|
||||
|
||||
/** Holds if this member is protected. */
|
||||
predicate isProtected() { this.hasSpecifier("protected") }
|
||||
|
||||
/** Holds if this member is public. */
|
||||
predicate isPublic() { this.hasSpecifier("public") }
|
||||
|
||||
/** Holds if this function overrides that function. */
|
||||
predicate overrides(MemberFunction that) {
|
||||
overrides(underlyingElement(this), unresolveElement(that))
|
||||
}
|
||||
|
||||
/** Gets a directly overridden function. */
|
||||
MemberFunction getAnOverriddenFunction() { this.overrides(result) }
|
||||
|
||||
/** Gets a directly overriding function. */
|
||||
MemberFunction getAnOverridingFunction() { result.overrides(this) }
|
||||
|
||||
/**
|
||||
* Gets the declaration entry for this member function that is within the
|
||||
* class body.
|
||||
*/
|
||||
FunctionDeclarationEntry getClassBodyDeclarationEntry() {
|
||||
if strictcount(getADeclarationEntry()) = 1
|
||||
then result = getDefinition()
|
||||
else (
|
||||
result = getADeclarationEntry() and result != getDefinition()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ virtual function. For example the two functions called
|
||||
* `myVirtualFunction` in the following code are each a
|
||||
* `VirtualFunction`:
|
||||
* ```
|
||||
* class A {
|
||||
* public:
|
||||
* virtual void myVirtualFunction() = 0;
|
||||
* };
|
||||
*
|
||||
* class B: public A {
|
||||
* public:
|
||||
* virtual void myVirtualFunction() {
|
||||
* doSomething();
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class VirtualFunction extends MemberFunction {
|
||||
VirtualFunction() { this.hasSpecifier("virtual") or purefunctions(underlyingElement(this)) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "VirtualFunction" }
|
||||
|
||||
/** Holds if this virtual function is pure. */
|
||||
predicate isPure() { this instanceof PureVirtualFunction }
|
||||
|
||||
/**
|
||||
* Holds if this function was declared with the `override` specifier
|
||||
* [N4140 10.3].
|
||||
*/
|
||||
predicate isOverrideExplicit() { this.hasSpecifier("override") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ pure virtual function [N4140 10.4]. For example the first function
|
||||
* called `myVirtualFunction` in the following code:
|
||||
* ```
|
||||
* class A {
|
||||
* public:
|
||||
* virtual void myVirtualFunction() = 0;
|
||||
* };
|
||||
*
|
||||
* class B: public A {
|
||||
* public:
|
||||
* virtual void myVirtualFunction() {
|
||||
* doSomething();
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class PureVirtualFunction extends VirtualFunction {
|
||||
PureVirtualFunction() { purefunctions(underlyingElement(this)) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "PureVirtualFunction" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A const C++ member function [N4140 9.3.1/4]. A const function has the
|
||||
* `const` specifier and does not modify the state of its class. For example
|
||||
* the member function `day` in the following code:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* ...
|
||||
*
|
||||
* int day() const {
|
||||
* return d;
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class ConstMemberFunction extends MemberFunction {
|
||||
ConstMemberFunction() { this.hasSpecifier("const") }
|
||||
|
||||
override string getCanonicalQLClass() { result = "ConstMemberFunction" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ constructor [N4140 12.1]. For example the function `MyClass` in the
|
||||
* following code is a constructor:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass() {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class Constructor extends MemberFunction {
|
||||
Constructor() { functions(underlyingElement(this), _, 2) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "Constructor" }
|
||||
|
||||
/**
|
||||
* Holds if this constructor serves as a default constructor.
|
||||
*
|
||||
* This holds for constructors with zero formal parameters. It also holds
|
||||
* for constructors which have a non-zero number of formal parameters,
|
||||
* provided that every parameter has a default value.
|
||||
*/
|
||||
predicate isDefault() { forall(Parameter p | p = this.getAParameter() | p.hasInitializer()) }
|
||||
|
||||
/**
|
||||
* Gets an entry in the constructor's initializer list, or a
|
||||
* compiler-generated action which initializes a base class or member
|
||||
* variable.
|
||||
*/
|
||||
ConstructorInit getAnInitializer() { result = getInitializer(_) }
|
||||
|
||||
/**
|
||||
* Gets an entry in the constructor's initializer list, or a
|
||||
* compiler-generated action which initializes a base class or member
|
||||
* variable. The index specifies the order in which the initializer is
|
||||
* to be evaluated.
|
||||
*/
|
||||
ConstructorInit getInitializer(int i) {
|
||||
exprparents(unresolveElement(result), i, underlyingElement(this))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that defines an implicit conversion.
|
||||
*/
|
||||
abstract class ImplicitConversionFunction extends MemberFunction {
|
||||
/** Gets the type this `ImplicitConversionFunction` takes as input. */
|
||||
abstract Type getSourceType();
|
||||
|
||||
/** Gets the type this `ImplicitConversionFunction` converts to. */
|
||||
abstract Type getDestType();
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ constructor that also defines an implicit conversion. For example the
|
||||
* function `MyClass` in the following code is a `ConversionConstructor`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass(const MyOtherClass &from) {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class ConversionConstructor extends Constructor, ImplicitConversionFunction {
|
||||
ConversionConstructor() {
|
||||
strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
|
||||
not hasSpecifier("explicit") and
|
||||
not this instanceof CopyConstructor
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() {
|
||||
not this instanceof MoveConstructor and result = "ConversionConstructor"
|
||||
}
|
||||
|
||||
/** Gets the type this `ConversionConstructor` takes as input. */
|
||||
override Type getSourceType() { result = this.getParameter(0).getType() }
|
||||
|
||||
/** Gets the type this `ConversionConstructor` is a constructor of. */
|
||||
override Type getDestType() { result = this.getDeclaringType() }
|
||||
}
|
||||
|
||||
private predicate hasCopySignature(MemberFunction f) {
|
||||
f.getParameter(0).getUnspecifiedType().(LValueReferenceType).getBaseType() = f.getDeclaringType()
|
||||
}
|
||||
|
||||
private predicate hasMoveSignature(MemberFunction f) {
|
||||
f.getParameter(0).getUnspecifiedType().(RValueReferenceType).getBaseType() = f.getDeclaringType()
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ copy constructor [N4140 12.8]. For example the function `MyClass` in
|
||||
* the following code is a `CopyConstructor`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass(const MyClass &from) {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* As per the standard, a copy constructor of class `T` is a non-template
|
||||
* constructor whose first parameter has type `T&`, `const T&`, `volatile
|
||||
* T&`, or `const volatile T&`, and either there are no other parameters,
|
||||
* or the rest of the parameters all have default values.
|
||||
*
|
||||
* For template classes, it can generally not be determined until instantiation
|
||||
* whether a constructor is a copy constructor. For such classes, `CopyConstructor`
|
||||
* over-approximates the set of copy constructors; if an under-approximation is
|
||||
* desired instead, see the member predicate
|
||||
* `mayNotBeCopyConstructorInInstantiation`.
|
||||
*/
|
||||
class CopyConstructor extends Constructor {
|
||||
CopyConstructor() {
|
||||
hasCopySignature(this) and
|
||||
(
|
||||
// The rest of the parameters all have default values
|
||||
forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer())
|
||||
or
|
||||
// or this is a template class, in which case the default values have
|
||||
// not been extracted even if they exist. In that case, we assume that
|
||||
// there are default values present since that is the most common case
|
||||
// in real-world code.
|
||||
getDeclaringType() instanceof TemplateClass
|
||||
) and
|
||||
not exists(getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "CopyConstructor" }
|
||||
|
||||
/**
|
||||
* Holds if we cannot determine that this constructor will become a copy
|
||||
* constructor in all instantiations. Depending on template parameters of the
|
||||
* enclosing class, this may become an ordinary constructor or a copy
|
||||
* constructor.
|
||||
*/
|
||||
predicate mayNotBeCopyConstructorInInstantiation() {
|
||||
// In general, default arguments of template classes can only be
|
||||
// type-checked for each template instantiation; if an argument in an
|
||||
// instantiation fails to type-check then the corresponding parameter has
|
||||
// no default argument in the instantiation.
|
||||
getDeclaringType() instanceof TemplateClass and
|
||||
getNumberOfParameters() > 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ move constructor [N4140 12.8]. For example the function `MyClass` in
|
||||
* the following code is a `MoveConstructor`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass(MyClass &&from) {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* As per the standard, a move constructor of class `T` is a non-template
|
||||
* constructor whose first parameter is `T&&`, `const T&&`, `volatile T&&`,
|
||||
* or `const volatile T&&`, and either there are no other parameters, or
|
||||
* the rest of the parameters all have default values.
|
||||
*
|
||||
* For template classes, it can generally not be determined until instantiation
|
||||
* whether a constructor is a move constructor. For such classes, `MoveConstructor`
|
||||
* over-approximates the set of move constructors; if an under-approximation is
|
||||
* desired instead, see the member predicate
|
||||
* `mayNotBeMoveConstructorInInstantiation`.
|
||||
*/
|
||||
class MoveConstructor extends Constructor {
|
||||
MoveConstructor() {
|
||||
hasMoveSignature(this) and
|
||||
(
|
||||
// The rest of the parameters all have default values
|
||||
forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer())
|
||||
or
|
||||
// or this is a template class, in which case the default values have
|
||||
// not been extracted even if they exist. In that case, we assume that
|
||||
// there are default values present since that is the most common case
|
||||
// in real-world code.
|
||||
getDeclaringType() instanceof TemplateClass
|
||||
) and
|
||||
not exists(getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "MoveConstructor" }
|
||||
|
||||
/**
|
||||
* Holds if we cannot determine that this constructor will become a move
|
||||
* constructor in all instantiations. Depending on template parameters of the
|
||||
* enclosing class, this may become an ordinary constructor or a move
|
||||
* constructor.
|
||||
*/
|
||||
predicate mayNotBeMoveConstructorInInstantiation() {
|
||||
// In general, default arguments of template classes can only be
|
||||
// type-checked for each template instantiation; if an argument in an
|
||||
// instantiation fails to type-check then the corresponding parameter has
|
||||
// no default argument in the instantiation.
|
||||
getDeclaringType() instanceof TemplateClass and
|
||||
getNumberOfParameters() > 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ constructor that takes no arguments ('default' constructor). This
|
||||
* is the constructor that is invoked when no initializer is given. For
|
||||
* example the function `MyClass` in the following code is a
|
||||
* `NoArgConstructor`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass() {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class NoArgConstructor extends Constructor {
|
||||
NoArgConstructor() { this.getNumberOfParameters() = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ destructor [N4140 12.4]. For example the function `~MyClass` in the
|
||||
* following code is a destructor:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* ~MyClass() {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class Destructor extends MemberFunction {
|
||||
Destructor() { functions(underlyingElement(this), _, 3) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "Destructor" }
|
||||
|
||||
/**
|
||||
* Gets a compiler-generated action which destructs a base class or member
|
||||
* variable.
|
||||
*/
|
||||
DestructorDestruction getADestruction() { result = getDestruction(_) }
|
||||
|
||||
/**
|
||||
* Gets a compiler-generated action which destructs a base class or member
|
||||
* variable. The index specifies the order in which the destruction should
|
||||
* be evaluated.
|
||||
*/
|
||||
DestructorDestruction getDestruction(int i) {
|
||||
exprparents(unresolveElement(result), i, underlyingElement(this))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ conversion operator [N4140 12.3.2]. For example the function
|
||||
* `operator int` in the following code is a `ConversionOperator`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* operator int();
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class ConversionOperator extends MemberFunction, ImplicitConversionFunction {
|
||||
ConversionOperator() { functions(underlyingElement(this), _, 4) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "ConversionOperator" }
|
||||
|
||||
override Type getSourceType() { result = this.getDeclaringType() }
|
||||
|
||||
override Type getDestType() { result = this.getType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ copy assignment operator [N4140 12.8]. For example the function
|
||||
* `operator=` in the following code is a `CopyAssignmentOperator`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass &operator=(const MyClass &other);
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* As per the standard, a copy assignment operator of class `T` is a
|
||||
* non-template non-static member function with the name `operator=` that
|
||||
* takes exactly one parameter of type `T`, `T&`, `const T&`, `volatile
|
||||
* T&`, or `const volatile T&`.
|
||||
*/
|
||||
class CopyAssignmentOperator extends Operator {
|
||||
CopyAssignmentOperator() {
|
||||
hasName("operator=") and
|
||||
(
|
||||
hasCopySignature(this)
|
||||
or
|
||||
// Unlike CopyConstructor, this member allows a non-reference
|
||||
// parameter.
|
||||
getParameter(0).getUnspecifiedType() = getDeclaringType()
|
||||
) and
|
||||
not exists(this.getParameter(1)) and
|
||||
not exists(getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "CopyAssignmentOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ move assignment operator [N4140 12.8]. For example the function
|
||||
* `operator=` in the following code is a `MoveAssignmentOperator`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass &operator=(MyClass &&other);
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* As per the standard, a move assignment operator of class `T` is a
|
||||
* non-template non-static member function with the name `operator=` that
|
||||
* takes exactly one parameter of type `T&&`, `const T&&`, `volatile T&&`,
|
||||
* or `const volatile T&&`.
|
||||
*/
|
||||
class MoveAssignmentOperator extends Operator {
|
||||
MoveAssignmentOperator() {
|
||||
hasName("operator=") and
|
||||
hasMoveSignature(this) and
|
||||
not exists(this.getParameter(1)) and
|
||||
not exists(getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "MoveAssignmentOperator" }
|
||||
}
|
||||
@@ -165,6 +165,7 @@ class Parameter extends LocalScopeVariable, @parameter {
|
||||
class ParameterIndex extends int {
|
||||
ParameterIndex() {
|
||||
exists(Parameter p | this = p.getIndex()) or
|
||||
exists(Call c | exists(c.getArgument(this))) // permit indexing varargs
|
||||
exists(Call c | exists(c.getArgument(this))) or // permit indexing varargs
|
||||
this = -1 // used for `this`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import semmle.code.cpp.ir.IR
|
||||
* has the AST for the `Function` itself, which tends to confuse mapping between the AST `BasicBlock`
|
||||
* and the `IRBlock`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate isUnreachedBlock(IRBlock block) {
|
||||
block.getFirstInstruction() instanceof UnreachedInstruction
|
||||
}
|
||||
@@ -304,13 +305,13 @@ class IRGuardCondition extends Instruction {
|
||||
pred.getASuccessor() = succ and
|
||||
controls(pred, testIsTrue)
|
||||
or
|
||||
hasBranchEdge(succ, testIsTrue) and
|
||||
succ = getBranchSuccessor(testIsTrue) and
|
||||
branch.getCondition() = this and
|
||||
branch.getBlock() = pred
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `branch` jumps directly to `succ` when this condition is `testIsTrue`.
|
||||
* Gets the block to which `branch` jumps directly when this condition is `testIsTrue`.
|
||||
*
|
||||
* This predicate is intended to help with situations in which an inference can only be made
|
||||
* based on an edge between a block with multiple successors and a block with multiple
|
||||
@@ -324,14 +325,14 @@ class IRGuardCondition extends Instruction {
|
||||
* return x;
|
||||
* ```
|
||||
*/
|
||||
private predicate hasBranchEdge(IRBlock succ, boolean testIsTrue) {
|
||||
private IRBlock getBranchSuccessor(boolean testIsTrue) {
|
||||
branch.getCondition() = this and
|
||||
(
|
||||
testIsTrue = true and
|
||||
succ.getFirstInstruction() = branch.getTrueSuccessor()
|
||||
result.getFirstInstruction() = branch.getTrueSuccessor()
|
||||
or
|
||||
testIsTrue = false and
|
||||
succ.getFirstInstruction() = branch.getFalseSuccessor()
|
||||
result.getFirstInstruction() = branch.getFalseSuccessor()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -405,20 +406,78 @@ class IRGuardCondition extends Instruction {
|
||||
*/
|
||||
private predicate controlsBlock(IRBlock controlled, boolean testIsTrue) {
|
||||
not isUnreachedBlock(controlled) and
|
||||
exists(IRBlock branchBlock | branchBlock.getAnInstruction() = branch |
|
||||
exists(IRBlock succ |
|
||||
testIsTrue = true and succ.getFirstInstruction() = branch.getTrueSuccessor()
|
||||
//
|
||||
// For this block to control the block `controlled` with `testIsTrue` the
|
||||
// following must hold: Execution must have passed through the test; that
|
||||
// is, `this` must strictly dominate `controlled`. Execution must have
|
||||
// passed through the `testIsTrue` edge leaving `this`.
|
||||
//
|
||||
// Although "passed through the true edge" implies that
|
||||
// `getBranchSuccessor(true)` dominates `controlled`, the reverse is not
|
||||
// true, as flow may have passed through another edge to get to
|
||||
// `getBranchSuccessor(true)`, so we need to assert that
|
||||
// `getBranchSuccessor(true)` dominates `controlled` *and* that all
|
||||
// predecessors of `getBranchSuccessor(true)` are either `this` or
|
||||
// dominated by `getBranchSuccessor(true)`.
|
||||
//
|
||||
// For example, in the following snippet:
|
||||
//
|
||||
// if (x)
|
||||
// controlled;
|
||||
// false_successor;
|
||||
// uncontrolled;
|
||||
//
|
||||
// `false_successor` dominates `uncontrolled`, but not all of its
|
||||
// predecessors are `this` (`if (x)`) or dominated by itself. Whereas in
|
||||
// the following code:
|
||||
//
|
||||
// if (x)
|
||||
// while (controlled)
|
||||
// also_controlled;
|
||||
// false_successor;
|
||||
// uncontrolled;
|
||||
//
|
||||
// the block `while (controlled)` is controlled because all of its
|
||||
// predecessors are `this` (`if (x)`) or (in the case of `also_controlled`)
|
||||
// dominated by itself.
|
||||
//
|
||||
// The additional constraint on the predecessors of the test successor implies
|
||||
// that `this` strictly dominates `controlled` so that isn't necessary to check
|
||||
// directly.
|
||||
exists(IRBlock succ |
|
||||
succ = this.getBranchSuccessor(testIsTrue) and
|
||||
this.hasDominatingEdgeTo(succ) and
|
||||
succ.dominates(controlled)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(this, succ)` is an edge that dominates `succ`, that is, all other
|
||||
* predecessors of `succ` are dominated by `succ`. This implies that `this` is the
|
||||
* immediate dominator of `succ`.
|
||||
*
|
||||
* This is a necessary and sufficient condition for an edge to dominate anything,
|
||||
* and in particular `bb1.hasDominatingEdgeTo(bb2) and bb2.dominates(bb3)` means
|
||||
* that the edge `(bb1, bb2)` dominates `bb3`.
|
||||
*/
|
||||
private predicate hasDominatingEdgeTo(IRBlock succ) {
|
||||
exists(IRBlock branchBlock | branchBlock = this.getBranchBlock() |
|
||||
branchBlock.immediatelyDominates(succ) and
|
||||
branchBlock.getASuccessor() = succ and
|
||||
forall(IRBlock pred | pred = succ.getAPredecessor() and pred != branchBlock |
|
||||
succ.dominates(pred)
|
||||
or
|
||||
testIsTrue = false and succ.getFirstInstruction() = branch.getFalseSuccessor()
|
||||
|
|
||||
branch.getCondition() = this and
|
||||
succ.dominates(controlled) and
|
||||
forall(IRBlock pred | pred.getASuccessor() = succ |
|
||||
pred = branchBlock or succ.dominates(pred) or not pred.isReachableFromFunctionEntry()
|
||||
)
|
||||
// An unreachable `pred` is vacuously dominated by `succ` since all
|
||||
// paths from the entry to `pred` go through `succ`. Such vacuous
|
||||
// dominance is not included in the `dominates` predicate since that
|
||||
// could cause quadratic blow-up.
|
||||
not pred.isReachableFromFunctionEntry()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private IRBlock getBranchBlock() { result = branch.getBlock() }
|
||||
}
|
||||
|
||||
private ConditionalBranchInstruction get_branch_for_condition(Instruction guard) {
|
||||
|
||||
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
exists(Node mid |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
store(mid, _, node) and
|
||||
store(mid, _, node, _) and
|
||||
not outBarrier(mid, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(f, config) and
|
||||
exists(Content c |
|
||||
nodeCandFwd1Read(c, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(c, config) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
or
|
||||
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
read(mid, f, node)
|
||||
read(mid, c, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
|
||||
exists(Node mid, Node node, TypedContent tc |
|
||||
not fullBarrier(node, config) and
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, config) and
|
||||
store(mid, f, node)
|
||||
store(mid, tc, node, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, toReturn, config) and
|
||||
nodeCand1IsRead(f, config)
|
||||
exists(Content c |
|
||||
nodeCand1Store(c, node, toReturn, config) and
|
||||
nodeCand1IsRead(c, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
exists(Node mid, Content c |
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, toReturn, config)
|
||||
)
|
||||
or
|
||||
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1IsRead(Content f, Configuration config) {
|
||||
private predicate nodeCand1IsRead(Content c, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(node, unbind(config)) and
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
nodeCand1(mid, toReturn, config) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
store(node, tc, mid, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a read and a store in the flow covered
|
||||
* Holds if `c` is the target of both a read and a store in the flow covered
|
||||
* by `nodeCand1`.
|
||||
*/
|
||||
private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
|
||||
nodeCand1IsRead(f, conf) and
|
||||
nodeCand1Store(f, _, _, conf)
|
||||
private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
|
||||
nodeCand1IsRead(c, conf) and
|
||||
nodeCand1Store(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, f, n2)
|
||||
private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, tc, n2, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate read(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
private predicate read(Node n1, Content c, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
read(n1, f, n2)
|
||||
read(n1, c, n2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, _, config) and
|
||||
store(mid, f, node, config) and
|
||||
storeCand1(mid, _, node, config) and
|
||||
stored = true
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(f, stored, config)
|
||||
exists(Content c |
|
||||
nodeCandFwd2Read(c, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(c, stored, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
|
||||
private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCand1(node, unbind(config)) and
|
||||
nodeCandFwd2(mid, _, _, stored, config) and
|
||||
store(mid, f, node, config)
|
||||
storeCand1(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(
|
||||
Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, true, config) and
|
||||
read(mid, f, node, config)
|
||||
read(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -896,15 +901,15 @@ private predicate nodeCand2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(f, read, config)
|
||||
exists(Content c |
|
||||
nodeCand2Store(c, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(c, read, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f, boolean read0 |
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
|
||||
exists(Node mid, Content c, boolean read0 |
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
|
||||
nodeCand2(mid, toReturn, returnRead, read0, config) and
|
||||
read = true
|
||||
)
|
||||
@@ -930,51 +935,51 @@ private predicate nodeCand2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
|
||||
private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd2(node, _, _, true, unbind(config)) and
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
|
||||
nodeCand2(mid, _, _, read, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2Store(
|
||||
Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid, config) and
|
||||
storeCand1(node, c, mid, config) and
|
||||
nodeCand2(mid, toReturn, returnRead, true, config) and
|
||||
nodeCandFwd2(node, _, _, stored, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
|
||||
private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, _, stored, conf) and
|
||||
nodeCand2Store(c, node, _, _, stored, conf) and
|
||||
nodeCand2(node, _, _, stored, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
* Holds if `c` is the target of both a store and a read in the path graph
|
||||
* covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
|
||||
private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
|
||||
exists(boolean apNonEmpty |
|
||||
nodeCand2IsStored(f, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(f, apNonEmpty, conf)
|
||||
nodeCand2IsStored(c, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(c, apNonEmpty, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1058,7 +1063,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(_, node, config) or
|
||||
node instanceof ParameterNode or
|
||||
node instanceof OutNodeExt or
|
||||
store(_, _, node) or
|
||||
store(_, _, node, _) or
|
||||
read(_, _, node) or
|
||||
node instanceof CastNode
|
||||
)
|
||||
@@ -1074,7 +1079,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
store(node, _, next) or
|
||||
store(node, _, next, _) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
@@ -1154,19 +1159,21 @@ private module LocalFlowBigStep {
|
||||
private import LocalFlowBigStep
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2, config) and
|
||||
private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
|
||||
read(node1, c, node2, config) and
|
||||
nodeCand2(node1, _, _, true, unbind(config)) and
|
||||
nodeCand2(node2, config) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(c, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2, config) and
|
||||
private predicate storeCand2(
|
||||
Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(node1, tc, node2, contentType) and
|
||||
nodeCand2(node1, config) and
|
||||
nodeCand2(node2, _, _, true, unbind(config)) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1227,17 +1234,18 @@ private predicate flowCandFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
flowCandFwd(mid, fromArg, argApf, _, config) and
|
||||
storeCand2(mid, f, node, config) and
|
||||
exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
storeCand2(mid, tc, node, contentType, config) and
|
||||
nodeCand2(node, _, _, true, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
apf.headUsesContent(tc) and
|
||||
compatibleTypes(apf0.getType(), contentType)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(TypedContent tc |
|
||||
flowCandFwdRead(tc, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
|
||||
)
|
||||
or
|
||||
@@ -1261,24 +1269,30 @@ private predicate flowCandFwd0(
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n, DataFlowType contentType |
|
||||
flowCandFwd(mid, _, _, apf, config) and
|
||||
storeCand2(mid, f, n, config) and
|
||||
storeCand2(mid, tc, n, contentType, config) and
|
||||
nodeCand2(n, _, _, true, unbind(config)) and
|
||||
compatibleTypes(apf.getType(), f.getType())
|
||||
compatibleTypes(apf.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
private predicate flowCandFwdRead0(
|
||||
Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
|
||||
AccessPathFrontHead apf, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f)
|
||||
)
|
||||
flowCandFwd(node1, fromArg, argApf, apf, config) and
|
||||
readCand2(node1, c, node2, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1385,17 +1399,15 @@ private predicate flowCand0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPathFrontHead apf0 |
|
||||
flowCandStore(node, f, toReturn, returnApf, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandConsCand(f, apf, config)
|
||||
exists(TypedContent tc |
|
||||
flowCandStore(node, tc, apf, toReturn, returnApf, config) and
|
||||
flowCandConsCand(tc, apf, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
exists(TypedContent tc, AccessPathFront apf0 |
|
||||
flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(tc, apf0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1417,36 +1429,40 @@ private predicate flowCand0(
|
||||
else returnApf = TAccessPathFrontNone()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCandFwd(
|
||||
Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandRead(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
readCand2(node, f, mid, config) and
|
||||
readCandFwd(node, tc, apf, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
storeCand2(node, f, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
flowCandFwd(node, _, _, apf, config) and
|
||||
storeCand2(node, tc, mid, _, unbind(config)) and
|
||||
flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(Node n, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(n, _, _, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandRead(n, f, _, _, apf, config)
|
||||
)
|
||||
private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
flowCandRead(_, tc, _, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1499,13 +1515,13 @@ private predicate flowCandIsReturned(
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
|
||||
TConsCons(Content f1, Content f2, int len) {
|
||||
flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
|
||||
TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -1514,7 +1530,7 @@ private newtype TAccessPath =
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
abstract Content getHead();
|
||||
abstract TypedContent getHead();
|
||||
|
||||
abstract int len();
|
||||
|
||||
@@ -1525,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
/**
|
||||
* Holds if this access path has `head` at the front and may be followed by `tail`.
|
||||
*/
|
||||
abstract predicate pop(Content head, AccessPath tail);
|
||||
abstract predicate pop(TypedContent head, AccessPath tail);
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
@@ -1535,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override string toString() { result = concat(": " + ppReprType(t)) }
|
||||
|
||||
override Content getHead() { none() }
|
||||
override TypedContent getHead() { none() }
|
||||
|
||||
override int len() { result = 0 }
|
||||
|
||||
@@ -1543,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontNil(t) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { none() }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { none() }
|
||||
}
|
||||
|
||||
abstract private class AccessPathCons extends AccessPath { }
|
||||
|
||||
private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
private Content f;
|
||||
private TypedContent tc;
|
||||
private DataFlowType t;
|
||||
|
||||
AccessPathConsNil() { this = TConsNil(f, t) }
|
||||
AccessPathConsNil() { this = TConsNil(tc, t) }
|
||||
|
||||
override string toString() {
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
}
|
||||
|
||||
override Content getHead() { result = f }
|
||||
override TypedContent getHead() { result = tc }
|
||||
|
||||
override int len() { result = 1 }
|
||||
|
||||
override DataFlowType getType() { result = f.getContainerType() }
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
|
||||
}
|
||||
|
||||
private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
private Content f1;
|
||||
private Content f2;
|
||||
private TypedContent tc1;
|
||||
private TypedContent tc2;
|
||||
private int len;
|
||||
|
||||
AccessPathConsCons() { this = TConsCons(f1, f2, len) }
|
||||
AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
|
||||
|
||||
override string toString() {
|
||||
if len = 2
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
|
||||
else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
|
||||
}
|
||||
|
||||
override Content getHead() { result = f1 }
|
||||
override TypedContent getHead() { result = tc1 }
|
||||
|
||||
override int len() { result = len }
|
||||
|
||||
override DataFlowType getType() { result = f1.getContainerType() }
|
||||
override DataFlowType getType() { result = tc1.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f1) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc1) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) {
|
||||
head = f1 and
|
||||
override predicate pop(TypedContent head, AccessPath tail) {
|
||||
head = tc1 and
|
||||
(
|
||||
tail = TConsCons(f2, _, len - 1)
|
||||
tail = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
tail = TConsNil(f2, _)
|
||||
tail = TConsNil(tc2, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the access path obtained by popping `f` from `ap`, if any. */
|
||||
private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
|
||||
/** Gets the access path obtained by popping `tc` from `ap`, if any. */
|
||||
private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
|
||||
|
||||
/** Gets the access path obtained by pushing `f` onto `ap`. */
|
||||
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
|
||||
/** Gets the access path obtained by pushing `tc` onto `ap`. */
|
||||
private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
|
||||
|
||||
private newtype TAccessPathOption =
|
||||
TAccessPathNone() or
|
||||
@@ -1678,15 +1694,12 @@ private predicate flowFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPath ap0 |
|
||||
flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
|
||||
ap = push(f, ap0)
|
||||
)
|
||||
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
|
||||
flowFwdConsCand(f, apf, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
|
||||
flowFwdConsCand(tc, apf, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1710,54 +1723,63 @@ private predicate flowFwd0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore(
|
||||
Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFront apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
flowFwdStore1(mid, f, node, apf0, apf, config)
|
||||
flowFwdStore0(mid, tc, node, apf0, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
|
||||
private predicate storeCand(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
|
||||
Configuration config
|
||||
) {
|
||||
storeCand2(mid, f, node, config) and
|
||||
flowCand(mid, _, _, apf0, config)
|
||||
storeCand2(mid, tc, node, _, config) and
|
||||
flowCand(mid, _, _, apf0, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowFwdStore1(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
Configuration config
|
||||
) {
|
||||
flowFwdStore0(mid, f, node, apf0, config) and
|
||||
flowCandConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f) and
|
||||
storeCand(mid, tc, node, apf0, apf, config) and
|
||||
flowCandConsCand(tc, apf0, config) and
|
||||
flowCand(node, _, _, apf, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
|
||||
Configuration config
|
||||
private predicate flowFwdRead0(
|
||||
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
|
||||
boolean fromArg, AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCand(node, _, _, _, unbind(config))
|
||||
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
|
||||
readCandFwd(node1, tc, apf0, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
|
||||
flowCand(node, _, _, apf, unbind(config)) and
|
||||
flowCandConsCand(tc, apf, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdConsCand(
|
||||
Content f, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node n |
|
||||
flowFwd(n, _, _, apf, ap, config) and
|
||||
flowFwdStore1(n, f, _, apf, _, config)
|
||||
flowFwdStore0(n, tc, _, apf, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1863,9 +1885,9 @@ private predicate flow0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
flowStore(f, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(f, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowStore(tc, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(tc, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
@@ -1895,39 +1917,41 @@ private predicate flow0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
storeCand2(node1, f, node2, config) and
|
||||
flowFwdStore(node2, f, ap, _, _, _, config) and
|
||||
ap0 = push(f, ap)
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flowFwdStore(node2, tc, ap, _, _, _, config) and
|
||||
ap0 = push(tc, ap)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowStore(
|
||||
Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPath ap0 |
|
||||
storeFlowFwd(node, f, mid, ap, ap0, config) and
|
||||
storeFlowFwd(node, tc, mid, ap, ap0, config) and
|
||||
flow(mid, toReturn, returnAp, ap0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
readCand2(node1, f, node2, config) and
|
||||
flowFwdRead(node2, f, ap, _, _, config) and
|
||||
ap0 = pop(f, ap) and
|
||||
flowFwdConsCand(f, _, ap0, unbind(config))
|
||||
exists(AccessPathFrontHead apf |
|
||||
readCandFwd(node1, tc, apf, node2, config) and
|
||||
flowFwdRead(node2, apf, ap, _, _, _, config) and
|
||||
ap0 = pop(tc, ap) and
|
||||
flowFwdConsCand(tc, _, ap0, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
|
||||
private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
|
||||
exists(Node n, Node mid |
|
||||
flow(mid, _, _, ap, config) and
|
||||
readFlowFwd(n, f, mid, _, ap, config)
|
||||
readFlowFwd(n, tc, mid, _, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2270,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = TNil(getErasedNodeTypeBound(node))
|
||||
or
|
||||
exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
@@ -2284,30 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2) and
|
||||
private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
readCandFwd(node1, tc, _, node2, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
|
||||
private predicate pathReadStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
readCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2) and
|
||||
private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathStoreStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
@@ -2538,10 +2564,10 @@ private module FlowExploration {
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(Content f, int len) { len in [1 .. 5] }
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -2550,7 +2576,7 @@ private module FlowExploration {
|
||||
private class PartialAccessPath extends TPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TPartialCons(result, _) }
|
||||
TypedContent getHead() { this = TPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TPartialNil(_) and result = 0
|
||||
@@ -2561,7 +2587,7 @@ private module FlowExploration {
|
||||
DataFlowType getType() {
|
||||
this = TPartialNil(result)
|
||||
or
|
||||
exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
@@ -2579,15 +2605,15 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
|
||||
if len = 1
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc.toString() + "]"
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2763,11 +2789,12 @@ private module FlowExploration {
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(PartialAccessPath ap0, Content f |
|
||||
partialPathReadStep(mid, ap0, f, node, cc, config) and
|
||||
exists(PartialAccessPath ap0, TypedContent tc |
|
||||
partialPathReadStep(mid, ap0, tc, node, cc, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
apConsFwd(ap, tc, ap0, config) and
|
||||
compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
|
||||
@@ -2786,35 +2813,42 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
ap1 = mid.getAp() and
|
||||
store(mid.getNode(), f, node) and
|
||||
ap2.getHead() = f and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), f.getType())
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
store(midNode, tc, node, contentType) and
|
||||
ap2.getHead() = tc and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, f, _, ap2) and
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
ap = mid.getAp() and
|
||||
readStep(mid.getNode(), f, node) and
|
||||
ap.getHead() = f and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
read(midNode, tc.getContent(), node) and
|
||||
ap.getHead() = tc and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
|
||||
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
exists(Node mid |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
store(mid, _, node) and
|
||||
store(mid, _, node, _) and
|
||||
not outBarrier(mid, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(f, config) and
|
||||
exists(Content c |
|
||||
nodeCandFwd1Read(c, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(c, config) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
or
|
||||
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
read(mid, f, node)
|
||||
read(mid, c, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
|
||||
exists(Node mid, Node node, TypedContent tc |
|
||||
not fullBarrier(node, config) and
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, config) and
|
||||
store(mid, f, node)
|
||||
store(mid, tc, node, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, toReturn, config) and
|
||||
nodeCand1IsRead(f, config)
|
||||
exists(Content c |
|
||||
nodeCand1Store(c, node, toReturn, config) and
|
||||
nodeCand1IsRead(c, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
exists(Node mid, Content c |
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, toReturn, config)
|
||||
)
|
||||
or
|
||||
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1IsRead(Content f, Configuration config) {
|
||||
private predicate nodeCand1IsRead(Content c, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(node, unbind(config)) and
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
nodeCand1(mid, toReturn, config) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
store(node, tc, mid, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a read and a store in the flow covered
|
||||
* Holds if `c` is the target of both a read and a store in the flow covered
|
||||
* by `nodeCand1`.
|
||||
*/
|
||||
private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
|
||||
nodeCand1IsRead(f, conf) and
|
||||
nodeCand1Store(f, _, _, conf)
|
||||
private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
|
||||
nodeCand1IsRead(c, conf) and
|
||||
nodeCand1Store(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, f, n2)
|
||||
private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, tc, n2, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate read(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
private predicate read(Node n1, Content c, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
read(n1, f, n2)
|
||||
read(n1, c, n2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, _, config) and
|
||||
store(mid, f, node, config) and
|
||||
storeCand1(mid, _, node, config) and
|
||||
stored = true
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(f, stored, config)
|
||||
exists(Content c |
|
||||
nodeCandFwd2Read(c, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(c, stored, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
|
||||
private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCand1(node, unbind(config)) and
|
||||
nodeCandFwd2(mid, _, _, stored, config) and
|
||||
store(mid, f, node, config)
|
||||
storeCand1(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(
|
||||
Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, true, config) and
|
||||
read(mid, f, node, config)
|
||||
read(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -896,15 +901,15 @@ private predicate nodeCand2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(f, read, config)
|
||||
exists(Content c |
|
||||
nodeCand2Store(c, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(c, read, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f, boolean read0 |
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
|
||||
exists(Node mid, Content c, boolean read0 |
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
|
||||
nodeCand2(mid, toReturn, returnRead, read0, config) and
|
||||
read = true
|
||||
)
|
||||
@@ -930,51 +935,51 @@ private predicate nodeCand2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
|
||||
private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd2(node, _, _, true, unbind(config)) and
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
|
||||
nodeCand2(mid, _, _, read, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2Store(
|
||||
Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid, config) and
|
||||
storeCand1(node, c, mid, config) and
|
||||
nodeCand2(mid, toReturn, returnRead, true, config) and
|
||||
nodeCandFwd2(node, _, _, stored, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
|
||||
private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, _, stored, conf) and
|
||||
nodeCand2Store(c, node, _, _, stored, conf) and
|
||||
nodeCand2(node, _, _, stored, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
* Holds if `c` is the target of both a store and a read in the path graph
|
||||
* covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
|
||||
private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
|
||||
exists(boolean apNonEmpty |
|
||||
nodeCand2IsStored(f, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(f, apNonEmpty, conf)
|
||||
nodeCand2IsStored(c, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(c, apNonEmpty, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1058,7 +1063,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(_, node, config) or
|
||||
node instanceof ParameterNode or
|
||||
node instanceof OutNodeExt or
|
||||
store(_, _, node) or
|
||||
store(_, _, node, _) or
|
||||
read(_, _, node) or
|
||||
node instanceof CastNode
|
||||
)
|
||||
@@ -1074,7 +1079,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
store(node, _, next) or
|
||||
store(node, _, next, _) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
@@ -1154,19 +1159,21 @@ private module LocalFlowBigStep {
|
||||
private import LocalFlowBigStep
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2, config) and
|
||||
private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
|
||||
read(node1, c, node2, config) and
|
||||
nodeCand2(node1, _, _, true, unbind(config)) and
|
||||
nodeCand2(node2, config) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(c, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2, config) and
|
||||
private predicate storeCand2(
|
||||
Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(node1, tc, node2, contentType) and
|
||||
nodeCand2(node1, config) and
|
||||
nodeCand2(node2, _, _, true, unbind(config)) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1227,17 +1234,18 @@ private predicate flowCandFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
flowCandFwd(mid, fromArg, argApf, _, config) and
|
||||
storeCand2(mid, f, node, config) and
|
||||
exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
storeCand2(mid, tc, node, contentType, config) and
|
||||
nodeCand2(node, _, _, true, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
apf.headUsesContent(tc) and
|
||||
compatibleTypes(apf0.getType(), contentType)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(TypedContent tc |
|
||||
flowCandFwdRead(tc, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
|
||||
)
|
||||
or
|
||||
@@ -1261,24 +1269,30 @@ private predicate flowCandFwd0(
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n, DataFlowType contentType |
|
||||
flowCandFwd(mid, _, _, apf, config) and
|
||||
storeCand2(mid, f, n, config) and
|
||||
storeCand2(mid, tc, n, contentType, config) and
|
||||
nodeCand2(n, _, _, true, unbind(config)) and
|
||||
compatibleTypes(apf.getType(), f.getType())
|
||||
compatibleTypes(apf.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
private predicate flowCandFwdRead0(
|
||||
Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
|
||||
AccessPathFrontHead apf, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f)
|
||||
)
|
||||
flowCandFwd(node1, fromArg, argApf, apf, config) and
|
||||
readCand2(node1, c, node2, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1385,17 +1399,15 @@ private predicate flowCand0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPathFrontHead apf0 |
|
||||
flowCandStore(node, f, toReturn, returnApf, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandConsCand(f, apf, config)
|
||||
exists(TypedContent tc |
|
||||
flowCandStore(node, tc, apf, toReturn, returnApf, config) and
|
||||
flowCandConsCand(tc, apf, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
exists(TypedContent tc, AccessPathFront apf0 |
|
||||
flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(tc, apf0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1417,36 +1429,40 @@ private predicate flowCand0(
|
||||
else returnApf = TAccessPathFrontNone()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCandFwd(
|
||||
Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandRead(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
readCand2(node, f, mid, config) and
|
||||
readCandFwd(node, tc, apf, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
storeCand2(node, f, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
flowCandFwd(node, _, _, apf, config) and
|
||||
storeCand2(node, tc, mid, _, unbind(config)) and
|
||||
flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(Node n, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(n, _, _, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandRead(n, f, _, _, apf, config)
|
||||
)
|
||||
private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
flowCandRead(_, tc, _, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1499,13 +1515,13 @@ private predicate flowCandIsReturned(
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
|
||||
TConsCons(Content f1, Content f2, int len) {
|
||||
flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
|
||||
TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -1514,7 +1530,7 @@ private newtype TAccessPath =
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
abstract Content getHead();
|
||||
abstract TypedContent getHead();
|
||||
|
||||
abstract int len();
|
||||
|
||||
@@ -1525,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
/**
|
||||
* Holds if this access path has `head` at the front and may be followed by `tail`.
|
||||
*/
|
||||
abstract predicate pop(Content head, AccessPath tail);
|
||||
abstract predicate pop(TypedContent head, AccessPath tail);
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
@@ -1535,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override string toString() { result = concat(": " + ppReprType(t)) }
|
||||
|
||||
override Content getHead() { none() }
|
||||
override TypedContent getHead() { none() }
|
||||
|
||||
override int len() { result = 0 }
|
||||
|
||||
@@ -1543,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontNil(t) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { none() }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { none() }
|
||||
}
|
||||
|
||||
abstract private class AccessPathCons extends AccessPath { }
|
||||
|
||||
private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
private Content f;
|
||||
private TypedContent tc;
|
||||
private DataFlowType t;
|
||||
|
||||
AccessPathConsNil() { this = TConsNil(f, t) }
|
||||
AccessPathConsNil() { this = TConsNil(tc, t) }
|
||||
|
||||
override string toString() {
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
}
|
||||
|
||||
override Content getHead() { result = f }
|
||||
override TypedContent getHead() { result = tc }
|
||||
|
||||
override int len() { result = 1 }
|
||||
|
||||
override DataFlowType getType() { result = f.getContainerType() }
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
|
||||
}
|
||||
|
||||
private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
private Content f1;
|
||||
private Content f2;
|
||||
private TypedContent tc1;
|
||||
private TypedContent tc2;
|
||||
private int len;
|
||||
|
||||
AccessPathConsCons() { this = TConsCons(f1, f2, len) }
|
||||
AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
|
||||
|
||||
override string toString() {
|
||||
if len = 2
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
|
||||
else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
|
||||
}
|
||||
|
||||
override Content getHead() { result = f1 }
|
||||
override TypedContent getHead() { result = tc1 }
|
||||
|
||||
override int len() { result = len }
|
||||
|
||||
override DataFlowType getType() { result = f1.getContainerType() }
|
||||
override DataFlowType getType() { result = tc1.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f1) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc1) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) {
|
||||
head = f1 and
|
||||
override predicate pop(TypedContent head, AccessPath tail) {
|
||||
head = tc1 and
|
||||
(
|
||||
tail = TConsCons(f2, _, len - 1)
|
||||
tail = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
tail = TConsNil(f2, _)
|
||||
tail = TConsNil(tc2, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the access path obtained by popping `f` from `ap`, if any. */
|
||||
private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
|
||||
/** Gets the access path obtained by popping `tc` from `ap`, if any. */
|
||||
private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
|
||||
|
||||
/** Gets the access path obtained by pushing `f` onto `ap`. */
|
||||
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
|
||||
/** Gets the access path obtained by pushing `tc` onto `ap`. */
|
||||
private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
|
||||
|
||||
private newtype TAccessPathOption =
|
||||
TAccessPathNone() or
|
||||
@@ -1678,15 +1694,12 @@ private predicate flowFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPath ap0 |
|
||||
flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
|
||||
ap = push(f, ap0)
|
||||
)
|
||||
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
|
||||
flowFwdConsCand(f, apf, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
|
||||
flowFwdConsCand(tc, apf, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1710,54 +1723,63 @@ private predicate flowFwd0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore(
|
||||
Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFront apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
flowFwdStore1(mid, f, node, apf0, apf, config)
|
||||
flowFwdStore0(mid, tc, node, apf0, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
|
||||
private predicate storeCand(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
|
||||
Configuration config
|
||||
) {
|
||||
storeCand2(mid, f, node, config) and
|
||||
flowCand(mid, _, _, apf0, config)
|
||||
storeCand2(mid, tc, node, _, config) and
|
||||
flowCand(mid, _, _, apf0, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowFwdStore1(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
Configuration config
|
||||
) {
|
||||
flowFwdStore0(mid, f, node, apf0, config) and
|
||||
flowCandConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f) and
|
||||
storeCand(mid, tc, node, apf0, apf, config) and
|
||||
flowCandConsCand(tc, apf0, config) and
|
||||
flowCand(node, _, _, apf, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
|
||||
Configuration config
|
||||
private predicate flowFwdRead0(
|
||||
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
|
||||
boolean fromArg, AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCand(node, _, _, _, unbind(config))
|
||||
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
|
||||
readCandFwd(node1, tc, apf0, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
|
||||
flowCand(node, _, _, apf, unbind(config)) and
|
||||
flowCandConsCand(tc, apf, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdConsCand(
|
||||
Content f, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node n |
|
||||
flowFwd(n, _, _, apf, ap, config) and
|
||||
flowFwdStore1(n, f, _, apf, _, config)
|
||||
flowFwdStore0(n, tc, _, apf, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1863,9 +1885,9 @@ private predicate flow0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
flowStore(f, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(f, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowStore(tc, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(tc, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
@@ -1895,39 +1917,41 @@ private predicate flow0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
storeCand2(node1, f, node2, config) and
|
||||
flowFwdStore(node2, f, ap, _, _, _, config) and
|
||||
ap0 = push(f, ap)
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flowFwdStore(node2, tc, ap, _, _, _, config) and
|
||||
ap0 = push(tc, ap)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowStore(
|
||||
Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPath ap0 |
|
||||
storeFlowFwd(node, f, mid, ap, ap0, config) and
|
||||
storeFlowFwd(node, tc, mid, ap, ap0, config) and
|
||||
flow(mid, toReturn, returnAp, ap0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
readCand2(node1, f, node2, config) and
|
||||
flowFwdRead(node2, f, ap, _, _, config) and
|
||||
ap0 = pop(f, ap) and
|
||||
flowFwdConsCand(f, _, ap0, unbind(config))
|
||||
exists(AccessPathFrontHead apf |
|
||||
readCandFwd(node1, tc, apf, node2, config) and
|
||||
flowFwdRead(node2, apf, ap, _, _, _, config) and
|
||||
ap0 = pop(tc, ap) and
|
||||
flowFwdConsCand(tc, _, ap0, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
|
||||
private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
|
||||
exists(Node n, Node mid |
|
||||
flow(mid, _, _, ap, config) and
|
||||
readFlowFwd(n, f, mid, _, ap, config)
|
||||
readFlowFwd(n, tc, mid, _, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2270,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = TNil(getErasedNodeTypeBound(node))
|
||||
or
|
||||
exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
@@ -2284,30 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2) and
|
||||
private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
readCandFwd(node1, tc, _, node2, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
|
||||
private predicate pathReadStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
readCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2) and
|
||||
private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathStoreStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
@@ -2538,10 +2564,10 @@ private module FlowExploration {
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(Content f, int len) { len in [1 .. 5] }
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -2550,7 +2576,7 @@ private module FlowExploration {
|
||||
private class PartialAccessPath extends TPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TPartialCons(result, _) }
|
||||
TypedContent getHead() { this = TPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TPartialNil(_) and result = 0
|
||||
@@ -2561,7 +2587,7 @@ private module FlowExploration {
|
||||
DataFlowType getType() {
|
||||
this = TPartialNil(result)
|
||||
or
|
||||
exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
@@ -2579,15 +2605,15 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
|
||||
if len = 1
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc.toString() + "]"
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2763,11 +2789,12 @@ private module FlowExploration {
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(PartialAccessPath ap0, Content f |
|
||||
partialPathReadStep(mid, ap0, f, node, cc, config) and
|
||||
exists(PartialAccessPath ap0, TypedContent tc |
|
||||
partialPathReadStep(mid, ap0, tc, node, cc, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
apConsFwd(ap, tc, ap0, config) and
|
||||
compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
|
||||
@@ -2786,35 +2813,42 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
ap1 = mid.getAp() and
|
||||
store(mid.getNode(), f, node) and
|
||||
ap2.getHead() = f and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), f.getType())
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
store(midNode, tc, node, contentType) and
|
||||
ap2.getHead() = tc and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, f, _, ap2) and
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
ap = mid.getAp() and
|
||||
readStep(mid.getNode(), f, node) and
|
||||
ap.getHead() = f and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
read(midNode, tc.getContent(), node) and
|
||||
ap.getHead() = tc and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
|
||||
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
exists(Node mid |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
store(mid, _, node) and
|
||||
store(mid, _, node, _) and
|
||||
not outBarrier(mid, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(f, config) and
|
||||
exists(Content c |
|
||||
nodeCandFwd1Read(c, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(c, config) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
or
|
||||
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
read(mid, f, node)
|
||||
read(mid, c, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
|
||||
exists(Node mid, Node node, TypedContent tc |
|
||||
not fullBarrier(node, config) and
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, config) and
|
||||
store(mid, f, node)
|
||||
store(mid, tc, node, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, toReturn, config) and
|
||||
nodeCand1IsRead(f, config)
|
||||
exists(Content c |
|
||||
nodeCand1Store(c, node, toReturn, config) and
|
||||
nodeCand1IsRead(c, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
exists(Node mid, Content c |
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, toReturn, config)
|
||||
)
|
||||
or
|
||||
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1IsRead(Content f, Configuration config) {
|
||||
private predicate nodeCand1IsRead(Content c, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(node, unbind(config)) and
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
nodeCand1(mid, toReturn, config) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
store(node, tc, mid, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a read and a store in the flow covered
|
||||
* Holds if `c` is the target of both a read and a store in the flow covered
|
||||
* by `nodeCand1`.
|
||||
*/
|
||||
private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
|
||||
nodeCand1IsRead(f, conf) and
|
||||
nodeCand1Store(f, _, _, conf)
|
||||
private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
|
||||
nodeCand1IsRead(c, conf) and
|
||||
nodeCand1Store(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, f, n2)
|
||||
private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, tc, n2, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate read(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
private predicate read(Node n1, Content c, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
read(n1, f, n2)
|
||||
read(n1, c, n2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, _, config) and
|
||||
store(mid, f, node, config) and
|
||||
storeCand1(mid, _, node, config) and
|
||||
stored = true
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(f, stored, config)
|
||||
exists(Content c |
|
||||
nodeCandFwd2Read(c, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(c, stored, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
|
||||
private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCand1(node, unbind(config)) and
|
||||
nodeCandFwd2(mid, _, _, stored, config) and
|
||||
store(mid, f, node, config)
|
||||
storeCand1(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(
|
||||
Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, true, config) and
|
||||
read(mid, f, node, config)
|
||||
read(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -896,15 +901,15 @@ private predicate nodeCand2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(f, read, config)
|
||||
exists(Content c |
|
||||
nodeCand2Store(c, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(c, read, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f, boolean read0 |
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
|
||||
exists(Node mid, Content c, boolean read0 |
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
|
||||
nodeCand2(mid, toReturn, returnRead, read0, config) and
|
||||
read = true
|
||||
)
|
||||
@@ -930,51 +935,51 @@ private predicate nodeCand2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
|
||||
private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd2(node, _, _, true, unbind(config)) and
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
|
||||
nodeCand2(mid, _, _, read, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2Store(
|
||||
Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid, config) and
|
||||
storeCand1(node, c, mid, config) and
|
||||
nodeCand2(mid, toReturn, returnRead, true, config) and
|
||||
nodeCandFwd2(node, _, _, stored, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
|
||||
private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, _, stored, conf) and
|
||||
nodeCand2Store(c, node, _, _, stored, conf) and
|
||||
nodeCand2(node, _, _, stored, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
* Holds if `c` is the target of both a store and a read in the path graph
|
||||
* covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
|
||||
private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
|
||||
exists(boolean apNonEmpty |
|
||||
nodeCand2IsStored(f, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(f, apNonEmpty, conf)
|
||||
nodeCand2IsStored(c, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(c, apNonEmpty, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1058,7 +1063,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(_, node, config) or
|
||||
node instanceof ParameterNode or
|
||||
node instanceof OutNodeExt or
|
||||
store(_, _, node) or
|
||||
store(_, _, node, _) or
|
||||
read(_, _, node) or
|
||||
node instanceof CastNode
|
||||
)
|
||||
@@ -1074,7 +1079,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
store(node, _, next) or
|
||||
store(node, _, next, _) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
@@ -1154,19 +1159,21 @@ private module LocalFlowBigStep {
|
||||
private import LocalFlowBigStep
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2, config) and
|
||||
private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
|
||||
read(node1, c, node2, config) and
|
||||
nodeCand2(node1, _, _, true, unbind(config)) and
|
||||
nodeCand2(node2, config) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(c, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2, config) and
|
||||
private predicate storeCand2(
|
||||
Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(node1, tc, node2, contentType) and
|
||||
nodeCand2(node1, config) and
|
||||
nodeCand2(node2, _, _, true, unbind(config)) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1227,17 +1234,18 @@ private predicate flowCandFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
flowCandFwd(mid, fromArg, argApf, _, config) and
|
||||
storeCand2(mid, f, node, config) and
|
||||
exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
storeCand2(mid, tc, node, contentType, config) and
|
||||
nodeCand2(node, _, _, true, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
apf.headUsesContent(tc) and
|
||||
compatibleTypes(apf0.getType(), contentType)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(TypedContent tc |
|
||||
flowCandFwdRead(tc, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
|
||||
)
|
||||
or
|
||||
@@ -1261,24 +1269,30 @@ private predicate flowCandFwd0(
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n, DataFlowType contentType |
|
||||
flowCandFwd(mid, _, _, apf, config) and
|
||||
storeCand2(mid, f, n, config) and
|
||||
storeCand2(mid, tc, n, contentType, config) and
|
||||
nodeCand2(n, _, _, true, unbind(config)) and
|
||||
compatibleTypes(apf.getType(), f.getType())
|
||||
compatibleTypes(apf.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
private predicate flowCandFwdRead0(
|
||||
Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
|
||||
AccessPathFrontHead apf, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f)
|
||||
)
|
||||
flowCandFwd(node1, fromArg, argApf, apf, config) and
|
||||
readCand2(node1, c, node2, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1385,17 +1399,15 @@ private predicate flowCand0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPathFrontHead apf0 |
|
||||
flowCandStore(node, f, toReturn, returnApf, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandConsCand(f, apf, config)
|
||||
exists(TypedContent tc |
|
||||
flowCandStore(node, tc, apf, toReturn, returnApf, config) and
|
||||
flowCandConsCand(tc, apf, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
exists(TypedContent tc, AccessPathFront apf0 |
|
||||
flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(tc, apf0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1417,36 +1429,40 @@ private predicate flowCand0(
|
||||
else returnApf = TAccessPathFrontNone()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCandFwd(
|
||||
Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandRead(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
readCand2(node, f, mid, config) and
|
||||
readCandFwd(node, tc, apf, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
storeCand2(node, f, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
flowCandFwd(node, _, _, apf, config) and
|
||||
storeCand2(node, tc, mid, _, unbind(config)) and
|
||||
flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(Node n, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(n, _, _, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandRead(n, f, _, _, apf, config)
|
||||
)
|
||||
private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
flowCandRead(_, tc, _, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1499,13 +1515,13 @@ private predicate flowCandIsReturned(
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
|
||||
TConsCons(Content f1, Content f2, int len) {
|
||||
flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
|
||||
TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -1514,7 +1530,7 @@ private newtype TAccessPath =
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
abstract Content getHead();
|
||||
abstract TypedContent getHead();
|
||||
|
||||
abstract int len();
|
||||
|
||||
@@ -1525,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
/**
|
||||
* Holds if this access path has `head` at the front and may be followed by `tail`.
|
||||
*/
|
||||
abstract predicate pop(Content head, AccessPath tail);
|
||||
abstract predicate pop(TypedContent head, AccessPath tail);
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
@@ -1535,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override string toString() { result = concat(": " + ppReprType(t)) }
|
||||
|
||||
override Content getHead() { none() }
|
||||
override TypedContent getHead() { none() }
|
||||
|
||||
override int len() { result = 0 }
|
||||
|
||||
@@ -1543,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontNil(t) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { none() }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { none() }
|
||||
}
|
||||
|
||||
abstract private class AccessPathCons extends AccessPath { }
|
||||
|
||||
private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
private Content f;
|
||||
private TypedContent tc;
|
||||
private DataFlowType t;
|
||||
|
||||
AccessPathConsNil() { this = TConsNil(f, t) }
|
||||
AccessPathConsNil() { this = TConsNil(tc, t) }
|
||||
|
||||
override string toString() {
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
}
|
||||
|
||||
override Content getHead() { result = f }
|
||||
override TypedContent getHead() { result = tc }
|
||||
|
||||
override int len() { result = 1 }
|
||||
|
||||
override DataFlowType getType() { result = f.getContainerType() }
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
|
||||
}
|
||||
|
||||
private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
private Content f1;
|
||||
private Content f2;
|
||||
private TypedContent tc1;
|
||||
private TypedContent tc2;
|
||||
private int len;
|
||||
|
||||
AccessPathConsCons() { this = TConsCons(f1, f2, len) }
|
||||
AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
|
||||
|
||||
override string toString() {
|
||||
if len = 2
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
|
||||
else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
|
||||
}
|
||||
|
||||
override Content getHead() { result = f1 }
|
||||
override TypedContent getHead() { result = tc1 }
|
||||
|
||||
override int len() { result = len }
|
||||
|
||||
override DataFlowType getType() { result = f1.getContainerType() }
|
||||
override DataFlowType getType() { result = tc1.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f1) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc1) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) {
|
||||
head = f1 and
|
||||
override predicate pop(TypedContent head, AccessPath tail) {
|
||||
head = tc1 and
|
||||
(
|
||||
tail = TConsCons(f2, _, len - 1)
|
||||
tail = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
tail = TConsNil(f2, _)
|
||||
tail = TConsNil(tc2, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the access path obtained by popping `f` from `ap`, if any. */
|
||||
private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
|
||||
/** Gets the access path obtained by popping `tc` from `ap`, if any. */
|
||||
private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
|
||||
|
||||
/** Gets the access path obtained by pushing `f` onto `ap`. */
|
||||
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
|
||||
/** Gets the access path obtained by pushing `tc` onto `ap`. */
|
||||
private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
|
||||
|
||||
private newtype TAccessPathOption =
|
||||
TAccessPathNone() or
|
||||
@@ -1678,15 +1694,12 @@ private predicate flowFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPath ap0 |
|
||||
flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
|
||||
ap = push(f, ap0)
|
||||
)
|
||||
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
|
||||
flowFwdConsCand(f, apf, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
|
||||
flowFwdConsCand(tc, apf, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1710,54 +1723,63 @@ private predicate flowFwd0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore(
|
||||
Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFront apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
flowFwdStore1(mid, f, node, apf0, apf, config)
|
||||
flowFwdStore0(mid, tc, node, apf0, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
|
||||
private predicate storeCand(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
|
||||
Configuration config
|
||||
) {
|
||||
storeCand2(mid, f, node, config) and
|
||||
flowCand(mid, _, _, apf0, config)
|
||||
storeCand2(mid, tc, node, _, config) and
|
||||
flowCand(mid, _, _, apf0, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowFwdStore1(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
Configuration config
|
||||
) {
|
||||
flowFwdStore0(mid, f, node, apf0, config) and
|
||||
flowCandConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f) and
|
||||
storeCand(mid, tc, node, apf0, apf, config) and
|
||||
flowCandConsCand(tc, apf0, config) and
|
||||
flowCand(node, _, _, apf, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
|
||||
Configuration config
|
||||
private predicate flowFwdRead0(
|
||||
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
|
||||
boolean fromArg, AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCand(node, _, _, _, unbind(config))
|
||||
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
|
||||
readCandFwd(node1, tc, apf0, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
|
||||
flowCand(node, _, _, apf, unbind(config)) and
|
||||
flowCandConsCand(tc, apf, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdConsCand(
|
||||
Content f, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node n |
|
||||
flowFwd(n, _, _, apf, ap, config) and
|
||||
flowFwdStore1(n, f, _, apf, _, config)
|
||||
flowFwdStore0(n, tc, _, apf, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1863,9 +1885,9 @@ private predicate flow0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
flowStore(f, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(f, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowStore(tc, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(tc, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
@@ -1895,39 +1917,41 @@ private predicate flow0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
storeCand2(node1, f, node2, config) and
|
||||
flowFwdStore(node2, f, ap, _, _, _, config) and
|
||||
ap0 = push(f, ap)
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flowFwdStore(node2, tc, ap, _, _, _, config) and
|
||||
ap0 = push(tc, ap)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowStore(
|
||||
Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPath ap0 |
|
||||
storeFlowFwd(node, f, mid, ap, ap0, config) and
|
||||
storeFlowFwd(node, tc, mid, ap, ap0, config) and
|
||||
flow(mid, toReturn, returnAp, ap0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
readCand2(node1, f, node2, config) and
|
||||
flowFwdRead(node2, f, ap, _, _, config) and
|
||||
ap0 = pop(f, ap) and
|
||||
flowFwdConsCand(f, _, ap0, unbind(config))
|
||||
exists(AccessPathFrontHead apf |
|
||||
readCandFwd(node1, tc, apf, node2, config) and
|
||||
flowFwdRead(node2, apf, ap, _, _, _, config) and
|
||||
ap0 = pop(tc, ap) and
|
||||
flowFwdConsCand(tc, _, ap0, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
|
||||
private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
|
||||
exists(Node n, Node mid |
|
||||
flow(mid, _, _, ap, config) and
|
||||
readFlowFwd(n, f, mid, _, ap, config)
|
||||
readFlowFwd(n, tc, mid, _, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2270,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = TNil(getErasedNodeTypeBound(node))
|
||||
or
|
||||
exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
@@ -2284,30 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2) and
|
||||
private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
readCandFwd(node1, tc, _, node2, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
|
||||
private predicate pathReadStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
readCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2) and
|
||||
private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathStoreStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
@@ -2538,10 +2564,10 @@ private module FlowExploration {
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(Content f, int len) { len in [1 .. 5] }
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -2550,7 +2576,7 @@ private module FlowExploration {
|
||||
private class PartialAccessPath extends TPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TPartialCons(result, _) }
|
||||
TypedContent getHead() { this = TPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TPartialNil(_) and result = 0
|
||||
@@ -2561,7 +2587,7 @@ private module FlowExploration {
|
||||
DataFlowType getType() {
|
||||
this = TPartialNil(result)
|
||||
or
|
||||
exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
@@ -2579,15 +2605,15 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
|
||||
if len = 1
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc.toString() + "]"
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2763,11 +2789,12 @@ private module FlowExploration {
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(PartialAccessPath ap0, Content f |
|
||||
partialPathReadStep(mid, ap0, f, node, cc, config) and
|
||||
exists(PartialAccessPath ap0, TypedContent tc |
|
||||
partialPathReadStep(mid, ap0, tc, node, cc, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
apConsFwd(ap, tc, ap0, config) and
|
||||
compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
|
||||
@@ -2786,35 +2813,42 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
ap1 = mid.getAp() and
|
||||
store(mid.getNode(), f, node) and
|
||||
ap2.getHead() = f and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), f.getType())
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
store(midNode, tc, node, contentType) and
|
||||
ap2.getHead() = tc and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, f, _, ap2) and
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
ap = mid.getAp() and
|
||||
readStep(mid.getNode(), f, node) and
|
||||
ap.getHead() = f and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
read(midNode, tc.getContent(), node) and
|
||||
ap.getHead() = tc and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
|
||||
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
exists(Node mid |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
store(mid, _, node) and
|
||||
store(mid, _, node, _) and
|
||||
not outBarrier(mid, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(f, config) and
|
||||
exists(Content c |
|
||||
nodeCandFwd1Read(c, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(c, config) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
or
|
||||
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
read(mid, f, node)
|
||||
read(mid, c, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
|
||||
exists(Node mid, Node node, TypedContent tc |
|
||||
not fullBarrier(node, config) and
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, config) and
|
||||
store(mid, f, node)
|
||||
store(mid, tc, node, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, toReturn, config) and
|
||||
nodeCand1IsRead(f, config)
|
||||
exists(Content c |
|
||||
nodeCand1Store(c, node, toReturn, config) and
|
||||
nodeCand1IsRead(c, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
exists(Node mid, Content c |
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, toReturn, config)
|
||||
)
|
||||
or
|
||||
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1IsRead(Content f, Configuration config) {
|
||||
private predicate nodeCand1IsRead(Content c, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(node, unbind(config)) and
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
nodeCand1(mid, toReturn, config) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
store(node, tc, mid, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a read and a store in the flow covered
|
||||
* Holds if `c` is the target of both a read and a store in the flow covered
|
||||
* by `nodeCand1`.
|
||||
*/
|
||||
private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
|
||||
nodeCand1IsRead(f, conf) and
|
||||
nodeCand1Store(f, _, _, conf)
|
||||
private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
|
||||
nodeCand1IsRead(c, conf) and
|
||||
nodeCand1Store(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, f, n2)
|
||||
private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, tc, n2, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate read(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
private predicate read(Node n1, Content c, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
read(n1, f, n2)
|
||||
read(n1, c, n2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, _, config) and
|
||||
store(mid, f, node, config) and
|
||||
storeCand1(mid, _, node, config) and
|
||||
stored = true
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(f, stored, config)
|
||||
exists(Content c |
|
||||
nodeCandFwd2Read(c, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(c, stored, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
|
||||
private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCand1(node, unbind(config)) and
|
||||
nodeCandFwd2(mid, _, _, stored, config) and
|
||||
store(mid, f, node, config)
|
||||
storeCand1(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(
|
||||
Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, true, config) and
|
||||
read(mid, f, node, config)
|
||||
read(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -896,15 +901,15 @@ private predicate nodeCand2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(f, read, config)
|
||||
exists(Content c |
|
||||
nodeCand2Store(c, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(c, read, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f, boolean read0 |
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
|
||||
exists(Node mid, Content c, boolean read0 |
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
|
||||
nodeCand2(mid, toReturn, returnRead, read0, config) and
|
||||
read = true
|
||||
)
|
||||
@@ -930,51 +935,51 @@ private predicate nodeCand2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
|
||||
private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd2(node, _, _, true, unbind(config)) and
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
|
||||
nodeCand2(mid, _, _, read, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2Store(
|
||||
Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid, config) and
|
||||
storeCand1(node, c, mid, config) and
|
||||
nodeCand2(mid, toReturn, returnRead, true, config) and
|
||||
nodeCandFwd2(node, _, _, stored, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
|
||||
private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, _, stored, conf) and
|
||||
nodeCand2Store(c, node, _, _, stored, conf) and
|
||||
nodeCand2(node, _, _, stored, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
* Holds if `c` is the target of both a store and a read in the path graph
|
||||
* covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
|
||||
private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
|
||||
exists(boolean apNonEmpty |
|
||||
nodeCand2IsStored(f, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(f, apNonEmpty, conf)
|
||||
nodeCand2IsStored(c, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(c, apNonEmpty, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1058,7 +1063,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(_, node, config) or
|
||||
node instanceof ParameterNode or
|
||||
node instanceof OutNodeExt or
|
||||
store(_, _, node) or
|
||||
store(_, _, node, _) or
|
||||
read(_, _, node) or
|
||||
node instanceof CastNode
|
||||
)
|
||||
@@ -1074,7 +1079,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
store(node, _, next) or
|
||||
store(node, _, next, _) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
@@ -1154,19 +1159,21 @@ private module LocalFlowBigStep {
|
||||
private import LocalFlowBigStep
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2, config) and
|
||||
private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
|
||||
read(node1, c, node2, config) and
|
||||
nodeCand2(node1, _, _, true, unbind(config)) and
|
||||
nodeCand2(node2, config) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(c, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2, config) and
|
||||
private predicate storeCand2(
|
||||
Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(node1, tc, node2, contentType) and
|
||||
nodeCand2(node1, config) and
|
||||
nodeCand2(node2, _, _, true, unbind(config)) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1227,17 +1234,18 @@ private predicate flowCandFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
flowCandFwd(mid, fromArg, argApf, _, config) and
|
||||
storeCand2(mid, f, node, config) and
|
||||
exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
storeCand2(mid, tc, node, contentType, config) and
|
||||
nodeCand2(node, _, _, true, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
apf.headUsesContent(tc) and
|
||||
compatibleTypes(apf0.getType(), contentType)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(TypedContent tc |
|
||||
flowCandFwdRead(tc, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
|
||||
)
|
||||
or
|
||||
@@ -1261,24 +1269,30 @@ private predicate flowCandFwd0(
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n, DataFlowType contentType |
|
||||
flowCandFwd(mid, _, _, apf, config) and
|
||||
storeCand2(mid, f, n, config) and
|
||||
storeCand2(mid, tc, n, contentType, config) and
|
||||
nodeCand2(n, _, _, true, unbind(config)) and
|
||||
compatibleTypes(apf.getType(), f.getType())
|
||||
compatibleTypes(apf.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
private predicate flowCandFwdRead0(
|
||||
Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
|
||||
AccessPathFrontHead apf, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f)
|
||||
)
|
||||
flowCandFwd(node1, fromArg, argApf, apf, config) and
|
||||
readCand2(node1, c, node2, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1385,17 +1399,15 @@ private predicate flowCand0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPathFrontHead apf0 |
|
||||
flowCandStore(node, f, toReturn, returnApf, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandConsCand(f, apf, config)
|
||||
exists(TypedContent tc |
|
||||
flowCandStore(node, tc, apf, toReturn, returnApf, config) and
|
||||
flowCandConsCand(tc, apf, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
exists(TypedContent tc, AccessPathFront apf0 |
|
||||
flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(tc, apf0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1417,36 +1429,40 @@ private predicate flowCand0(
|
||||
else returnApf = TAccessPathFrontNone()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCandFwd(
|
||||
Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandRead(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
readCand2(node, f, mid, config) and
|
||||
readCandFwd(node, tc, apf, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
storeCand2(node, f, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
flowCandFwd(node, _, _, apf, config) and
|
||||
storeCand2(node, tc, mid, _, unbind(config)) and
|
||||
flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(Node n, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(n, _, _, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandRead(n, f, _, _, apf, config)
|
||||
)
|
||||
private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
flowCandRead(_, tc, _, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1499,13 +1515,13 @@ private predicate flowCandIsReturned(
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
|
||||
TConsCons(Content f1, Content f2, int len) {
|
||||
flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
|
||||
TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -1514,7 +1530,7 @@ private newtype TAccessPath =
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
abstract Content getHead();
|
||||
abstract TypedContent getHead();
|
||||
|
||||
abstract int len();
|
||||
|
||||
@@ -1525,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
/**
|
||||
* Holds if this access path has `head` at the front and may be followed by `tail`.
|
||||
*/
|
||||
abstract predicate pop(Content head, AccessPath tail);
|
||||
abstract predicate pop(TypedContent head, AccessPath tail);
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
@@ -1535,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override string toString() { result = concat(": " + ppReprType(t)) }
|
||||
|
||||
override Content getHead() { none() }
|
||||
override TypedContent getHead() { none() }
|
||||
|
||||
override int len() { result = 0 }
|
||||
|
||||
@@ -1543,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontNil(t) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { none() }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { none() }
|
||||
}
|
||||
|
||||
abstract private class AccessPathCons extends AccessPath { }
|
||||
|
||||
private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
private Content f;
|
||||
private TypedContent tc;
|
||||
private DataFlowType t;
|
||||
|
||||
AccessPathConsNil() { this = TConsNil(f, t) }
|
||||
AccessPathConsNil() { this = TConsNil(tc, t) }
|
||||
|
||||
override string toString() {
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
}
|
||||
|
||||
override Content getHead() { result = f }
|
||||
override TypedContent getHead() { result = tc }
|
||||
|
||||
override int len() { result = 1 }
|
||||
|
||||
override DataFlowType getType() { result = f.getContainerType() }
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
|
||||
}
|
||||
|
||||
private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
private Content f1;
|
||||
private Content f2;
|
||||
private TypedContent tc1;
|
||||
private TypedContent tc2;
|
||||
private int len;
|
||||
|
||||
AccessPathConsCons() { this = TConsCons(f1, f2, len) }
|
||||
AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
|
||||
|
||||
override string toString() {
|
||||
if len = 2
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
|
||||
else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
|
||||
}
|
||||
|
||||
override Content getHead() { result = f1 }
|
||||
override TypedContent getHead() { result = tc1 }
|
||||
|
||||
override int len() { result = len }
|
||||
|
||||
override DataFlowType getType() { result = f1.getContainerType() }
|
||||
override DataFlowType getType() { result = tc1.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f1) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc1) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) {
|
||||
head = f1 and
|
||||
override predicate pop(TypedContent head, AccessPath tail) {
|
||||
head = tc1 and
|
||||
(
|
||||
tail = TConsCons(f2, _, len - 1)
|
||||
tail = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
tail = TConsNil(f2, _)
|
||||
tail = TConsNil(tc2, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the access path obtained by popping `f` from `ap`, if any. */
|
||||
private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
|
||||
/** Gets the access path obtained by popping `tc` from `ap`, if any. */
|
||||
private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
|
||||
|
||||
/** Gets the access path obtained by pushing `f` onto `ap`. */
|
||||
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
|
||||
/** Gets the access path obtained by pushing `tc` onto `ap`. */
|
||||
private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
|
||||
|
||||
private newtype TAccessPathOption =
|
||||
TAccessPathNone() or
|
||||
@@ -1678,15 +1694,12 @@ private predicate flowFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPath ap0 |
|
||||
flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
|
||||
ap = push(f, ap0)
|
||||
)
|
||||
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
|
||||
flowFwdConsCand(f, apf, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
|
||||
flowFwdConsCand(tc, apf, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1710,54 +1723,63 @@ private predicate flowFwd0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore(
|
||||
Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFront apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
flowFwdStore1(mid, f, node, apf0, apf, config)
|
||||
flowFwdStore0(mid, tc, node, apf0, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
|
||||
private predicate storeCand(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
|
||||
Configuration config
|
||||
) {
|
||||
storeCand2(mid, f, node, config) and
|
||||
flowCand(mid, _, _, apf0, config)
|
||||
storeCand2(mid, tc, node, _, config) and
|
||||
flowCand(mid, _, _, apf0, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowFwdStore1(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
Configuration config
|
||||
) {
|
||||
flowFwdStore0(mid, f, node, apf0, config) and
|
||||
flowCandConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f) and
|
||||
storeCand(mid, tc, node, apf0, apf, config) and
|
||||
flowCandConsCand(tc, apf0, config) and
|
||||
flowCand(node, _, _, apf, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
|
||||
Configuration config
|
||||
private predicate flowFwdRead0(
|
||||
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
|
||||
boolean fromArg, AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCand(node, _, _, _, unbind(config))
|
||||
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
|
||||
readCandFwd(node1, tc, apf0, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
|
||||
flowCand(node, _, _, apf, unbind(config)) and
|
||||
flowCandConsCand(tc, apf, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdConsCand(
|
||||
Content f, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node n |
|
||||
flowFwd(n, _, _, apf, ap, config) and
|
||||
flowFwdStore1(n, f, _, apf, _, config)
|
||||
flowFwdStore0(n, tc, _, apf, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1863,9 +1885,9 @@ private predicate flow0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
flowStore(f, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(f, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowStore(tc, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(tc, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
@@ -1895,39 +1917,41 @@ private predicate flow0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
storeCand2(node1, f, node2, config) and
|
||||
flowFwdStore(node2, f, ap, _, _, _, config) and
|
||||
ap0 = push(f, ap)
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flowFwdStore(node2, tc, ap, _, _, _, config) and
|
||||
ap0 = push(tc, ap)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowStore(
|
||||
Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPath ap0 |
|
||||
storeFlowFwd(node, f, mid, ap, ap0, config) and
|
||||
storeFlowFwd(node, tc, mid, ap, ap0, config) and
|
||||
flow(mid, toReturn, returnAp, ap0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
readCand2(node1, f, node2, config) and
|
||||
flowFwdRead(node2, f, ap, _, _, config) and
|
||||
ap0 = pop(f, ap) and
|
||||
flowFwdConsCand(f, _, ap0, unbind(config))
|
||||
exists(AccessPathFrontHead apf |
|
||||
readCandFwd(node1, tc, apf, node2, config) and
|
||||
flowFwdRead(node2, apf, ap, _, _, _, config) and
|
||||
ap0 = pop(tc, ap) and
|
||||
flowFwdConsCand(tc, _, ap0, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
|
||||
private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
|
||||
exists(Node n, Node mid |
|
||||
flow(mid, _, _, ap, config) and
|
||||
readFlowFwd(n, f, mid, _, ap, config)
|
||||
readFlowFwd(n, tc, mid, _, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2270,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = TNil(getErasedNodeTypeBound(node))
|
||||
or
|
||||
exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
@@ -2284,30 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2) and
|
||||
private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
readCandFwd(node1, tc, _, node2, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
|
||||
private predicate pathReadStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
readCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2) and
|
||||
private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathStoreStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
@@ -2538,10 +2564,10 @@ private module FlowExploration {
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(Content f, int len) { len in [1 .. 5] }
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -2550,7 +2576,7 @@ private module FlowExploration {
|
||||
private class PartialAccessPath extends TPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TPartialCons(result, _) }
|
||||
TypedContent getHead() { this = TPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TPartialNil(_) and result = 0
|
||||
@@ -2561,7 +2587,7 @@ private module FlowExploration {
|
||||
DataFlowType getType() {
|
||||
this = TPartialNil(result)
|
||||
or
|
||||
exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
@@ -2579,15 +2605,15 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
|
||||
if len = 1
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc.toString() + "]"
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2763,11 +2789,12 @@ private module FlowExploration {
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(PartialAccessPath ap0, Content f |
|
||||
partialPathReadStep(mid, ap0, f, node, cc, config) and
|
||||
exists(PartialAccessPath ap0, TypedContent tc |
|
||||
partialPathReadStep(mid, ap0, tc, node, cc, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
apConsFwd(ap, tc, ap0, config) and
|
||||
compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
|
||||
@@ -2786,35 +2813,42 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
ap1 = mid.getAp() and
|
||||
store(mid.getNode(), f, node) and
|
||||
ap2.getHead() = f and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), f.getType())
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
store(midNode, tc, node, contentType) and
|
||||
ap2.getHead() = tc and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, f, _, ap2) and
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
ap = mid.getAp() and
|
||||
readStep(mid.getNode(), f, node) and
|
||||
ap.getHead() = f and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
read(midNode, tc.getContent(), node) and
|
||||
ap.getHead() = tc and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
|
||||
@@ -198,123 +198,130 @@ private module Cached {
|
||||
/**
|
||||
* The final flow-through calculation:
|
||||
*
|
||||
* - Input access paths are abstracted with a `ContentOption` parameter
|
||||
* that represents the head of the access path. `TContentNone()` means that
|
||||
* the access path is unrestricted.
|
||||
* - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`)
|
||||
* or summarized as a single read step with before and after types recorded
|
||||
* in the `ReadStepTypesOption` parameter.
|
||||
* - Types are checked using the `compatibleTypes()` relation.
|
||||
*/
|
||||
private module Final {
|
||||
/**
|
||||
* Holds if `p` can flow to `node` in the same callable using only
|
||||
* value-preserving steps, not taking call contexts into account.
|
||||
* value-preserving steps and possibly a single read step, not taking
|
||||
* call contexts into account.
|
||||
*
|
||||
* `contentIn` describes the content of `p` that can flow to `node`
|
||||
* (if any).
|
||||
* If a read step was taken, then `read` captures the `Content`, the
|
||||
* container type, and the content type.
|
||||
*/
|
||||
predicate parameterValueFlow(ParameterNode p, Node node, ContentOption contentIn) {
|
||||
parameterValueFlow0(p, node, contentIn) and
|
||||
predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) {
|
||||
parameterValueFlow0(p, node, read) and
|
||||
if node instanceof CastingNode
|
||||
then
|
||||
// normal flow through
|
||||
contentIn = TContentNone() and
|
||||
read = TReadStepTypesNone() and
|
||||
compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node))
|
||||
or
|
||||
// getter
|
||||
exists(Content fIn |
|
||||
contentIn.getContent() = fIn and
|
||||
compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
compatibleTypes(read.getContentType(), getErasedNodeTypeBound(node))
|
||||
else any()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlow0(ParameterNode p, Node node, ContentOption contentIn) {
|
||||
private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) {
|
||||
p = node and
|
||||
Cand::cand(p, _) and
|
||||
contentIn = TContentNone()
|
||||
read = TReadStepTypesNone()
|
||||
or
|
||||
// local flow
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid, contentIn) and
|
||||
parameterValueFlow(p, mid, read) and
|
||||
LocalFlowBigStep::localFlowBigStep(mid, node)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
parameterValueFlow(p, mid, TContentNone()) and
|
||||
readStep(mid, f, node) and
|
||||
contentIn.getContent() = f and
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid, TReadStepTypesNone()) and
|
||||
readStepWithTypes(mid, read.getContainerType(), read.getContent(), node,
|
||||
read.getContentType()) and
|
||||
Cand::parameterValueFlowReturnCand(p, _, true) and
|
||||
compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType())
|
||||
compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType())
|
||||
)
|
||||
or
|
||||
// flow through: no prior read
|
||||
exists(ArgumentNode arg |
|
||||
parameterValueFlowArg(p, arg, TContentNone()) and
|
||||
argumentValueFlowsThrough(arg, contentIn, node)
|
||||
parameterValueFlowArg(p, arg, TReadStepTypesNone()) and
|
||||
argumentValueFlowsThrough(arg, read, node)
|
||||
)
|
||||
or
|
||||
// flow through: no read inside method
|
||||
exists(ArgumentNode arg |
|
||||
parameterValueFlowArg(p, arg, contentIn) and
|
||||
argumentValueFlowsThrough(arg, TContentNone(), node)
|
||||
parameterValueFlowArg(p, arg, read) and
|
||||
argumentValueFlowsThrough(arg, TReadStepTypesNone(), node)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlowArg(
|
||||
ParameterNode p, ArgumentNode arg, ContentOption contentIn
|
||||
ParameterNode p, ArgumentNode arg, ReadStepTypesOption read
|
||||
) {
|
||||
parameterValueFlow(p, arg, contentIn) and
|
||||
parameterValueFlow(p, arg, read) and
|
||||
Cand::argumentValueFlowsThroughCand(arg, _, _)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate argumentValueFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read
|
||||
) {
|
||||
exists(ParameterNode param | viableParamArg(call, param, arg) |
|
||||
parameterValueFlowReturn(param, kind, contentIn)
|
||||
parameterValueFlowReturn(param, kind, read)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
|
||||
* not taking call contexts into account.
|
||||
* Holds if `arg` flows to `out` through a call using only
|
||||
* value-preserving steps and possibly a single read step, not taking
|
||||
* call contexts into account.
|
||||
*
|
||||
* `contentIn` describes the content of `arg` that can flow to `out` (if any).
|
||||
* If a read step was taken, then `read` captures the `Content`, the
|
||||
* container type, and the content type.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate argumentValueFlowsThrough(ArgumentNode arg, ContentOption contentIn, Node out) {
|
||||
predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
argumentValueFlowsThrough0(call, arg, kind, contentIn) and
|
||||
argumentValueFlowsThrough0(call, arg, kind, read) and
|
||||
out = getAnOutNode(call, kind)
|
||||
|
|
||||
// normal flow through
|
||||
contentIn = TContentNone() and
|
||||
read = TReadStepTypesNone() and
|
||||
compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out))
|
||||
or
|
||||
// getter
|
||||
exists(Content fIn |
|
||||
contentIn.getContent() = fIn and
|
||||
compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and
|
||||
compatibleTypes(fIn.getType(), getErasedNodeTypeBound(out))
|
||||
)
|
||||
compatibleTypes(getErasedNodeTypeBound(arg), read.getContainerType()) and
|
||||
compatibleTypes(read.getContentType(), getErasedNodeTypeBound(out))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` flows to `out` through a call using only
|
||||
* value-preserving steps and a single read step, not taking call
|
||||
* contexts into account, thus representing a getter-step.
|
||||
*/
|
||||
predicate getterStep(ArgumentNode arg, Content c, Node out) {
|
||||
argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `p` can flow to a return node of kind `kind` in the same
|
||||
* callable using only value-preserving steps.
|
||||
* callable using only value-preserving steps and possibly a single read
|
||||
* step.
|
||||
*
|
||||
* `contentIn` describes the content of `p` that can flow to the return
|
||||
* node (if any).
|
||||
* If a read step was taken, then `read` captures the `Content`, the
|
||||
* container type, and the content type.
|
||||
*/
|
||||
private predicate parameterValueFlowReturn(
|
||||
ParameterNode p, ReturnKind kind, ContentOption contentIn
|
||||
ParameterNode p, ReturnKind kind, ReadStepTypesOption read
|
||||
) {
|
||||
exists(ReturnNode ret |
|
||||
parameterValueFlow(p, ret, contentIn) and
|
||||
parameterValueFlow(p, ret, read) and
|
||||
kind = ret.getKind()
|
||||
)
|
||||
}
|
||||
@@ -329,7 +336,27 @@ private module Cached {
|
||||
*/
|
||||
cached
|
||||
predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) {
|
||||
parameterValueFlow(p, n.getPreUpdateNode(), TContentNone())
|
||||
parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone())
|
||||
}
|
||||
|
||||
private predicate store(
|
||||
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
|
||||
) {
|
||||
storeStep(node1, c, node2) and
|
||||
readStep(_, c, _) and
|
||||
contentType = getErasedNodeTypeBound(node1) and
|
||||
containerType = getErasedNodeTypeBound(node2)
|
||||
or
|
||||
exists(Node n1, Node n2 |
|
||||
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
|
||||
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
||||
|
|
||||
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1)
|
||||
or
|
||||
readStep(n2, c, n1) and
|
||||
contentType = getErasedNodeTypeBound(n1) and
|
||||
containerType = getErasedNodeTypeBound(n2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -340,17 +367,8 @@ private module Cached {
|
||||
* been stored into, in order to handle cases like `x.f1.f2 = y`.
|
||||
*/
|
||||
cached
|
||||
predicate store(Node node1, Content f, Node node2) {
|
||||
storeStep(node1, f, node2) and readStep(_, f, _)
|
||||
or
|
||||
exists(Node n1, Node n2 |
|
||||
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
|
||||
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
||||
|
|
||||
argumentValueFlowsThrough(n2, TContentSome(f), n1)
|
||||
or
|
||||
readStep(n2, f, n1)
|
||||
)
|
||||
predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
|
||||
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
|
||||
}
|
||||
|
||||
import FlowThrough
|
||||
@@ -397,10 +415,13 @@ private module Cached {
|
||||
TBooleanNone() or
|
||||
TBooleanSome(boolean b) { b = true or b = false }
|
||||
|
||||
cached
|
||||
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
|
||||
|
||||
cached
|
||||
newtype TAccessPathFront =
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(Content f)
|
||||
TFrontHead(TypedContent tc)
|
||||
|
||||
cached
|
||||
newtype TAccessPathFrontOption =
|
||||
@@ -415,25 +436,38 @@ class CastingNode extends Node {
|
||||
CastingNode() {
|
||||
this instanceof ParameterNode or
|
||||
this instanceof CastNode or
|
||||
this instanceof OutNodeExt
|
||||
this instanceof OutNodeExt or
|
||||
// For reads, `x.f`, we want to check that the tracked type after the read (which
|
||||
// is obtained by popping the head of the access path stack) is compatible with
|
||||
// the type of `x.f`.
|
||||
readStep(_, _, this)
|
||||
}
|
||||
}
|
||||
|
||||
newtype TContentOption =
|
||||
TContentNone() or
|
||||
TContentSome(Content f)
|
||||
private predicate readStepWithTypes(
|
||||
Node n1, DataFlowType container, Content c, Node n2, DataFlowType content
|
||||
) {
|
||||
readStep(n1, c, n2) and
|
||||
container = getErasedNodeTypeBound(n1) and
|
||||
content = getErasedNodeTypeBound(n2)
|
||||
}
|
||||
|
||||
private class ContentOption extends TContentOption {
|
||||
Content getContent() { this = TContentSome(result) }
|
||||
|
||||
predicate hasContent() { exists(this.getContent()) }
|
||||
|
||||
string toString() {
|
||||
result = this.getContent().toString()
|
||||
or
|
||||
not this.hasContent() and
|
||||
result = "<none>"
|
||||
private newtype TReadStepTypesOption =
|
||||
TReadStepTypesNone() or
|
||||
TReadStepTypesSome(DataFlowType container, Content c, DataFlowType content) {
|
||||
readStepWithTypes(_, container, c, _, content)
|
||||
}
|
||||
|
||||
private class ReadStepTypesOption extends TReadStepTypesOption {
|
||||
predicate isSome() { this instanceof TReadStepTypesSome }
|
||||
|
||||
DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) }
|
||||
|
||||
Content getContent() { this = TReadStepTypesSome(_, result, _) }
|
||||
|
||||
DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) }
|
||||
|
||||
string toString() { if this.isSome() then result = "Some(..)" else result = "None()" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -692,6 +726,23 @@ class BooleanOption extends TBooleanOption {
|
||||
}
|
||||
}
|
||||
|
||||
/** Content tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
private DataFlowType t;
|
||||
|
||||
TypedContent() { this = MkTypedContent(c, t) }
|
||||
|
||||
/** Gets the content. */
|
||||
Content getContent() { result = c }
|
||||
|
||||
/** Gets the container type. */
|
||||
DataFlowType getContainerType() { result = t }
|
||||
|
||||
/** Gets a textual representation of this content. */
|
||||
string toString() { result = c.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The front of an access path. This is either a head or a nil.
|
||||
*/
|
||||
@@ -702,25 +753,29 @@ abstract class AccessPathFront extends TAccessPathFront {
|
||||
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
predicate headUsesContent(Content f) { this = TFrontHead(f) }
|
||||
predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) }
|
||||
}
|
||||
|
||||
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
}
|
||||
private DataFlowType t;
|
||||
|
||||
override DataFlowType getType() { this = TFrontNil(result) }
|
||||
AccessPathFrontNil() { this = TFrontNil(t) }
|
||||
|
||||
override string toString() { result = ppReprType(t) }
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = false }
|
||||
}
|
||||
|
||||
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
|
||||
override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) }
|
||||
private TypedContent tc;
|
||||
|
||||
override DataFlowType getType() {
|
||||
exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
|
||||
}
|
||||
AccessPathFrontHead() { this = TFrontHead(tc) }
|
||||
|
||||
override string toString() { result = tc.toString() }
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = true }
|
||||
}
|
||||
|
||||
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
exists(Node mid |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
store(mid, _, node) and
|
||||
store(mid, _, node, _) and
|
||||
not outBarrier(mid, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(f, config) and
|
||||
exists(Content c |
|
||||
nodeCandFwd1Read(c, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(c, config) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
or
|
||||
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
read(mid, f, node)
|
||||
read(mid, c, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
|
||||
exists(Node mid, Node node, TypedContent tc |
|
||||
not fullBarrier(node, config) and
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, config) and
|
||||
store(mid, f, node)
|
||||
store(mid, tc, node, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, toReturn, config) and
|
||||
nodeCand1IsRead(f, config)
|
||||
exists(Content c |
|
||||
nodeCand1Store(c, node, toReturn, config) and
|
||||
nodeCand1IsRead(c, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
exists(Node mid, Content c |
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, toReturn, config)
|
||||
)
|
||||
or
|
||||
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1IsRead(Content f, Configuration config) {
|
||||
private predicate nodeCand1IsRead(Content c, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(node, unbind(config)) and
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
nodeCand1(mid, toReturn, config) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
store(node, tc, mid, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a read and a store in the flow covered
|
||||
* Holds if `c` is the target of both a read and a store in the flow covered
|
||||
* by `nodeCand1`.
|
||||
*/
|
||||
private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
|
||||
nodeCand1IsRead(f, conf) and
|
||||
nodeCand1Store(f, _, _, conf)
|
||||
private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
|
||||
nodeCand1IsRead(c, conf) and
|
||||
nodeCand1Store(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, f, n2)
|
||||
private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, tc, n2, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate read(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
private predicate read(Node n1, Content c, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
read(n1, f, n2)
|
||||
read(n1, c, n2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, _, config) and
|
||||
store(mid, f, node, config) and
|
||||
storeCand1(mid, _, node, config) and
|
||||
stored = true
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(f, stored, config)
|
||||
exists(Content c |
|
||||
nodeCandFwd2Read(c, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(c, stored, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
|
||||
private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCand1(node, unbind(config)) and
|
||||
nodeCandFwd2(mid, _, _, stored, config) and
|
||||
store(mid, f, node, config)
|
||||
storeCand1(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(
|
||||
Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, true, config) and
|
||||
read(mid, f, node, config)
|
||||
read(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -896,15 +901,15 @@ private predicate nodeCand2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(f, read, config)
|
||||
exists(Content c |
|
||||
nodeCand2Store(c, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(c, read, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f, boolean read0 |
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
|
||||
exists(Node mid, Content c, boolean read0 |
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
|
||||
nodeCand2(mid, toReturn, returnRead, read0, config) and
|
||||
read = true
|
||||
)
|
||||
@@ -930,51 +935,51 @@ private predicate nodeCand2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
|
||||
private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd2(node, _, _, true, unbind(config)) and
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
|
||||
nodeCand2(mid, _, _, read, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2Store(
|
||||
Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid, config) and
|
||||
storeCand1(node, c, mid, config) and
|
||||
nodeCand2(mid, toReturn, returnRead, true, config) and
|
||||
nodeCandFwd2(node, _, _, stored, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
|
||||
private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, _, stored, conf) and
|
||||
nodeCand2Store(c, node, _, _, stored, conf) and
|
||||
nodeCand2(node, _, _, stored, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
* Holds if `c` is the target of both a store and a read in the path graph
|
||||
* covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
|
||||
private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
|
||||
exists(boolean apNonEmpty |
|
||||
nodeCand2IsStored(f, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(f, apNonEmpty, conf)
|
||||
nodeCand2IsStored(c, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(c, apNonEmpty, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1058,7 +1063,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(_, node, config) or
|
||||
node instanceof ParameterNode or
|
||||
node instanceof OutNodeExt or
|
||||
store(_, _, node) or
|
||||
store(_, _, node, _) or
|
||||
read(_, _, node) or
|
||||
node instanceof CastNode
|
||||
)
|
||||
@@ -1074,7 +1079,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
store(node, _, next) or
|
||||
store(node, _, next, _) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
@@ -1154,19 +1159,21 @@ private module LocalFlowBigStep {
|
||||
private import LocalFlowBigStep
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2, config) and
|
||||
private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
|
||||
read(node1, c, node2, config) and
|
||||
nodeCand2(node1, _, _, true, unbind(config)) and
|
||||
nodeCand2(node2, config) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(c, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2, config) and
|
||||
private predicate storeCand2(
|
||||
Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(node1, tc, node2, contentType) and
|
||||
nodeCand2(node1, config) and
|
||||
nodeCand2(node2, _, _, true, unbind(config)) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1227,17 +1234,18 @@ private predicate flowCandFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
flowCandFwd(mid, fromArg, argApf, _, config) and
|
||||
storeCand2(mid, f, node, config) and
|
||||
exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
storeCand2(mid, tc, node, contentType, config) and
|
||||
nodeCand2(node, _, _, true, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
apf.headUsesContent(tc) and
|
||||
compatibleTypes(apf0.getType(), contentType)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(TypedContent tc |
|
||||
flowCandFwdRead(tc, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
|
||||
)
|
||||
or
|
||||
@@ -1261,24 +1269,30 @@ private predicate flowCandFwd0(
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n, DataFlowType contentType |
|
||||
flowCandFwd(mid, _, _, apf, config) and
|
||||
storeCand2(mid, f, n, config) and
|
||||
storeCand2(mid, tc, n, contentType, config) and
|
||||
nodeCand2(n, _, _, true, unbind(config)) and
|
||||
compatibleTypes(apf.getType(), f.getType())
|
||||
compatibleTypes(apf.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
private predicate flowCandFwdRead0(
|
||||
Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
|
||||
AccessPathFrontHead apf, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f)
|
||||
)
|
||||
flowCandFwd(node1, fromArg, argApf, apf, config) and
|
||||
readCand2(node1, c, node2, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1385,17 +1399,15 @@ private predicate flowCand0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPathFrontHead apf0 |
|
||||
flowCandStore(node, f, toReturn, returnApf, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandConsCand(f, apf, config)
|
||||
exists(TypedContent tc |
|
||||
flowCandStore(node, tc, apf, toReturn, returnApf, config) and
|
||||
flowCandConsCand(tc, apf, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
exists(TypedContent tc, AccessPathFront apf0 |
|
||||
flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(tc, apf0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1417,36 +1429,40 @@ private predicate flowCand0(
|
||||
else returnApf = TAccessPathFrontNone()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCandFwd(
|
||||
Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandRead(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
readCand2(node, f, mid, config) and
|
||||
readCandFwd(node, tc, apf, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
storeCand2(node, f, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
flowCandFwd(node, _, _, apf, config) and
|
||||
storeCand2(node, tc, mid, _, unbind(config)) and
|
||||
flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(Node n, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(n, _, _, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandRead(n, f, _, _, apf, config)
|
||||
)
|
||||
private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
flowCandRead(_, tc, _, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1499,13 +1515,13 @@ private predicate flowCandIsReturned(
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
|
||||
TConsCons(Content f1, Content f2, int len) {
|
||||
flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
|
||||
TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -1514,7 +1530,7 @@ private newtype TAccessPath =
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
abstract Content getHead();
|
||||
abstract TypedContent getHead();
|
||||
|
||||
abstract int len();
|
||||
|
||||
@@ -1525,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
/**
|
||||
* Holds if this access path has `head` at the front and may be followed by `tail`.
|
||||
*/
|
||||
abstract predicate pop(Content head, AccessPath tail);
|
||||
abstract predicate pop(TypedContent head, AccessPath tail);
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
@@ -1535,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override string toString() { result = concat(": " + ppReprType(t)) }
|
||||
|
||||
override Content getHead() { none() }
|
||||
override TypedContent getHead() { none() }
|
||||
|
||||
override int len() { result = 0 }
|
||||
|
||||
@@ -1543,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontNil(t) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { none() }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { none() }
|
||||
}
|
||||
|
||||
abstract private class AccessPathCons extends AccessPath { }
|
||||
|
||||
private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
private Content f;
|
||||
private TypedContent tc;
|
||||
private DataFlowType t;
|
||||
|
||||
AccessPathConsNil() { this = TConsNil(f, t) }
|
||||
AccessPathConsNil() { this = TConsNil(tc, t) }
|
||||
|
||||
override string toString() {
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
}
|
||||
|
||||
override Content getHead() { result = f }
|
||||
override TypedContent getHead() { result = tc }
|
||||
|
||||
override int len() { result = 1 }
|
||||
|
||||
override DataFlowType getType() { result = f.getContainerType() }
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
|
||||
}
|
||||
|
||||
private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
private Content f1;
|
||||
private Content f2;
|
||||
private TypedContent tc1;
|
||||
private TypedContent tc2;
|
||||
private int len;
|
||||
|
||||
AccessPathConsCons() { this = TConsCons(f1, f2, len) }
|
||||
AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
|
||||
|
||||
override string toString() {
|
||||
if len = 2
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
|
||||
else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
|
||||
}
|
||||
|
||||
override Content getHead() { result = f1 }
|
||||
override TypedContent getHead() { result = tc1 }
|
||||
|
||||
override int len() { result = len }
|
||||
|
||||
override DataFlowType getType() { result = f1.getContainerType() }
|
||||
override DataFlowType getType() { result = tc1.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f1) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc1) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) {
|
||||
head = f1 and
|
||||
override predicate pop(TypedContent head, AccessPath tail) {
|
||||
head = tc1 and
|
||||
(
|
||||
tail = TConsCons(f2, _, len - 1)
|
||||
tail = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
tail = TConsNil(f2, _)
|
||||
tail = TConsNil(tc2, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the access path obtained by popping `f` from `ap`, if any. */
|
||||
private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
|
||||
/** Gets the access path obtained by popping `tc` from `ap`, if any. */
|
||||
private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
|
||||
|
||||
/** Gets the access path obtained by pushing `f` onto `ap`. */
|
||||
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
|
||||
/** Gets the access path obtained by pushing `tc` onto `ap`. */
|
||||
private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
|
||||
|
||||
private newtype TAccessPathOption =
|
||||
TAccessPathNone() or
|
||||
@@ -1678,15 +1694,12 @@ private predicate flowFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPath ap0 |
|
||||
flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
|
||||
ap = push(f, ap0)
|
||||
)
|
||||
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
|
||||
flowFwdConsCand(f, apf, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
|
||||
flowFwdConsCand(tc, apf, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1710,54 +1723,63 @@ private predicate flowFwd0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore(
|
||||
Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFront apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
flowFwdStore1(mid, f, node, apf0, apf, config)
|
||||
flowFwdStore0(mid, tc, node, apf0, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
|
||||
private predicate storeCand(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
|
||||
Configuration config
|
||||
) {
|
||||
storeCand2(mid, f, node, config) and
|
||||
flowCand(mid, _, _, apf0, config)
|
||||
storeCand2(mid, tc, node, _, config) and
|
||||
flowCand(mid, _, _, apf0, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowFwdStore1(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
Configuration config
|
||||
) {
|
||||
flowFwdStore0(mid, f, node, apf0, config) and
|
||||
flowCandConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f) and
|
||||
storeCand(mid, tc, node, apf0, apf, config) and
|
||||
flowCandConsCand(tc, apf0, config) and
|
||||
flowCand(node, _, _, apf, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
|
||||
Configuration config
|
||||
private predicate flowFwdRead0(
|
||||
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
|
||||
boolean fromArg, AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCand(node, _, _, _, unbind(config))
|
||||
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
|
||||
readCandFwd(node1, tc, apf0, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
|
||||
flowCand(node, _, _, apf, unbind(config)) and
|
||||
flowCandConsCand(tc, apf, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdConsCand(
|
||||
Content f, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node n |
|
||||
flowFwd(n, _, _, apf, ap, config) and
|
||||
flowFwdStore1(n, f, _, apf, _, config)
|
||||
flowFwdStore0(n, tc, _, apf, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1863,9 +1885,9 @@ private predicate flow0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
flowStore(f, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(f, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowStore(tc, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(tc, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
@@ -1895,39 +1917,41 @@ private predicate flow0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
storeCand2(node1, f, node2, config) and
|
||||
flowFwdStore(node2, f, ap, _, _, _, config) and
|
||||
ap0 = push(f, ap)
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flowFwdStore(node2, tc, ap, _, _, _, config) and
|
||||
ap0 = push(tc, ap)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowStore(
|
||||
Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPath ap0 |
|
||||
storeFlowFwd(node, f, mid, ap, ap0, config) and
|
||||
storeFlowFwd(node, tc, mid, ap, ap0, config) and
|
||||
flow(mid, toReturn, returnAp, ap0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
readCand2(node1, f, node2, config) and
|
||||
flowFwdRead(node2, f, ap, _, _, config) and
|
||||
ap0 = pop(f, ap) and
|
||||
flowFwdConsCand(f, _, ap0, unbind(config))
|
||||
exists(AccessPathFrontHead apf |
|
||||
readCandFwd(node1, tc, apf, node2, config) and
|
||||
flowFwdRead(node2, apf, ap, _, _, _, config) and
|
||||
ap0 = pop(tc, ap) and
|
||||
flowFwdConsCand(tc, _, ap0, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
|
||||
private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
|
||||
exists(Node n, Node mid |
|
||||
flow(mid, _, _, ap, config) and
|
||||
readFlowFwd(n, f, mid, _, ap, config)
|
||||
readFlowFwd(n, tc, mid, _, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2270,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = TNil(getErasedNodeTypeBound(node))
|
||||
or
|
||||
exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
@@ -2284,30 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2) and
|
||||
private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
readCandFwd(node1, tc, _, node2, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
|
||||
private predicate pathReadStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
readCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2) and
|
||||
private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathStoreStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
@@ -2538,10 +2564,10 @@ private module FlowExploration {
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(Content f, int len) { len in [1 .. 5] }
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -2550,7 +2576,7 @@ private module FlowExploration {
|
||||
private class PartialAccessPath extends TPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TPartialCons(result, _) }
|
||||
TypedContent getHead() { this = TPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TPartialNil(_) and result = 0
|
||||
@@ -2561,7 +2587,7 @@ private module FlowExploration {
|
||||
DataFlowType getType() {
|
||||
this = TPartialNil(result)
|
||||
or
|
||||
exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
@@ -2579,15 +2605,15 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
|
||||
if len = 1
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc.toString() + "]"
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2763,11 +2789,12 @@ private module FlowExploration {
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(PartialAccessPath ap0, Content f |
|
||||
partialPathReadStep(mid, ap0, f, node, cc, config) and
|
||||
exists(PartialAccessPath ap0, TypedContent tc |
|
||||
partialPathReadStep(mid, ap0, tc, node, cc, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
apConsFwd(ap, tc, ap0, config) and
|
||||
compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
|
||||
@@ -2786,35 +2813,42 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
ap1 = mid.getAp() and
|
||||
store(mid.getNode(), f, node) and
|
||||
ap2.getHead() = f and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), f.getType())
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
store(midNode, tc, node, contentType) and
|
||||
ap2.getHead() = tc and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, f, _, ap2) and
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
ap = mid.getAp() and
|
||||
readStep(mid.getNode(), f, node) and
|
||||
ap.getHead() = f and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
read(midNode, tc.getContent(), node) and
|
||||
ap.getHead() = tc and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
|
||||
@@ -148,12 +148,6 @@ class Content extends TContent {
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
|
||||
}
|
||||
|
||||
/** Gets the type of the object containing this content. */
|
||||
abstract Type getContainerType();
|
||||
|
||||
/** Gets the type of this content. */
|
||||
abstract Type getType();
|
||||
}
|
||||
|
||||
private class FieldContent extends Content, TFieldContent {
|
||||
@@ -168,26 +162,14 @@ private class FieldContent extends Content, TFieldContent {
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
|
||||
}
|
||||
|
||||
override Type getContainerType() { result = f.getDeclaringType() }
|
||||
|
||||
override Type getType() { result = f.getType() }
|
||||
}
|
||||
|
||||
private class CollectionContent extends Content, TCollectionContent {
|
||||
override string toString() { result = "collection" }
|
||||
|
||||
override Type getContainerType() { none() }
|
||||
|
||||
override Type getType() { none() }
|
||||
}
|
||||
|
||||
private class ArrayContent extends Content, TArrayContent {
|
||||
override string toString() { result = "array" }
|
||||
|
||||
override Type getContainerType() { none() }
|
||||
|
||||
override Type getType() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
exists(Node mid |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
store(mid, _, node) and
|
||||
store(mid, _, node, _) and
|
||||
not outBarrier(mid, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(f, config) and
|
||||
exists(Content c |
|
||||
nodeCandFwd1Read(c, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(c, config) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
or
|
||||
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
read(mid, f, node)
|
||||
read(mid, c, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
|
||||
exists(Node mid, Node node, TypedContent tc |
|
||||
not fullBarrier(node, config) and
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, config) and
|
||||
store(mid, f, node)
|
||||
store(mid, tc, node, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, toReturn, config) and
|
||||
nodeCand1IsRead(f, config)
|
||||
exists(Content c |
|
||||
nodeCand1Store(c, node, toReturn, config) and
|
||||
nodeCand1IsRead(c, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
exists(Node mid, Content c |
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, toReturn, config)
|
||||
)
|
||||
or
|
||||
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1IsRead(Content f, Configuration config) {
|
||||
private predicate nodeCand1IsRead(Content c, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(node, unbind(config)) and
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
nodeCand1(mid, toReturn, config) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
store(node, tc, mid, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a read and a store in the flow covered
|
||||
* Holds if `c` is the target of both a read and a store in the flow covered
|
||||
* by `nodeCand1`.
|
||||
*/
|
||||
private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
|
||||
nodeCand1IsRead(f, conf) and
|
||||
nodeCand1Store(f, _, _, conf)
|
||||
private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
|
||||
nodeCand1IsRead(c, conf) and
|
||||
nodeCand1Store(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, f, n2)
|
||||
private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, tc, n2, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate read(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
private predicate read(Node n1, Content c, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
read(n1, f, n2)
|
||||
read(n1, c, n2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, _, config) and
|
||||
store(mid, f, node, config) and
|
||||
storeCand1(mid, _, node, config) and
|
||||
stored = true
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(f, stored, config)
|
||||
exists(Content c |
|
||||
nodeCandFwd2Read(c, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(c, stored, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
|
||||
private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCand1(node, unbind(config)) and
|
||||
nodeCandFwd2(mid, _, _, stored, config) and
|
||||
store(mid, f, node, config)
|
||||
storeCand1(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(
|
||||
Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, true, config) and
|
||||
read(mid, f, node, config)
|
||||
read(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -896,15 +901,15 @@ private predicate nodeCand2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(f, read, config)
|
||||
exists(Content c |
|
||||
nodeCand2Store(c, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(c, read, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f, boolean read0 |
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
|
||||
exists(Node mid, Content c, boolean read0 |
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
|
||||
nodeCand2(mid, toReturn, returnRead, read0, config) and
|
||||
read = true
|
||||
)
|
||||
@@ -930,51 +935,51 @@ private predicate nodeCand2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
|
||||
private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd2(node, _, _, true, unbind(config)) and
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
|
||||
nodeCand2(mid, _, _, read, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2Store(
|
||||
Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid, config) and
|
||||
storeCand1(node, c, mid, config) and
|
||||
nodeCand2(mid, toReturn, returnRead, true, config) and
|
||||
nodeCandFwd2(node, _, _, stored, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
|
||||
private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, _, stored, conf) and
|
||||
nodeCand2Store(c, node, _, _, stored, conf) and
|
||||
nodeCand2(node, _, _, stored, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
* Holds if `c` is the target of both a store and a read in the path graph
|
||||
* covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
|
||||
private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
|
||||
exists(boolean apNonEmpty |
|
||||
nodeCand2IsStored(f, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(f, apNonEmpty, conf)
|
||||
nodeCand2IsStored(c, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(c, apNonEmpty, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1058,7 +1063,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(_, node, config) or
|
||||
node instanceof ParameterNode or
|
||||
node instanceof OutNodeExt or
|
||||
store(_, _, node) or
|
||||
store(_, _, node, _) or
|
||||
read(_, _, node) or
|
||||
node instanceof CastNode
|
||||
)
|
||||
@@ -1074,7 +1079,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
store(node, _, next) or
|
||||
store(node, _, next, _) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
@@ -1154,19 +1159,21 @@ private module LocalFlowBigStep {
|
||||
private import LocalFlowBigStep
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2, config) and
|
||||
private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
|
||||
read(node1, c, node2, config) and
|
||||
nodeCand2(node1, _, _, true, unbind(config)) and
|
||||
nodeCand2(node2, config) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(c, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2, config) and
|
||||
private predicate storeCand2(
|
||||
Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(node1, tc, node2, contentType) and
|
||||
nodeCand2(node1, config) and
|
||||
nodeCand2(node2, _, _, true, unbind(config)) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1227,17 +1234,18 @@ private predicate flowCandFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
flowCandFwd(mid, fromArg, argApf, _, config) and
|
||||
storeCand2(mid, f, node, config) and
|
||||
exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
storeCand2(mid, tc, node, contentType, config) and
|
||||
nodeCand2(node, _, _, true, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
apf.headUsesContent(tc) and
|
||||
compatibleTypes(apf0.getType(), contentType)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(TypedContent tc |
|
||||
flowCandFwdRead(tc, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
|
||||
)
|
||||
or
|
||||
@@ -1261,24 +1269,30 @@ private predicate flowCandFwd0(
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n, DataFlowType contentType |
|
||||
flowCandFwd(mid, _, _, apf, config) and
|
||||
storeCand2(mid, f, n, config) and
|
||||
storeCand2(mid, tc, n, contentType, config) and
|
||||
nodeCand2(n, _, _, true, unbind(config)) and
|
||||
compatibleTypes(apf.getType(), f.getType())
|
||||
compatibleTypes(apf.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
private predicate flowCandFwdRead0(
|
||||
Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
|
||||
AccessPathFrontHead apf, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f)
|
||||
)
|
||||
flowCandFwd(node1, fromArg, argApf, apf, config) and
|
||||
readCand2(node1, c, node2, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1385,17 +1399,15 @@ private predicate flowCand0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPathFrontHead apf0 |
|
||||
flowCandStore(node, f, toReturn, returnApf, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandConsCand(f, apf, config)
|
||||
exists(TypedContent tc |
|
||||
flowCandStore(node, tc, apf, toReturn, returnApf, config) and
|
||||
flowCandConsCand(tc, apf, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
exists(TypedContent tc, AccessPathFront apf0 |
|
||||
flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(tc, apf0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1417,36 +1429,40 @@ private predicate flowCand0(
|
||||
else returnApf = TAccessPathFrontNone()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCandFwd(
|
||||
Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandRead(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
readCand2(node, f, mid, config) and
|
||||
readCandFwd(node, tc, apf, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
storeCand2(node, f, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
flowCandFwd(node, _, _, apf, config) and
|
||||
storeCand2(node, tc, mid, _, unbind(config)) and
|
||||
flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(Node n, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(n, _, _, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandRead(n, f, _, _, apf, config)
|
||||
)
|
||||
private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
flowCandRead(_, tc, _, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1499,13 +1515,13 @@ private predicate flowCandIsReturned(
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
|
||||
TConsCons(Content f1, Content f2, int len) {
|
||||
flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
|
||||
TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -1514,7 +1530,7 @@ private newtype TAccessPath =
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
abstract Content getHead();
|
||||
abstract TypedContent getHead();
|
||||
|
||||
abstract int len();
|
||||
|
||||
@@ -1525,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
/**
|
||||
* Holds if this access path has `head` at the front and may be followed by `tail`.
|
||||
*/
|
||||
abstract predicate pop(Content head, AccessPath tail);
|
||||
abstract predicate pop(TypedContent head, AccessPath tail);
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
@@ -1535,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override string toString() { result = concat(": " + ppReprType(t)) }
|
||||
|
||||
override Content getHead() { none() }
|
||||
override TypedContent getHead() { none() }
|
||||
|
||||
override int len() { result = 0 }
|
||||
|
||||
@@ -1543,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontNil(t) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { none() }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { none() }
|
||||
}
|
||||
|
||||
abstract private class AccessPathCons extends AccessPath { }
|
||||
|
||||
private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
private Content f;
|
||||
private TypedContent tc;
|
||||
private DataFlowType t;
|
||||
|
||||
AccessPathConsNil() { this = TConsNil(f, t) }
|
||||
AccessPathConsNil() { this = TConsNil(tc, t) }
|
||||
|
||||
override string toString() {
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
}
|
||||
|
||||
override Content getHead() { result = f }
|
||||
override TypedContent getHead() { result = tc }
|
||||
|
||||
override int len() { result = 1 }
|
||||
|
||||
override DataFlowType getType() { result = f.getContainerType() }
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
|
||||
}
|
||||
|
||||
private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
private Content f1;
|
||||
private Content f2;
|
||||
private TypedContent tc1;
|
||||
private TypedContent tc2;
|
||||
private int len;
|
||||
|
||||
AccessPathConsCons() { this = TConsCons(f1, f2, len) }
|
||||
AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
|
||||
|
||||
override string toString() {
|
||||
if len = 2
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
|
||||
else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
|
||||
}
|
||||
|
||||
override Content getHead() { result = f1 }
|
||||
override TypedContent getHead() { result = tc1 }
|
||||
|
||||
override int len() { result = len }
|
||||
|
||||
override DataFlowType getType() { result = f1.getContainerType() }
|
||||
override DataFlowType getType() { result = tc1.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f1) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc1) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) {
|
||||
head = f1 and
|
||||
override predicate pop(TypedContent head, AccessPath tail) {
|
||||
head = tc1 and
|
||||
(
|
||||
tail = TConsCons(f2, _, len - 1)
|
||||
tail = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
tail = TConsNil(f2, _)
|
||||
tail = TConsNil(tc2, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the access path obtained by popping `f` from `ap`, if any. */
|
||||
private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
|
||||
/** Gets the access path obtained by popping `tc` from `ap`, if any. */
|
||||
private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
|
||||
|
||||
/** Gets the access path obtained by pushing `f` onto `ap`. */
|
||||
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
|
||||
/** Gets the access path obtained by pushing `tc` onto `ap`. */
|
||||
private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
|
||||
|
||||
private newtype TAccessPathOption =
|
||||
TAccessPathNone() or
|
||||
@@ -1678,15 +1694,12 @@ private predicate flowFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPath ap0 |
|
||||
flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
|
||||
ap = push(f, ap0)
|
||||
)
|
||||
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
|
||||
flowFwdConsCand(f, apf, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
|
||||
flowFwdConsCand(tc, apf, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1710,54 +1723,63 @@ private predicate flowFwd0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore(
|
||||
Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFront apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
flowFwdStore1(mid, f, node, apf0, apf, config)
|
||||
flowFwdStore0(mid, tc, node, apf0, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
|
||||
private predicate storeCand(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
|
||||
Configuration config
|
||||
) {
|
||||
storeCand2(mid, f, node, config) and
|
||||
flowCand(mid, _, _, apf0, config)
|
||||
storeCand2(mid, tc, node, _, config) and
|
||||
flowCand(mid, _, _, apf0, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowFwdStore1(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
Configuration config
|
||||
) {
|
||||
flowFwdStore0(mid, f, node, apf0, config) and
|
||||
flowCandConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f) and
|
||||
storeCand(mid, tc, node, apf0, apf, config) and
|
||||
flowCandConsCand(tc, apf0, config) and
|
||||
flowCand(node, _, _, apf, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
|
||||
Configuration config
|
||||
private predicate flowFwdRead0(
|
||||
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
|
||||
boolean fromArg, AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCand(node, _, _, _, unbind(config))
|
||||
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
|
||||
readCandFwd(node1, tc, apf0, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
|
||||
flowCand(node, _, _, apf, unbind(config)) and
|
||||
flowCandConsCand(tc, apf, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdConsCand(
|
||||
Content f, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node n |
|
||||
flowFwd(n, _, _, apf, ap, config) and
|
||||
flowFwdStore1(n, f, _, apf, _, config)
|
||||
flowFwdStore0(n, tc, _, apf, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1863,9 +1885,9 @@ private predicate flow0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
flowStore(f, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(f, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowStore(tc, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(tc, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
@@ -1895,39 +1917,41 @@ private predicate flow0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
storeCand2(node1, f, node2, config) and
|
||||
flowFwdStore(node2, f, ap, _, _, _, config) and
|
||||
ap0 = push(f, ap)
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flowFwdStore(node2, tc, ap, _, _, _, config) and
|
||||
ap0 = push(tc, ap)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowStore(
|
||||
Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPath ap0 |
|
||||
storeFlowFwd(node, f, mid, ap, ap0, config) and
|
||||
storeFlowFwd(node, tc, mid, ap, ap0, config) and
|
||||
flow(mid, toReturn, returnAp, ap0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
readCand2(node1, f, node2, config) and
|
||||
flowFwdRead(node2, f, ap, _, _, config) and
|
||||
ap0 = pop(f, ap) and
|
||||
flowFwdConsCand(f, _, ap0, unbind(config))
|
||||
exists(AccessPathFrontHead apf |
|
||||
readCandFwd(node1, tc, apf, node2, config) and
|
||||
flowFwdRead(node2, apf, ap, _, _, _, config) and
|
||||
ap0 = pop(tc, ap) and
|
||||
flowFwdConsCand(tc, _, ap0, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
|
||||
private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
|
||||
exists(Node n, Node mid |
|
||||
flow(mid, _, _, ap, config) and
|
||||
readFlowFwd(n, f, mid, _, ap, config)
|
||||
readFlowFwd(n, tc, mid, _, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2270,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = TNil(getErasedNodeTypeBound(node))
|
||||
or
|
||||
exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
@@ -2284,30 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2) and
|
||||
private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
readCandFwd(node1, tc, _, node2, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
|
||||
private predicate pathReadStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
readCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2) and
|
||||
private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathStoreStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
@@ -2538,10 +2564,10 @@ private module FlowExploration {
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(Content f, int len) { len in [1 .. 5] }
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -2550,7 +2576,7 @@ private module FlowExploration {
|
||||
private class PartialAccessPath extends TPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TPartialCons(result, _) }
|
||||
TypedContent getHead() { this = TPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TPartialNil(_) and result = 0
|
||||
@@ -2561,7 +2587,7 @@ private module FlowExploration {
|
||||
DataFlowType getType() {
|
||||
this = TPartialNil(result)
|
||||
or
|
||||
exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
@@ -2579,15 +2605,15 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
|
||||
if len = 1
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc.toString() + "]"
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2763,11 +2789,12 @@ private module FlowExploration {
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(PartialAccessPath ap0, Content f |
|
||||
partialPathReadStep(mid, ap0, f, node, cc, config) and
|
||||
exists(PartialAccessPath ap0, TypedContent tc |
|
||||
partialPathReadStep(mid, ap0, tc, node, cc, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
apConsFwd(ap, tc, ap0, config) and
|
||||
compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
|
||||
@@ -2786,35 +2813,42 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
ap1 = mid.getAp() and
|
||||
store(mid.getNode(), f, node) and
|
||||
ap2.getHead() = f and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), f.getType())
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
store(midNode, tc, node, contentType) and
|
||||
ap2.getHead() = tc and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, f, _, ap2) and
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
ap = mid.getAp() and
|
||||
readStep(mid.getNode(), f, node) and
|
||||
ap.getHead() = f and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
read(midNode, tc.getContent(), node) and
|
||||
ap.getHead() = tc and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
|
||||
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
exists(Node mid |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
store(mid, _, node) and
|
||||
store(mid, _, node, _) and
|
||||
not outBarrier(mid, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(f, config) and
|
||||
exists(Content c |
|
||||
nodeCandFwd1Read(c, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(c, config) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
or
|
||||
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
read(mid, f, node)
|
||||
read(mid, c, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
|
||||
exists(Node mid, Node node, TypedContent tc |
|
||||
not fullBarrier(node, config) and
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, config) and
|
||||
store(mid, f, node)
|
||||
store(mid, tc, node, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, toReturn, config) and
|
||||
nodeCand1IsRead(f, config)
|
||||
exists(Content c |
|
||||
nodeCand1Store(c, node, toReturn, config) and
|
||||
nodeCand1IsRead(c, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
exists(Node mid, Content c |
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, toReturn, config)
|
||||
)
|
||||
or
|
||||
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1IsRead(Content f, Configuration config) {
|
||||
private predicate nodeCand1IsRead(Content c, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(node, unbind(config)) and
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
nodeCand1(mid, toReturn, config) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
store(node, tc, mid, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a read and a store in the flow covered
|
||||
* Holds if `c` is the target of both a read and a store in the flow covered
|
||||
* by `nodeCand1`.
|
||||
*/
|
||||
private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
|
||||
nodeCand1IsRead(f, conf) and
|
||||
nodeCand1Store(f, _, _, conf)
|
||||
private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
|
||||
nodeCand1IsRead(c, conf) and
|
||||
nodeCand1Store(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, f, n2)
|
||||
private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, tc, n2, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate read(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
private predicate read(Node n1, Content c, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
read(n1, f, n2)
|
||||
read(n1, c, n2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, _, config) and
|
||||
store(mid, f, node, config) and
|
||||
storeCand1(mid, _, node, config) and
|
||||
stored = true
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(f, stored, config)
|
||||
exists(Content c |
|
||||
nodeCandFwd2Read(c, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(c, stored, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
|
||||
private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCand1(node, unbind(config)) and
|
||||
nodeCandFwd2(mid, _, _, stored, config) and
|
||||
store(mid, f, node, config)
|
||||
storeCand1(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(
|
||||
Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, true, config) and
|
||||
read(mid, f, node, config)
|
||||
read(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -896,15 +901,15 @@ private predicate nodeCand2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(f, read, config)
|
||||
exists(Content c |
|
||||
nodeCand2Store(c, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(c, read, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f, boolean read0 |
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
|
||||
exists(Node mid, Content c, boolean read0 |
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
|
||||
nodeCand2(mid, toReturn, returnRead, read0, config) and
|
||||
read = true
|
||||
)
|
||||
@@ -930,51 +935,51 @@ private predicate nodeCand2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
|
||||
private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd2(node, _, _, true, unbind(config)) and
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
|
||||
nodeCand2(mid, _, _, read, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2Store(
|
||||
Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid, config) and
|
||||
storeCand1(node, c, mid, config) and
|
||||
nodeCand2(mid, toReturn, returnRead, true, config) and
|
||||
nodeCandFwd2(node, _, _, stored, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
|
||||
private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, _, stored, conf) and
|
||||
nodeCand2Store(c, node, _, _, stored, conf) and
|
||||
nodeCand2(node, _, _, stored, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
* Holds if `c` is the target of both a store and a read in the path graph
|
||||
* covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
|
||||
private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
|
||||
exists(boolean apNonEmpty |
|
||||
nodeCand2IsStored(f, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(f, apNonEmpty, conf)
|
||||
nodeCand2IsStored(c, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(c, apNonEmpty, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1058,7 +1063,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(_, node, config) or
|
||||
node instanceof ParameterNode or
|
||||
node instanceof OutNodeExt or
|
||||
store(_, _, node) or
|
||||
store(_, _, node, _) or
|
||||
read(_, _, node) or
|
||||
node instanceof CastNode
|
||||
)
|
||||
@@ -1074,7 +1079,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
store(node, _, next) or
|
||||
store(node, _, next, _) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
@@ -1154,19 +1159,21 @@ private module LocalFlowBigStep {
|
||||
private import LocalFlowBigStep
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2, config) and
|
||||
private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
|
||||
read(node1, c, node2, config) and
|
||||
nodeCand2(node1, _, _, true, unbind(config)) and
|
||||
nodeCand2(node2, config) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(c, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2, config) and
|
||||
private predicate storeCand2(
|
||||
Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(node1, tc, node2, contentType) and
|
||||
nodeCand2(node1, config) and
|
||||
nodeCand2(node2, _, _, true, unbind(config)) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1227,17 +1234,18 @@ private predicate flowCandFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
flowCandFwd(mid, fromArg, argApf, _, config) and
|
||||
storeCand2(mid, f, node, config) and
|
||||
exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
storeCand2(mid, tc, node, contentType, config) and
|
||||
nodeCand2(node, _, _, true, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
apf.headUsesContent(tc) and
|
||||
compatibleTypes(apf0.getType(), contentType)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(TypedContent tc |
|
||||
flowCandFwdRead(tc, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
|
||||
)
|
||||
or
|
||||
@@ -1261,24 +1269,30 @@ private predicate flowCandFwd0(
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n, DataFlowType contentType |
|
||||
flowCandFwd(mid, _, _, apf, config) and
|
||||
storeCand2(mid, f, n, config) and
|
||||
storeCand2(mid, tc, n, contentType, config) and
|
||||
nodeCand2(n, _, _, true, unbind(config)) and
|
||||
compatibleTypes(apf.getType(), f.getType())
|
||||
compatibleTypes(apf.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
private predicate flowCandFwdRead0(
|
||||
Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
|
||||
AccessPathFrontHead apf, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f)
|
||||
)
|
||||
flowCandFwd(node1, fromArg, argApf, apf, config) and
|
||||
readCand2(node1, c, node2, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1385,17 +1399,15 @@ private predicate flowCand0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPathFrontHead apf0 |
|
||||
flowCandStore(node, f, toReturn, returnApf, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandConsCand(f, apf, config)
|
||||
exists(TypedContent tc |
|
||||
flowCandStore(node, tc, apf, toReturn, returnApf, config) and
|
||||
flowCandConsCand(tc, apf, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
exists(TypedContent tc, AccessPathFront apf0 |
|
||||
flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(tc, apf0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1417,36 +1429,40 @@ private predicate flowCand0(
|
||||
else returnApf = TAccessPathFrontNone()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCandFwd(
|
||||
Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandRead(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
readCand2(node, f, mid, config) and
|
||||
readCandFwd(node, tc, apf, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
storeCand2(node, f, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
flowCandFwd(node, _, _, apf, config) and
|
||||
storeCand2(node, tc, mid, _, unbind(config)) and
|
||||
flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(Node n, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(n, _, _, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandRead(n, f, _, _, apf, config)
|
||||
)
|
||||
private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
flowCandRead(_, tc, _, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1499,13 +1515,13 @@ private predicate flowCandIsReturned(
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
|
||||
TConsCons(Content f1, Content f2, int len) {
|
||||
flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
|
||||
TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -1514,7 +1530,7 @@ private newtype TAccessPath =
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
abstract Content getHead();
|
||||
abstract TypedContent getHead();
|
||||
|
||||
abstract int len();
|
||||
|
||||
@@ -1525,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
/**
|
||||
* Holds if this access path has `head` at the front and may be followed by `tail`.
|
||||
*/
|
||||
abstract predicate pop(Content head, AccessPath tail);
|
||||
abstract predicate pop(TypedContent head, AccessPath tail);
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
@@ -1535,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override string toString() { result = concat(": " + ppReprType(t)) }
|
||||
|
||||
override Content getHead() { none() }
|
||||
override TypedContent getHead() { none() }
|
||||
|
||||
override int len() { result = 0 }
|
||||
|
||||
@@ -1543,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontNil(t) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { none() }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { none() }
|
||||
}
|
||||
|
||||
abstract private class AccessPathCons extends AccessPath { }
|
||||
|
||||
private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
private Content f;
|
||||
private TypedContent tc;
|
||||
private DataFlowType t;
|
||||
|
||||
AccessPathConsNil() { this = TConsNil(f, t) }
|
||||
AccessPathConsNil() { this = TConsNil(tc, t) }
|
||||
|
||||
override string toString() {
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
}
|
||||
|
||||
override Content getHead() { result = f }
|
||||
override TypedContent getHead() { result = tc }
|
||||
|
||||
override int len() { result = 1 }
|
||||
|
||||
override DataFlowType getType() { result = f.getContainerType() }
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
|
||||
}
|
||||
|
||||
private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
private Content f1;
|
||||
private Content f2;
|
||||
private TypedContent tc1;
|
||||
private TypedContent tc2;
|
||||
private int len;
|
||||
|
||||
AccessPathConsCons() { this = TConsCons(f1, f2, len) }
|
||||
AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
|
||||
|
||||
override string toString() {
|
||||
if len = 2
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
|
||||
else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
|
||||
}
|
||||
|
||||
override Content getHead() { result = f1 }
|
||||
override TypedContent getHead() { result = tc1 }
|
||||
|
||||
override int len() { result = len }
|
||||
|
||||
override DataFlowType getType() { result = f1.getContainerType() }
|
||||
override DataFlowType getType() { result = tc1.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f1) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc1) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) {
|
||||
head = f1 and
|
||||
override predicate pop(TypedContent head, AccessPath tail) {
|
||||
head = tc1 and
|
||||
(
|
||||
tail = TConsCons(f2, _, len - 1)
|
||||
tail = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
tail = TConsNil(f2, _)
|
||||
tail = TConsNil(tc2, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the access path obtained by popping `f` from `ap`, if any. */
|
||||
private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
|
||||
/** Gets the access path obtained by popping `tc` from `ap`, if any. */
|
||||
private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
|
||||
|
||||
/** Gets the access path obtained by pushing `f` onto `ap`. */
|
||||
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
|
||||
/** Gets the access path obtained by pushing `tc` onto `ap`. */
|
||||
private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
|
||||
|
||||
private newtype TAccessPathOption =
|
||||
TAccessPathNone() or
|
||||
@@ -1678,15 +1694,12 @@ private predicate flowFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPath ap0 |
|
||||
flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
|
||||
ap = push(f, ap0)
|
||||
)
|
||||
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
|
||||
flowFwdConsCand(f, apf, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
|
||||
flowFwdConsCand(tc, apf, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1710,54 +1723,63 @@ private predicate flowFwd0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore(
|
||||
Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFront apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
flowFwdStore1(mid, f, node, apf0, apf, config)
|
||||
flowFwdStore0(mid, tc, node, apf0, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
|
||||
private predicate storeCand(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
|
||||
Configuration config
|
||||
) {
|
||||
storeCand2(mid, f, node, config) and
|
||||
flowCand(mid, _, _, apf0, config)
|
||||
storeCand2(mid, tc, node, _, config) and
|
||||
flowCand(mid, _, _, apf0, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowFwdStore1(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
Configuration config
|
||||
) {
|
||||
flowFwdStore0(mid, f, node, apf0, config) and
|
||||
flowCandConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f) and
|
||||
storeCand(mid, tc, node, apf0, apf, config) and
|
||||
flowCandConsCand(tc, apf0, config) and
|
||||
flowCand(node, _, _, apf, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
|
||||
Configuration config
|
||||
private predicate flowFwdRead0(
|
||||
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
|
||||
boolean fromArg, AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCand(node, _, _, _, unbind(config))
|
||||
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
|
||||
readCandFwd(node1, tc, apf0, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
|
||||
flowCand(node, _, _, apf, unbind(config)) and
|
||||
flowCandConsCand(tc, apf, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdConsCand(
|
||||
Content f, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node n |
|
||||
flowFwd(n, _, _, apf, ap, config) and
|
||||
flowFwdStore1(n, f, _, apf, _, config)
|
||||
flowFwdStore0(n, tc, _, apf, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1863,9 +1885,9 @@ private predicate flow0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
flowStore(f, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(f, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowStore(tc, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(tc, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
@@ -1895,39 +1917,41 @@ private predicate flow0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
storeCand2(node1, f, node2, config) and
|
||||
flowFwdStore(node2, f, ap, _, _, _, config) and
|
||||
ap0 = push(f, ap)
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flowFwdStore(node2, tc, ap, _, _, _, config) and
|
||||
ap0 = push(tc, ap)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowStore(
|
||||
Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPath ap0 |
|
||||
storeFlowFwd(node, f, mid, ap, ap0, config) and
|
||||
storeFlowFwd(node, tc, mid, ap, ap0, config) and
|
||||
flow(mid, toReturn, returnAp, ap0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
readCand2(node1, f, node2, config) and
|
||||
flowFwdRead(node2, f, ap, _, _, config) and
|
||||
ap0 = pop(f, ap) and
|
||||
flowFwdConsCand(f, _, ap0, unbind(config))
|
||||
exists(AccessPathFrontHead apf |
|
||||
readCandFwd(node1, tc, apf, node2, config) and
|
||||
flowFwdRead(node2, apf, ap, _, _, _, config) and
|
||||
ap0 = pop(tc, ap) and
|
||||
flowFwdConsCand(tc, _, ap0, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
|
||||
private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
|
||||
exists(Node n, Node mid |
|
||||
flow(mid, _, _, ap, config) and
|
||||
readFlowFwd(n, f, mid, _, ap, config)
|
||||
readFlowFwd(n, tc, mid, _, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2270,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = TNil(getErasedNodeTypeBound(node))
|
||||
or
|
||||
exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
@@ -2284,30 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2) and
|
||||
private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
readCandFwd(node1, tc, _, node2, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
|
||||
private predicate pathReadStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
readCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2) and
|
||||
private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathStoreStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
@@ -2538,10 +2564,10 @@ private module FlowExploration {
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(Content f, int len) { len in [1 .. 5] }
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -2550,7 +2576,7 @@ private module FlowExploration {
|
||||
private class PartialAccessPath extends TPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TPartialCons(result, _) }
|
||||
TypedContent getHead() { this = TPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TPartialNil(_) and result = 0
|
||||
@@ -2561,7 +2587,7 @@ private module FlowExploration {
|
||||
DataFlowType getType() {
|
||||
this = TPartialNil(result)
|
||||
or
|
||||
exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
@@ -2579,15 +2605,15 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
|
||||
if len = 1
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc.toString() + "]"
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2763,11 +2789,12 @@ private module FlowExploration {
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(PartialAccessPath ap0, Content f |
|
||||
partialPathReadStep(mid, ap0, f, node, cc, config) and
|
||||
exists(PartialAccessPath ap0, TypedContent tc |
|
||||
partialPathReadStep(mid, ap0, tc, node, cc, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
apConsFwd(ap, tc, ap0, config) and
|
||||
compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
|
||||
@@ -2786,35 +2813,42 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
ap1 = mid.getAp() and
|
||||
store(mid.getNode(), f, node) and
|
||||
ap2.getHead() = f and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), f.getType())
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
store(midNode, tc, node, contentType) and
|
||||
ap2.getHead() = tc and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, f, _, ap2) and
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
ap = mid.getAp() and
|
||||
readStep(mid.getNode(), f, node) and
|
||||
ap.getHead() = f and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
read(midNode, tc.getContent(), node) and
|
||||
ap.getHead() = tc and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
|
||||
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
exists(Node mid |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
store(mid, _, node) and
|
||||
store(mid, _, node, _) and
|
||||
not outBarrier(mid, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(f, config) and
|
||||
exists(Content c |
|
||||
nodeCandFwd1Read(c, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(c, config) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
or
|
||||
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
read(mid, f, node)
|
||||
read(mid, c, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
|
||||
exists(Node mid, Node node, TypedContent tc |
|
||||
not fullBarrier(node, config) and
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, config) and
|
||||
store(mid, f, node)
|
||||
store(mid, tc, node, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, toReturn, config) and
|
||||
nodeCand1IsRead(f, config)
|
||||
exists(Content c |
|
||||
nodeCand1Store(c, node, toReturn, config) and
|
||||
nodeCand1IsRead(c, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
exists(Node mid, Content c |
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, toReturn, config)
|
||||
)
|
||||
or
|
||||
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1IsRead(Content f, Configuration config) {
|
||||
private predicate nodeCand1IsRead(Content c, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(node, unbind(config)) and
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
nodeCand1(mid, toReturn, config) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
store(node, tc, mid, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a read and a store in the flow covered
|
||||
* Holds if `c` is the target of both a read and a store in the flow covered
|
||||
* by `nodeCand1`.
|
||||
*/
|
||||
private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
|
||||
nodeCand1IsRead(f, conf) and
|
||||
nodeCand1Store(f, _, _, conf)
|
||||
private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
|
||||
nodeCand1IsRead(c, conf) and
|
||||
nodeCand1Store(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, f, n2)
|
||||
private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, tc, n2, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate read(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
private predicate read(Node n1, Content c, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
read(n1, f, n2)
|
||||
read(n1, c, n2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, _, config) and
|
||||
store(mid, f, node, config) and
|
||||
storeCand1(mid, _, node, config) and
|
||||
stored = true
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(f, stored, config)
|
||||
exists(Content c |
|
||||
nodeCandFwd2Read(c, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(c, stored, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
|
||||
private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCand1(node, unbind(config)) and
|
||||
nodeCandFwd2(mid, _, _, stored, config) and
|
||||
store(mid, f, node, config)
|
||||
storeCand1(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(
|
||||
Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, true, config) and
|
||||
read(mid, f, node, config)
|
||||
read(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -896,15 +901,15 @@ private predicate nodeCand2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(f, read, config)
|
||||
exists(Content c |
|
||||
nodeCand2Store(c, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(c, read, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f, boolean read0 |
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
|
||||
exists(Node mid, Content c, boolean read0 |
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
|
||||
nodeCand2(mid, toReturn, returnRead, read0, config) and
|
||||
read = true
|
||||
)
|
||||
@@ -930,51 +935,51 @@ private predicate nodeCand2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
|
||||
private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd2(node, _, _, true, unbind(config)) and
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
|
||||
nodeCand2(mid, _, _, read, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2Store(
|
||||
Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid, config) and
|
||||
storeCand1(node, c, mid, config) and
|
||||
nodeCand2(mid, toReturn, returnRead, true, config) and
|
||||
nodeCandFwd2(node, _, _, stored, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
|
||||
private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, _, stored, conf) and
|
||||
nodeCand2Store(c, node, _, _, stored, conf) and
|
||||
nodeCand2(node, _, _, stored, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
* Holds if `c` is the target of both a store and a read in the path graph
|
||||
* covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
|
||||
private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
|
||||
exists(boolean apNonEmpty |
|
||||
nodeCand2IsStored(f, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(f, apNonEmpty, conf)
|
||||
nodeCand2IsStored(c, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(c, apNonEmpty, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1058,7 +1063,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(_, node, config) or
|
||||
node instanceof ParameterNode or
|
||||
node instanceof OutNodeExt or
|
||||
store(_, _, node) or
|
||||
store(_, _, node, _) or
|
||||
read(_, _, node) or
|
||||
node instanceof CastNode
|
||||
)
|
||||
@@ -1074,7 +1079,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
store(node, _, next) or
|
||||
store(node, _, next, _) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
@@ -1154,19 +1159,21 @@ private module LocalFlowBigStep {
|
||||
private import LocalFlowBigStep
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2, config) and
|
||||
private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
|
||||
read(node1, c, node2, config) and
|
||||
nodeCand2(node1, _, _, true, unbind(config)) and
|
||||
nodeCand2(node2, config) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(c, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2, config) and
|
||||
private predicate storeCand2(
|
||||
Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(node1, tc, node2, contentType) and
|
||||
nodeCand2(node1, config) and
|
||||
nodeCand2(node2, _, _, true, unbind(config)) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1227,17 +1234,18 @@ private predicate flowCandFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
flowCandFwd(mid, fromArg, argApf, _, config) and
|
||||
storeCand2(mid, f, node, config) and
|
||||
exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
storeCand2(mid, tc, node, contentType, config) and
|
||||
nodeCand2(node, _, _, true, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
apf.headUsesContent(tc) and
|
||||
compatibleTypes(apf0.getType(), contentType)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(TypedContent tc |
|
||||
flowCandFwdRead(tc, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
|
||||
)
|
||||
or
|
||||
@@ -1261,24 +1269,30 @@ private predicate flowCandFwd0(
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n, DataFlowType contentType |
|
||||
flowCandFwd(mid, _, _, apf, config) and
|
||||
storeCand2(mid, f, n, config) and
|
||||
storeCand2(mid, tc, n, contentType, config) and
|
||||
nodeCand2(n, _, _, true, unbind(config)) and
|
||||
compatibleTypes(apf.getType(), f.getType())
|
||||
compatibleTypes(apf.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
private predicate flowCandFwdRead0(
|
||||
Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
|
||||
AccessPathFrontHead apf, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f)
|
||||
)
|
||||
flowCandFwd(node1, fromArg, argApf, apf, config) and
|
||||
readCand2(node1, c, node2, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1385,17 +1399,15 @@ private predicate flowCand0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPathFrontHead apf0 |
|
||||
flowCandStore(node, f, toReturn, returnApf, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandConsCand(f, apf, config)
|
||||
exists(TypedContent tc |
|
||||
flowCandStore(node, tc, apf, toReturn, returnApf, config) and
|
||||
flowCandConsCand(tc, apf, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
exists(TypedContent tc, AccessPathFront apf0 |
|
||||
flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(tc, apf0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1417,36 +1429,40 @@ private predicate flowCand0(
|
||||
else returnApf = TAccessPathFrontNone()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCandFwd(
|
||||
Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandRead(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
readCand2(node, f, mid, config) and
|
||||
readCandFwd(node, tc, apf, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
storeCand2(node, f, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
flowCandFwd(node, _, _, apf, config) and
|
||||
storeCand2(node, tc, mid, _, unbind(config)) and
|
||||
flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(Node n, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(n, _, _, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandRead(n, f, _, _, apf, config)
|
||||
)
|
||||
private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
flowCandRead(_, tc, _, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1499,13 +1515,13 @@ private predicate flowCandIsReturned(
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
|
||||
TConsCons(Content f1, Content f2, int len) {
|
||||
flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
|
||||
TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -1514,7 +1530,7 @@ private newtype TAccessPath =
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
abstract Content getHead();
|
||||
abstract TypedContent getHead();
|
||||
|
||||
abstract int len();
|
||||
|
||||
@@ -1525,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
/**
|
||||
* Holds if this access path has `head` at the front and may be followed by `tail`.
|
||||
*/
|
||||
abstract predicate pop(Content head, AccessPath tail);
|
||||
abstract predicate pop(TypedContent head, AccessPath tail);
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
@@ -1535,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override string toString() { result = concat(": " + ppReprType(t)) }
|
||||
|
||||
override Content getHead() { none() }
|
||||
override TypedContent getHead() { none() }
|
||||
|
||||
override int len() { result = 0 }
|
||||
|
||||
@@ -1543,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontNil(t) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { none() }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { none() }
|
||||
}
|
||||
|
||||
abstract private class AccessPathCons extends AccessPath { }
|
||||
|
||||
private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
private Content f;
|
||||
private TypedContent tc;
|
||||
private DataFlowType t;
|
||||
|
||||
AccessPathConsNil() { this = TConsNil(f, t) }
|
||||
AccessPathConsNil() { this = TConsNil(tc, t) }
|
||||
|
||||
override string toString() {
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
}
|
||||
|
||||
override Content getHead() { result = f }
|
||||
override TypedContent getHead() { result = tc }
|
||||
|
||||
override int len() { result = 1 }
|
||||
|
||||
override DataFlowType getType() { result = f.getContainerType() }
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
|
||||
}
|
||||
|
||||
private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
private Content f1;
|
||||
private Content f2;
|
||||
private TypedContent tc1;
|
||||
private TypedContent tc2;
|
||||
private int len;
|
||||
|
||||
AccessPathConsCons() { this = TConsCons(f1, f2, len) }
|
||||
AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
|
||||
|
||||
override string toString() {
|
||||
if len = 2
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
|
||||
else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
|
||||
}
|
||||
|
||||
override Content getHead() { result = f1 }
|
||||
override TypedContent getHead() { result = tc1 }
|
||||
|
||||
override int len() { result = len }
|
||||
|
||||
override DataFlowType getType() { result = f1.getContainerType() }
|
||||
override DataFlowType getType() { result = tc1.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f1) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc1) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) {
|
||||
head = f1 and
|
||||
override predicate pop(TypedContent head, AccessPath tail) {
|
||||
head = tc1 and
|
||||
(
|
||||
tail = TConsCons(f2, _, len - 1)
|
||||
tail = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
tail = TConsNil(f2, _)
|
||||
tail = TConsNil(tc2, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the access path obtained by popping `f` from `ap`, if any. */
|
||||
private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
|
||||
/** Gets the access path obtained by popping `tc` from `ap`, if any. */
|
||||
private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
|
||||
|
||||
/** Gets the access path obtained by pushing `f` onto `ap`. */
|
||||
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
|
||||
/** Gets the access path obtained by pushing `tc` onto `ap`. */
|
||||
private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
|
||||
|
||||
private newtype TAccessPathOption =
|
||||
TAccessPathNone() or
|
||||
@@ -1678,15 +1694,12 @@ private predicate flowFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPath ap0 |
|
||||
flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
|
||||
ap = push(f, ap0)
|
||||
)
|
||||
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
|
||||
flowFwdConsCand(f, apf, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
|
||||
flowFwdConsCand(tc, apf, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1710,54 +1723,63 @@ private predicate flowFwd0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore(
|
||||
Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFront apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
flowFwdStore1(mid, f, node, apf0, apf, config)
|
||||
flowFwdStore0(mid, tc, node, apf0, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
|
||||
private predicate storeCand(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
|
||||
Configuration config
|
||||
) {
|
||||
storeCand2(mid, f, node, config) and
|
||||
flowCand(mid, _, _, apf0, config)
|
||||
storeCand2(mid, tc, node, _, config) and
|
||||
flowCand(mid, _, _, apf0, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowFwdStore1(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
Configuration config
|
||||
) {
|
||||
flowFwdStore0(mid, f, node, apf0, config) and
|
||||
flowCandConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f) and
|
||||
storeCand(mid, tc, node, apf0, apf, config) and
|
||||
flowCandConsCand(tc, apf0, config) and
|
||||
flowCand(node, _, _, apf, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
|
||||
Configuration config
|
||||
private predicate flowFwdRead0(
|
||||
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
|
||||
boolean fromArg, AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCand(node, _, _, _, unbind(config))
|
||||
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
|
||||
readCandFwd(node1, tc, apf0, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
|
||||
flowCand(node, _, _, apf, unbind(config)) and
|
||||
flowCandConsCand(tc, apf, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdConsCand(
|
||||
Content f, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node n |
|
||||
flowFwd(n, _, _, apf, ap, config) and
|
||||
flowFwdStore1(n, f, _, apf, _, config)
|
||||
flowFwdStore0(n, tc, _, apf, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1863,9 +1885,9 @@ private predicate flow0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
flowStore(f, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(f, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowStore(tc, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(tc, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
@@ -1895,39 +1917,41 @@ private predicate flow0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
storeCand2(node1, f, node2, config) and
|
||||
flowFwdStore(node2, f, ap, _, _, _, config) and
|
||||
ap0 = push(f, ap)
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flowFwdStore(node2, tc, ap, _, _, _, config) and
|
||||
ap0 = push(tc, ap)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowStore(
|
||||
Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPath ap0 |
|
||||
storeFlowFwd(node, f, mid, ap, ap0, config) and
|
||||
storeFlowFwd(node, tc, mid, ap, ap0, config) and
|
||||
flow(mid, toReturn, returnAp, ap0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
readCand2(node1, f, node2, config) and
|
||||
flowFwdRead(node2, f, ap, _, _, config) and
|
||||
ap0 = pop(f, ap) and
|
||||
flowFwdConsCand(f, _, ap0, unbind(config))
|
||||
exists(AccessPathFrontHead apf |
|
||||
readCandFwd(node1, tc, apf, node2, config) and
|
||||
flowFwdRead(node2, apf, ap, _, _, _, config) and
|
||||
ap0 = pop(tc, ap) and
|
||||
flowFwdConsCand(tc, _, ap0, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
|
||||
private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
|
||||
exists(Node n, Node mid |
|
||||
flow(mid, _, _, ap, config) and
|
||||
readFlowFwd(n, f, mid, _, ap, config)
|
||||
readFlowFwd(n, tc, mid, _, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2270,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = TNil(getErasedNodeTypeBound(node))
|
||||
or
|
||||
exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
@@ -2284,30 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2) and
|
||||
private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
readCandFwd(node1, tc, _, node2, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
|
||||
private predicate pathReadStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
readCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2) and
|
||||
private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathStoreStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
@@ -2538,10 +2564,10 @@ private module FlowExploration {
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(Content f, int len) { len in [1 .. 5] }
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -2550,7 +2576,7 @@ private module FlowExploration {
|
||||
private class PartialAccessPath extends TPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TPartialCons(result, _) }
|
||||
TypedContent getHead() { this = TPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TPartialNil(_) and result = 0
|
||||
@@ -2561,7 +2587,7 @@ private module FlowExploration {
|
||||
DataFlowType getType() {
|
||||
this = TPartialNil(result)
|
||||
or
|
||||
exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
@@ -2579,15 +2605,15 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
|
||||
if len = 1
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc.toString() + "]"
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2763,11 +2789,12 @@ private module FlowExploration {
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(PartialAccessPath ap0, Content f |
|
||||
partialPathReadStep(mid, ap0, f, node, cc, config) and
|
||||
exists(PartialAccessPath ap0, TypedContent tc |
|
||||
partialPathReadStep(mid, ap0, tc, node, cc, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
apConsFwd(ap, tc, ap0, config) and
|
||||
compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
|
||||
@@ -2786,35 +2813,42 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
ap1 = mid.getAp() and
|
||||
store(mid.getNode(), f, node) and
|
||||
ap2.getHead() = f and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), f.getType())
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
store(midNode, tc, node, contentType) and
|
||||
ap2.getHead() = tc and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, f, _, ap2) and
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
ap = mid.getAp() and
|
||||
readStep(mid.getNode(), f, node) and
|
||||
ap.getHead() = f and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
read(midNode, tc.getContent(), node) and
|
||||
ap.getHead() = tc and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
|
||||
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
exists(Node mid |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
store(mid, _, node) and
|
||||
store(mid, _, node, _) and
|
||||
not outBarrier(mid, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd1Read(f, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(f, config) and
|
||||
exists(Content c |
|
||||
nodeCandFwd1Read(c, node, fromArg, config) and
|
||||
nodeCandFwd1IsStored(c, config) and
|
||||
not inBarrier(node, config)
|
||||
)
|
||||
or
|
||||
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
|
||||
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
|
||||
private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd1(mid, fromArg, config) and
|
||||
read(mid, f, node)
|
||||
read(mid, c, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
|
||||
exists(Node mid, Node node, TypedContent tc |
|
||||
not fullBarrier(node, config) and
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(mid, config) and
|
||||
store(mid, f, node)
|
||||
store(mid, tc, node, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand1Store(f, node, toReturn, config) and
|
||||
nodeCand1IsRead(f, config)
|
||||
exists(Content c |
|
||||
nodeCand1Store(c, node, toReturn, config) and
|
||||
nodeCand1IsRead(c, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
exists(Node mid, Content c |
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, toReturn, config)
|
||||
)
|
||||
or
|
||||
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1IsRead(Content f, Configuration config) {
|
||||
private predicate nodeCand1IsRead(Content c, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd1(node, unbind(config)) and
|
||||
read(node, f, mid) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
read(node, c, mid) and
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
nodeCand1(mid, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid |
|
||||
private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
nodeCand1(mid, toReturn, config) and
|
||||
nodeCandFwd1IsStored(f, unbind(config)) and
|
||||
store(node, f, mid)
|
||||
nodeCandFwd1IsStored(c, unbind(config)) and
|
||||
store(node, tc, mid, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a read and a store in the flow covered
|
||||
* Holds if `c` is the target of both a read and a store in the flow covered
|
||||
* by `nodeCand1`.
|
||||
*/
|
||||
private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
|
||||
nodeCand1IsRead(f, conf) and
|
||||
nodeCand1Store(f, _, _, conf)
|
||||
private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
|
||||
nodeCand1IsRead(c, conf) and
|
||||
nodeCand1Store(c, _, _, conf)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate store(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, f, n2)
|
||||
private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
store(n1, tc, n2, _) and
|
||||
c = tc.getContent()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate read(Node n1, Content f, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(f, config) and
|
||||
private predicate read(Node n1, Content c, Node n2, Configuration config) {
|
||||
nodeCand1IsReadAndStored(c, config) and
|
||||
nodeCand1(n2, unbind(config)) and
|
||||
read(n1, f, n2)
|
||||
read(n1, c, n2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, _, config) and
|
||||
store(mid, f, node, config) and
|
||||
storeCand1(mid, _, node, config) and
|
||||
stored = true
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
nodeCandFwd2Read(f, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(f, stored, config)
|
||||
exists(Content c |
|
||||
nodeCandFwd2Read(c, node, fromArg, argStored, config) and
|
||||
nodeCandFwd2IsStored(c, stored, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
|
||||
private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCand1(node, unbind(config)) and
|
||||
nodeCandFwd2(mid, _, _, stored, config) and
|
||||
store(mid, f, node, config)
|
||||
storeCand1(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd2Read(
|
||||
Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
nodeCandFwd2(mid, fromArg, argStored, true, config) and
|
||||
read(mid, f, node, config)
|
||||
read(mid, c, node, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -896,15 +901,15 @@ private predicate nodeCand2(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
nodeCand2Store(f, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(f, read, config)
|
||||
exists(Content c |
|
||||
nodeCand2Store(c, node, toReturn, returnRead, read, config) and
|
||||
nodeCand2IsRead(c, read, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f, boolean read0 |
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
|
||||
exists(Node mid, Content c, boolean read0 |
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
|
||||
nodeCand2(mid, toReturn, returnRead, read0, config) and
|
||||
read = true
|
||||
)
|
||||
@@ -930,51 +935,51 @@ private predicate nodeCand2(
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
|
||||
private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
|
||||
exists(Node mid, Node node |
|
||||
useFieldFlow(config) and
|
||||
nodeCandFwd2(node, _, _, true, unbind(config)) and
|
||||
read(node, f, mid, config) and
|
||||
nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
|
||||
read(node, c, mid, config) and
|
||||
nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
|
||||
nodeCand2(mid, _, _, read, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2Store(
|
||||
Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
store(node, f, mid, config) and
|
||||
storeCand1(node, c, mid, config) and
|
||||
nodeCand2(mid, toReturn, returnRead, true, config) and
|
||||
nodeCandFwd2(node, _, _, stored, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
|
||||
* Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
|
||||
private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
|
||||
exists(Node node |
|
||||
nodeCand2Store(f, node, _, _, stored, conf) and
|
||||
nodeCand2Store(c, node, _, _, stored, conf) and
|
||||
nodeCand2(node, _, _, stored, conf)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of both a store and a read in the path graph
|
||||
* Holds if `c` is the target of both a store and a read in the path graph
|
||||
* covered by `nodeCand2`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
|
||||
private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
|
||||
exists(boolean apNonEmpty |
|
||||
nodeCand2IsStored(f, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(f, apNonEmpty, conf)
|
||||
nodeCand2IsStored(c, apNonEmpty, conf) and
|
||||
nodeCand2IsRead(c, apNonEmpty, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1058,7 +1063,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(_, node, config) or
|
||||
node instanceof ParameterNode or
|
||||
node instanceof OutNodeExt or
|
||||
store(_, _, node) or
|
||||
store(_, _, node, _) or
|
||||
read(_, _, node) or
|
||||
node instanceof CastNode
|
||||
)
|
||||
@@ -1074,7 +1079,7 @@ private module LocalFlowBigStep {
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
store(node, _, next) or
|
||||
store(node, _, next, _) or
|
||||
read(node, _, next)
|
||||
)
|
||||
or
|
||||
@@ -1154,19 +1159,21 @@ private module LocalFlowBigStep {
|
||||
private import LocalFlowBigStep
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2, config) and
|
||||
private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
|
||||
read(node1, c, node2, config) and
|
||||
nodeCand2(node1, _, _, true, unbind(config)) and
|
||||
nodeCand2(node2, config) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(c, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2, config) and
|
||||
private predicate storeCand2(
|
||||
Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
|
||||
) {
|
||||
store(node1, tc, node2, contentType) and
|
||||
nodeCand2(node1, config) and
|
||||
nodeCand2(node2, _, _, true, unbind(config)) and
|
||||
nodeCand2IsReadAndStored(f, unbind(config))
|
||||
nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1227,17 +1234,18 @@ private predicate flowCandFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Node mid, Content f |
|
||||
flowCandFwd(mid, fromArg, argApf, _, config) and
|
||||
storeCand2(mid, f, node, config) and
|
||||
exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
storeCand2(mid, tc, node, contentType, config) and
|
||||
nodeCand2(node, _, _, true, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
apf.headUsesContent(tc) and
|
||||
compatibleTypes(apf0.getType(), contentType)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowCandFwdRead(f, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(TypedContent tc |
|
||||
flowCandFwdRead(tc, node, fromArg, argApf, config) and
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
|
||||
)
|
||||
or
|
||||
@@ -1261,24 +1269,30 @@ private predicate flowCandFwd0(
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n |
|
||||
private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
exists(Node mid, Node n, DataFlowType contentType |
|
||||
flowCandFwd(mid, _, _, apf, config) and
|
||||
storeCand2(mid, f, n, config) and
|
||||
storeCand2(mid, tc, n, contentType, config) and
|
||||
nodeCand2(n, _, _, true, unbind(config)) and
|
||||
compatibleTypes(apf.getType(), f.getType())
|
||||
compatibleTypes(apf.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
private predicate flowCandFwdRead0(
|
||||
Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
|
||||
AccessPathFrontHead apf, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(mid, fromArg, argApf, apf0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f)
|
||||
)
|
||||
flowCandFwd(node1, fromArg, argApf, apf, config) and
|
||||
readCand2(node1, c, node2, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandFwdRead(
|
||||
TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1385,17 +1399,15 @@ private predicate flowCand0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPathFrontHead apf0 |
|
||||
flowCandStore(node, f, toReturn, returnApf, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandConsCand(f, apf, config)
|
||||
exists(TypedContent tc |
|
||||
flowCandStore(node, tc, apf, toReturn, returnApf, config) and
|
||||
flowCandConsCand(tc, apf, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f)
|
||||
exists(TypedContent tc, AccessPathFront apf0 |
|
||||
flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
|
||||
flowCandFwdConsCand(tc, apf0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1417,36 +1429,40 @@ private predicate flowCand0(
|
||||
else returnApf = TAccessPathFrontNone()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCandFwd(
|
||||
Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
|
||||
) {
|
||||
flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandRead(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
readCand2(node, f, mid, config) and
|
||||
readCandFwd(node, tc, apf, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandStore(
|
||||
Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
|
||||
Configuration config
|
||||
Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
|
||||
AccessPathFrontOption returnApf, Configuration config
|
||||
) {
|
||||
exists(Node mid |
|
||||
storeCand2(node, f, mid, config) and
|
||||
flowCand(mid, toReturn, returnApf, apf0, config)
|
||||
flowCandFwd(node, _, _, apf, config) and
|
||||
storeCand2(node, tc, mid, _, unbind(config)) and
|
||||
flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(f, apf, config) and
|
||||
exists(Node n, AccessPathFrontHead apf0 |
|
||||
flowCandFwd(n, _, _, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCandRead(n, f, _, _, apf, config)
|
||||
)
|
||||
private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
|
||||
flowCandFwdConsCand(tc, apf, config) and
|
||||
flowCandRead(_, tc, _, _, _, apf, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1499,13 +1515,13 @@ private predicate flowCandIsReturned(
|
||||
|
||||
private newtype TAccessPath =
|
||||
TNil(DataFlowType t) or
|
||||
TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
|
||||
TConsCons(Content f1, Content f2, int len) {
|
||||
flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
|
||||
TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
|
||||
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
|
||||
flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
|
||||
}
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
|
||||
* elements of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -1514,7 +1530,7 @@ private newtype TAccessPath =
|
||||
abstract private class AccessPath extends TAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
abstract Content getHead();
|
||||
abstract TypedContent getHead();
|
||||
|
||||
abstract int len();
|
||||
|
||||
@@ -1525,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
/**
|
||||
* Holds if this access path has `head` at the front and may be followed by `tail`.
|
||||
*/
|
||||
abstract predicate pop(Content head, AccessPath tail);
|
||||
abstract predicate pop(TypedContent head, AccessPath tail);
|
||||
}
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
@@ -1535,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override string toString() { result = concat(": " + ppReprType(t)) }
|
||||
|
||||
override Content getHead() { none() }
|
||||
override TypedContent getHead() { none() }
|
||||
|
||||
override int len() { result = 0 }
|
||||
|
||||
@@ -1543,70 +1559,70 @@ private class AccessPathNil extends AccessPath, TNil {
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontNil(t) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { none() }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { none() }
|
||||
}
|
||||
|
||||
abstract private class AccessPathCons extends AccessPath { }
|
||||
|
||||
private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
private Content f;
|
||||
private TypedContent tc;
|
||||
private DataFlowType t;
|
||||
|
||||
AccessPathConsNil() { this = TConsNil(f, t) }
|
||||
AccessPathConsNil() { this = TConsNil(tc, t) }
|
||||
|
||||
override string toString() {
|
||||
// The `concat` becomes "" if `ppReprType` has no result.
|
||||
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
|
||||
}
|
||||
|
||||
override Content getHead() { result = f }
|
||||
override TypedContent getHead() { result = tc }
|
||||
|
||||
override int len() { result = 1 }
|
||||
|
||||
override DataFlowType getType() { result = f.getContainerType() }
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
|
||||
override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
|
||||
}
|
||||
|
||||
private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
||||
private Content f1;
|
||||
private Content f2;
|
||||
private TypedContent tc1;
|
||||
private TypedContent tc2;
|
||||
private int len;
|
||||
|
||||
AccessPathConsCons() { this = TConsCons(f1, f2, len) }
|
||||
AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
|
||||
|
||||
override string toString() {
|
||||
if len = 2
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
|
||||
else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
|
||||
}
|
||||
|
||||
override Content getHead() { result = f1 }
|
||||
override TypedContent getHead() { result = tc1 }
|
||||
|
||||
override int len() { result = len }
|
||||
|
||||
override DataFlowType getType() { result = f1.getContainerType() }
|
||||
override DataFlowType getType() { result = tc1.getContainerType() }
|
||||
|
||||
override AccessPathFront getFront() { result = TFrontHead(f1) }
|
||||
override AccessPathFront getFront() { result = TFrontHead(tc1) }
|
||||
|
||||
override predicate pop(Content head, AccessPath tail) {
|
||||
head = f1 and
|
||||
override predicate pop(TypedContent head, AccessPath tail) {
|
||||
head = tc1 and
|
||||
(
|
||||
tail = TConsCons(f2, _, len - 1)
|
||||
tail = TConsCons(tc2, _, len - 1)
|
||||
or
|
||||
len = 2 and
|
||||
tail = TConsNil(f2, _)
|
||||
tail = TConsNil(tc2, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the access path obtained by popping `f` from `ap`, if any. */
|
||||
private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
|
||||
/** Gets the access path obtained by popping `tc` from `ap`, if any. */
|
||||
private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
|
||||
|
||||
/** Gets the access path obtained by pushing `f` onto `ap`. */
|
||||
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
|
||||
/** Gets the access path obtained by pushing `tc` onto `ap`. */
|
||||
private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
|
||||
|
||||
private newtype TAccessPathOption =
|
||||
TAccessPathNone() or
|
||||
@@ -1678,15 +1694,12 @@ private predicate flowFwd0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f, AccessPath ap0 |
|
||||
flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
|
||||
ap = push(f, ap0)
|
||||
)
|
||||
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
|
||||
or
|
||||
// read
|
||||
exists(Content f |
|
||||
flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
|
||||
flowFwdConsCand(f, apf, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
|
||||
flowFwdConsCand(tc, apf, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
@@ -1710,54 +1723,63 @@ private predicate flowFwd0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore(
|
||||
Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFront apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
flowFwdStore1(mid, f, node, apf0, apf, config)
|
||||
flowFwdStore0(mid, tc, node, apf0, apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
|
||||
private predicate storeCand(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
|
||||
Configuration config
|
||||
) {
|
||||
storeCand2(mid, f, node, config) and
|
||||
flowCand(mid, _, _, apf0, config)
|
||||
storeCand2(mid, tc, node, _, config) and
|
||||
flowCand(mid, _, _, apf0, config) and
|
||||
apf.headUsesContent(tc)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate flowFwdStore1(
|
||||
Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
private predicate flowFwdStore0(
|
||||
Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
|
||||
Configuration config
|
||||
) {
|
||||
flowFwdStore0(mid, f, node, apf0, config) and
|
||||
flowCandConsCand(f, apf0, config) and
|
||||
apf.headUsesContent(f) and
|
||||
storeCand(mid, tc, node, apf0, apf, config) and
|
||||
flowCandConsCand(tc, apf0, config) and
|
||||
flowCand(node, _, _, apf, unbind(config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
|
||||
Configuration config
|
||||
private predicate flowFwdRead0(
|
||||
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
|
||||
boolean fromArg, AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPathFrontHead apf0 |
|
||||
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
|
||||
readCand2(mid, f, node, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
flowCand(node, _, _, _, unbind(config))
|
||||
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
|
||||
readCandFwd(node1, tc, apf0, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdRead(
|
||||
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
|
||||
AccessPathOption argAp, Configuration config
|
||||
) {
|
||||
exists(Node mid, TypedContent tc |
|
||||
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
|
||||
flowCand(node, _, _, apf, unbind(config)) and
|
||||
flowCandConsCand(tc, apf, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowFwdConsCand(
|
||||
Content f, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node n |
|
||||
flowFwd(n, _, _, apf, ap, config) and
|
||||
flowFwdStore1(n, f, _, apf, _, config)
|
||||
flowFwdStore0(n, tc, _, apf, _, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1863,9 +1885,9 @@ private predicate flow0(
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Content f |
|
||||
flowStore(f, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(f, ap, config)
|
||||
exists(TypedContent tc |
|
||||
flowStore(tc, node, toReturn, returnAp, ap, config) and
|
||||
flowConsCand(tc, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
@@ -1895,39 +1917,41 @@ private predicate flow0(
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
storeCand2(node1, f, node2, config) and
|
||||
flowFwdStore(node2, f, ap, _, _, _, config) and
|
||||
ap0 = push(f, ap)
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flowFwdStore(node2, tc, ap, _, _, _, config) and
|
||||
ap0 = push(tc, ap)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowStore(
|
||||
Content f, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node mid, AccessPath ap0 |
|
||||
storeFlowFwd(node, f, mid, ap, ap0, config) and
|
||||
storeFlowFwd(node, tc, mid, ap, ap0, config) and
|
||||
flow(mid, toReturn, returnAp, ap0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readFlowFwd(
|
||||
Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
|
||||
) {
|
||||
readCand2(node1, f, node2, config) and
|
||||
flowFwdRead(node2, f, ap, _, _, config) and
|
||||
ap0 = pop(f, ap) and
|
||||
flowFwdConsCand(f, _, ap0, unbind(config))
|
||||
exists(AccessPathFrontHead apf |
|
||||
readCandFwd(node1, tc, apf, node2, config) and
|
||||
flowFwdRead(node2, apf, ap, _, _, _, config) and
|
||||
ap0 = pop(tc, ap) and
|
||||
flowFwdConsCand(tc, _, ap0, unbind(config))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
|
||||
private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
|
||||
exists(Node n, Node mid |
|
||||
flow(mid, _, _, ap, config) and
|
||||
readFlowFwd(n, f, mid, _, ap, config)
|
||||
readFlowFwd(n, tc, mid, _, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2270,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = TNil(getErasedNodeTypeBound(node))
|
||||
or
|
||||
exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
|
||||
exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
@@ -2284,30 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
read(node1, f, node2) and
|
||||
private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
readCandFwd(node1, tc, _, node2, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
|
||||
private predicate pathReadStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
readCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
store(node1, f, node2) and
|
||||
private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
|
||||
storeCand2(node1, tc, node2, _, config) and
|
||||
flow(node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathStoreStep(
|
||||
PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
|
||||
PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
|
||||
) {
|
||||
ap0 = mid.getAp() and
|
||||
storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
|
||||
storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext()
|
||||
}
|
||||
|
||||
@@ -2538,10 +2564,10 @@ private module FlowExploration {
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(Content f, int len) { len in [1 .. 5] }
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. 5] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||
* dereference operations needed to get from the value in the node to the
|
||||
@@ -2550,7 +2576,7 @@ private module FlowExploration {
|
||||
private class PartialAccessPath extends TPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TPartialCons(result, _) }
|
||||
TypedContent getHead() { this = TPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TPartialNil(_) and result = 0
|
||||
@@ -2561,7 +2587,7 @@ private module FlowExploration {
|
||||
DataFlowType getType() {
|
||||
this = TPartialNil(result)
|
||||
or
|
||||
exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
@@ -2579,15 +2605,15 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
|
||||
if len = 1
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
then result = "[" + tc.toString() + "]"
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2763,11 +2789,12 @@ private module FlowExploration {
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(PartialAccessPath ap0, Content f |
|
||||
partialPathReadStep(mid, ap0, f, node, cc, config) and
|
||||
exists(PartialAccessPath ap0, TypedContent tc |
|
||||
partialPathReadStep(mid, ap0, tc, node, cc, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
apConsFwd(ap, tc, ap0, config) and
|
||||
compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
|
||||
@@ -2786,35 +2813,42 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
ap1 = mid.getAp() and
|
||||
store(mid.getNode(), f, node) and
|
||||
ap2.getHead() = f and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), f.getType())
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
store(midNode, tc, node, contentType) and
|
||||
ap2.getHead() = tc and
|
||||
ap2.len() = unbindInt(ap1.len() + 1) and
|
||||
compatibleTypes(ap1.getType(), contentType)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, f, _, ap2) and
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
ap = mid.getAp() and
|
||||
readStep(mid.getNode(), f, node) and
|
||||
ap.getHead() = f and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
read(midNode, tc.getContent(), node) and
|
||||
ap.getHead() = tc and
|
||||
config = mid.getConfiguration() and
|
||||
cc = mid.getCallContext()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
|
||||
@@ -198,123 +198,130 @@ private module Cached {
|
||||
/**
|
||||
* The final flow-through calculation:
|
||||
*
|
||||
* - Input access paths are abstracted with a `ContentOption` parameter
|
||||
* that represents the head of the access path. `TContentNone()` means that
|
||||
* the access path is unrestricted.
|
||||
* - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`)
|
||||
* or summarized as a single read step with before and after types recorded
|
||||
* in the `ReadStepTypesOption` parameter.
|
||||
* - Types are checked using the `compatibleTypes()` relation.
|
||||
*/
|
||||
private module Final {
|
||||
/**
|
||||
* Holds if `p` can flow to `node` in the same callable using only
|
||||
* value-preserving steps, not taking call contexts into account.
|
||||
* value-preserving steps and possibly a single read step, not taking
|
||||
* call contexts into account.
|
||||
*
|
||||
* `contentIn` describes the content of `p` that can flow to `node`
|
||||
* (if any).
|
||||
* If a read step was taken, then `read` captures the `Content`, the
|
||||
* container type, and the content type.
|
||||
*/
|
||||
predicate parameterValueFlow(ParameterNode p, Node node, ContentOption contentIn) {
|
||||
parameterValueFlow0(p, node, contentIn) and
|
||||
predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) {
|
||||
parameterValueFlow0(p, node, read) and
|
||||
if node instanceof CastingNode
|
||||
then
|
||||
// normal flow through
|
||||
contentIn = TContentNone() and
|
||||
read = TReadStepTypesNone() and
|
||||
compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node))
|
||||
or
|
||||
// getter
|
||||
exists(Content fIn |
|
||||
contentIn.getContent() = fIn and
|
||||
compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node))
|
||||
)
|
||||
compatibleTypes(read.getContentType(), getErasedNodeTypeBound(node))
|
||||
else any()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlow0(ParameterNode p, Node node, ContentOption contentIn) {
|
||||
private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) {
|
||||
p = node and
|
||||
Cand::cand(p, _) and
|
||||
contentIn = TContentNone()
|
||||
read = TReadStepTypesNone()
|
||||
or
|
||||
// local flow
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid, contentIn) and
|
||||
parameterValueFlow(p, mid, read) and
|
||||
LocalFlowBigStep::localFlowBigStep(mid, node)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Node mid, Content f |
|
||||
parameterValueFlow(p, mid, TContentNone()) and
|
||||
readStep(mid, f, node) and
|
||||
contentIn.getContent() = f and
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid, TReadStepTypesNone()) and
|
||||
readStepWithTypes(mid, read.getContainerType(), read.getContent(), node,
|
||||
read.getContentType()) and
|
||||
Cand::parameterValueFlowReturnCand(p, _, true) and
|
||||
compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType())
|
||||
compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType())
|
||||
)
|
||||
or
|
||||
// flow through: no prior read
|
||||
exists(ArgumentNode arg |
|
||||
parameterValueFlowArg(p, arg, TContentNone()) and
|
||||
argumentValueFlowsThrough(arg, contentIn, node)
|
||||
parameterValueFlowArg(p, arg, TReadStepTypesNone()) and
|
||||
argumentValueFlowsThrough(arg, read, node)
|
||||
)
|
||||
or
|
||||
// flow through: no read inside method
|
||||
exists(ArgumentNode arg |
|
||||
parameterValueFlowArg(p, arg, contentIn) and
|
||||
argumentValueFlowsThrough(arg, TContentNone(), node)
|
||||
parameterValueFlowArg(p, arg, read) and
|
||||
argumentValueFlowsThrough(arg, TReadStepTypesNone(), node)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate parameterValueFlowArg(
|
||||
ParameterNode p, ArgumentNode arg, ContentOption contentIn
|
||||
ParameterNode p, ArgumentNode arg, ReadStepTypesOption read
|
||||
) {
|
||||
parameterValueFlow(p, arg, contentIn) and
|
||||
parameterValueFlow(p, arg, read) and
|
||||
Cand::argumentValueFlowsThroughCand(arg, _, _)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate argumentValueFlowsThrough0(
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn
|
||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read
|
||||
) {
|
||||
exists(ParameterNode param | viableParamArg(call, param, arg) |
|
||||
parameterValueFlowReturn(param, kind, contentIn)
|
||||
parameterValueFlowReturn(param, kind, read)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
|
||||
* not taking call contexts into account.
|
||||
* Holds if `arg` flows to `out` through a call using only
|
||||
* value-preserving steps and possibly a single read step, not taking
|
||||
* call contexts into account.
|
||||
*
|
||||
* `contentIn` describes the content of `arg` that can flow to `out` (if any).
|
||||
* If a read step was taken, then `read` captures the `Content`, the
|
||||
* container type, and the content type.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate argumentValueFlowsThrough(ArgumentNode arg, ContentOption contentIn, Node out) {
|
||||
predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
argumentValueFlowsThrough0(call, arg, kind, contentIn) and
|
||||
argumentValueFlowsThrough0(call, arg, kind, read) and
|
||||
out = getAnOutNode(call, kind)
|
||||
|
|
||||
// normal flow through
|
||||
contentIn = TContentNone() and
|
||||
read = TReadStepTypesNone() and
|
||||
compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out))
|
||||
or
|
||||
// getter
|
||||
exists(Content fIn |
|
||||
contentIn.getContent() = fIn and
|
||||
compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and
|
||||
compatibleTypes(fIn.getType(), getErasedNodeTypeBound(out))
|
||||
)
|
||||
compatibleTypes(getErasedNodeTypeBound(arg), read.getContainerType()) and
|
||||
compatibleTypes(read.getContentType(), getErasedNodeTypeBound(out))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` flows to `out` through a call using only
|
||||
* value-preserving steps and a single read step, not taking call
|
||||
* contexts into account, thus representing a getter-step.
|
||||
*/
|
||||
predicate getterStep(ArgumentNode arg, Content c, Node out) {
|
||||
argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `p` can flow to a return node of kind `kind` in the same
|
||||
* callable using only value-preserving steps.
|
||||
* callable using only value-preserving steps and possibly a single read
|
||||
* step.
|
||||
*
|
||||
* `contentIn` describes the content of `p` that can flow to the return
|
||||
* node (if any).
|
||||
* If a read step was taken, then `read` captures the `Content`, the
|
||||
* container type, and the content type.
|
||||
*/
|
||||
private predicate parameterValueFlowReturn(
|
||||
ParameterNode p, ReturnKind kind, ContentOption contentIn
|
||||
ParameterNode p, ReturnKind kind, ReadStepTypesOption read
|
||||
) {
|
||||
exists(ReturnNode ret |
|
||||
parameterValueFlow(p, ret, contentIn) and
|
||||
parameterValueFlow(p, ret, read) and
|
||||
kind = ret.getKind()
|
||||
)
|
||||
}
|
||||
@@ -329,7 +336,27 @@ private module Cached {
|
||||
*/
|
||||
cached
|
||||
predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) {
|
||||
parameterValueFlow(p, n.getPreUpdateNode(), TContentNone())
|
||||
parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone())
|
||||
}
|
||||
|
||||
private predicate store(
|
||||
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
|
||||
) {
|
||||
storeStep(node1, c, node2) and
|
||||
readStep(_, c, _) and
|
||||
contentType = getErasedNodeTypeBound(node1) and
|
||||
containerType = getErasedNodeTypeBound(node2)
|
||||
or
|
||||
exists(Node n1, Node n2 |
|
||||
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
|
||||
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
||||
|
|
||||
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1)
|
||||
or
|
||||
readStep(n2, c, n1) and
|
||||
contentType = getErasedNodeTypeBound(n1) and
|
||||
containerType = getErasedNodeTypeBound(n2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -340,17 +367,8 @@ private module Cached {
|
||||
* been stored into, in order to handle cases like `x.f1.f2 = y`.
|
||||
*/
|
||||
cached
|
||||
predicate store(Node node1, Content f, Node node2) {
|
||||
storeStep(node1, f, node2) and readStep(_, f, _)
|
||||
or
|
||||
exists(Node n1, Node n2 |
|
||||
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
|
||||
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
||||
|
|
||||
argumentValueFlowsThrough(n2, TContentSome(f), n1)
|
||||
or
|
||||
readStep(n2, f, n1)
|
||||
)
|
||||
predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
|
||||
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
|
||||
}
|
||||
|
||||
import FlowThrough
|
||||
@@ -397,10 +415,13 @@ private module Cached {
|
||||
TBooleanNone() or
|
||||
TBooleanSome(boolean b) { b = true or b = false }
|
||||
|
||||
cached
|
||||
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
|
||||
|
||||
cached
|
||||
newtype TAccessPathFront =
|
||||
TFrontNil(DataFlowType t) or
|
||||
TFrontHead(Content f)
|
||||
TFrontHead(TypedContent tc)
|
||||
|
||||
cached
|
||||
newtype TAccessPathFrontOption =
|
||||
@@ -415,25 +436,38 @@ class CastingNode extends Node {
|
||||
CastingNode() {
|
||||
this instanceof ParameterNode or
|
||||
this instanceof CastNode or
|
||||
this instanceof OutNodeExt
|
||||
this instanceof OutNodeExt or
|
||||
// For reads, `x.f`, we want to check that the tracked type after the read (which
|
||||
// is obtained by popping the head of the access path stack) is compatible with
|
||||
// the type of `x.f`.
|
||||
readStep(_, _, this)
|
||||
}
|
||||
}
|
||||
|
||||
newtype TContentOption =
|
||||
TContentNone() or
|
||||
TContentSome(Content f)
|
||||
private predicate readStepWithTypes(
|
||||
Node n1, DataFlowType container, Content c, Node n2, DataFlowType content
|
||||
) {
|
||||
readStep(n1, c, n2) and
|
||||
container = getErasedNodeTypeBound(n1) and
|
||||
content = getErasedNodeTypeBound(n2)
|
||||
}
|
||||
|
||||
private class ContentOption extends TContentOption {
|
||||
Content getContent() { this = TContentSome(result) }
|
||||
|
||||
predicate hasContent() { exists(this.getContent()) }
|
||||
|
||||
string toString() {
|
||||
result = this.getContent().toString()
|
||||
or
|
||||
not this.hasContent() and
|
||||
result = "<none>"
|
||||
private newtype TReadStepTypesOption =
|
||||
TReadStepTypesNone() or
|
||||
TReadStepTypesSome(DataFlowType container, Content c, DataFlowType content) {
|
||||
readStepWithTypes(_, container, c, _, content)
|
||||
}
|
||||
|
||||
private class ReadStepTypesOption extends TReadStepTypesOption {
|
||||
predicate isSome() { this instanceof TReadStepTypesSome }
|
||||
|
||||
DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) }
|
||||
|
||||
Content getContent() { this = TReadStepTypesSome(_, result, _) }
|
||||
|
||||
DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) }
|
||||
|
||||
string toString() { if this.isSome() then result = "Some(..)" else result = "None()" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -692,6 +726,23 @@ class BooleanOption extends TBooleanOption {
|
||||
}
|
||||
}
|
||||
|
||||
/** Content tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
private DataFlowType t;
|
||||
|
||||
TypedContent() { this = MkTypedContent(c, t) }
|
||||
|
||||
/** Gets the content. */
|
||||
Content getContent() { result = c }
|
||||
|
||||
/** Gets the container type. */
|
||||
DataFlowType getContainerType() { result = t }
|
||||
|
||||
/** Gets a textual representation of this content. */
|
||||
string toString() { result = c.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The front of an access path. This is either a head or a nil.
|
||||
*/
|
||||
@@ -702,25 +753,29 @@ abstract class AccessPathFront extends TAccessPathFront {
|
||||
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
predicate headUsesContent(Content f) { this = TFrontHead(f) }
|
||||
predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) }
|
||||
}
|
||||
|
||||
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
|
||||
}
|
||||
private DataFlowType t;
|
||||
|
||||
override DataFlowType getType() { this = TFrontNil(result) }
|
||||
AccessPathFrontNil() { this = TFrontNil(t) }
|
||||
|
||||
override string toString() { result = ppReprType(t) }
|
||||
|
||||
override DataFlowType getType() { result = t }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = false }
|
||||
}
|
||||
|
||||
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
|
||||
override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) }
|
||||
private TypedContent tc;
|
||||
|
||||
override DataFlowType getType() {
|
||||
exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
|
||||
}
|
||||
AccessPathFrontHead() { this = TFrontHead(tc) }
|
||||
|
||||
override string toString() { result = tc.toString() }
|
||||
|
||||
override DataFlowType getType() { result = tc.getContainerType() }
|
||||
|
||||
override boolean toBoolNonEmpty() { result = true }
|
||||
}
|
||||
|
||||
@@ -86,7 +86,12 @@ class ReturnValueNode extends ReturnNode {
|
||||
class ReturnIndirectionNode extends ReturnNode {
|
||||
override ReturnIndirectionInstruction primary;
|
||||
|
||||
override ReturnKind getKind() { result = TIndirectReturnKind(primary.getParameter().getIndex()) }
|
||||
override ReturnKind getKind() {
|
||||
result = TIndirectReturnKind(-1) and
|
||||
primary.isThisIndirection()
|
||||
or
|
||||
result = TIndirectReturnKind(primary.getParameter().getIndex())
|
||||
}
|
||||
}
|
||||
|
||||
/** A data flow node that represents the output of a call. */
|
||||
@@ -123,8 +128,13 @@ private class SideEffectOutNode extends OutNode {
|
||||
* `kind`.
|
||||
*/
|
||||
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
|
||||
result.getCall() = call and
|
||||
result.getReturnKind() = kind
|
||||
// There should be only one `OutNode` for a given `(call, kind)` pair. Showing the optimizer that
|
||||
// this is true helps it make better decisions downstream, especially in virtual dispatch.
|
||||
result =
|
||||
unique(OutNode outNode |
|
||||
outNode.getCall() = call and
|
||||
outNode.getReturnKind() = kind
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,12 +160,6 @@ class Content extends TContent {
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
|
||||
}
|
||||
|
||||
/** Gets the type of the object containing this content. */
|
||||
abstract Type getContainerType();
|
||||
|
||||
/** Gets the type of this content. */
|
||||
abstract Type getType();
|
||||
}
|
||||
|
||||
private class FieldContent extends Content, TFieldContent {
|
||||
@@ -170,26 +174,14 @@ private class FieldContent extends Content, TFieldContent {
|
||||
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
|
||||
}
|
||||
|
||||
override Type getContainerType() { result = f.getDeclaringType() }
|
||||
|
||||
override Type getType() { result = f.getType() }
|
||||
}
|
||||
|
||||
private class CollectionContent extends Content, TCollectionContent {
|
||||
override string toString() { result = "collection" }
|
||||
|
||||
override Type getContainerType() { none() }
|
||||
|
||||
override Type getType() { none() }
|
||||
}
|
||||
|
||||
private class ArrayContent extends Content, TArrayContent {
|
||||
override string toString() { result = "array" }
|
||||
|
||||
override Type getContainerType() { none() }
|
||||
|
||||
override Type getType() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -111,6 +111,8 @@ private class IRSizedType extends IRType {
|
||||
this = TIRFunctionAddressType(byteSize) or
|
||||
this = TIROpaqueType(_, byteSize)
|
||||
}
|
||||
// Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
|
||||
// overridden only in the leaf classes.
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,7 +130,7 @@ class IRBooleanType extends IRSizedType, TIRBooleanType {
|
||||
}
|
||||
|
||||
/**
|
||||
* A numberic type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and
|
||||
* A numeric type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and
|
||||
* `IRFloatingPointType`.
|
||||
*/
|
||||
class IRNumericType extends IRSizedType {
|
||||
@@ -137,13 +139,27 @@ class IRNumericType extends IRSizedType {
|
||||
this = TIRUnsignedIntegerType(byteSize) or
|
||||
this = TIRFloatingPointType(byteSize, _, _)
|
||||
}
|
||||
// Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
|
||||
// overridden only in the leaf classes.
|
||||
}
|
||||
|
||||
/**
|
||||
* An integer type. This includes `IRSignedIntegerType` and `IRUnsignedIntegerType`.
|
||||
*/
|
||||
class IRIntegerType extends IRNumericType {
|
||||
IRIntegerType() {
|
||||
this = TIRSignedIntegerType(byteSize) or
|
||||
this = TIRUnsignedIntegerType(byteSize)
|
||||
}
|
||||
// Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
|
||||
// overridden only in the leaf classes.
|
||||
}
|
||||
|
||||
/**
|
||||
* A signed two's-complement integer. Also used to represent enums whose underlying type is a signed
|
||||
* integer, as well as character types whose representation is signed.
|
||||
*/
|
||||
class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType {
|
||||
class IRSignedIntegerType extends IRIntegerType, TIRSignedIntegerType {
|
||||
final override string toString() { result = "int" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
@@ -158,7 +174,7 @@ class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType {
|
||||
* An unsigned two's-complement integer. Also used to represent enums whose underlying type is an
|
||||
* unsigned integer, as well as character types whose representation is unsigned.
|
||||
*/
|
||||
class IRUnsignedIntegerType extends IRNumericType, TIRUnsignedIntegerType {
|
||||
class IRUnsignedIntegerType extends IRIntegerType, TIRUnsignedIntegerType {
|
||||
final override string toString() { result = "uint" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
|
||||
@@ -8,10 +8,79 @@ module InstructionConsistency {
|
||||
private import Imports::Overlap
|
||||
private import internal.IRInternal
|
||||
|
||||
private newtype TOptionalIRFunction =
|
||||
TPresentIRFunction(IRFunction irFunc) or
|
||||
TMissingIRFunction()
|
||||
|
||||
/**
|
||||
* An `IRFunction` that might not exist. This is used so that we can produce consistency failures
|
||||
* for IR that also incorrectly lacks a `getEnclosingIRFunction()`.
|
||||
*/
|
||||
abstract private class OptionalIRFunction extends TOptionalIRFunction {
|
||||
abstract string toString();
|
||||
|
||||
abstract Language::Location getLocation();
|
||||
}
|
||||
|
||||
private class PresentIRFunction extends OptionalIRFunction, TPresentIRFunction {
|
||||
private IRFunction irFunc;
|
||||
|
||||
PresentIRFunction() { this = TPresentIRFunction(irFunc) }
|
||||
|
||||
override string toString() {
|
||||
result = concat(Language::getIdentityString(irFunc.getFunction()), "; ")
|
||||
}
|
||||
|
||||
override Language::Location getLocation() {
|
||||
// To avoid an overwhelming number of results when the extractor merges functions with the
|
||||
// same name, just pick a single location.
|
||||
result =
|
||||
rank[1](Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private class MissingIRFunction extends OptionalIRFunction, TMissingIRFunction {
|
||||
override string toString() { result = "<Missing IRFunction>" }
|
||||
|
||||
override Language::Location getLocation() { result instanceof Language::UnknownDefaultLocation }
|
||||
}
|
||||
|
||||
private OptionalIRFunction getInstructionIRFunction(Instruction instr) {
|
||||
result = TPresentIRFunction(instr.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(instr.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private OptionalIRFunction getInstructionIRFunction(Instruction instr, string irFuncText) {
|
||||
result = getInstructionIRFunction(instr) and
|
||||
irFuncText = result.toString()
|
||||
}
|
||||
|
||||
private OptionalIRFunction getOperandIRFunction(Operand operand) {
|
||||
result = TPresentIRFunction(operand.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(operand.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private OptionalIRFunction getOperandIRFunction(Operand operand, string irFuncText) {
|
||||
result = getOperandIRFunction(operand) and
|
||||
irFuncText = result.toString()
|
||||
}
|
||||
|
||||
private OptionalIRFunction getBlockIRFunction(IRBlock block) {
|
||||
result = TPresentIRFunction(block.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(block.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
|
||||
*/
|
||||
query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) {
|
||||
query predicate missingOperand(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag |
|
||||
instr.getOpcode().hasOperand(tag) and
|
||||
not exists(NonPhiOperand operand |
|
||||
@@ -21,32 +90,39 @@ module InstructionConsistency {
|
||||
message =
|
||||
"Instruction '" + instr.getOpcode().toString() +
|
||||
"' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has an unexpected operand with tag `tag`.
|
||||
*/
|
||||
query predicate unexpectedOperand(Instruction instr, OperandTag tag) {
|
||||
exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
not instr.getOpcode().hasOperand(tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (
|
||||
instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
|
||||
) and
|
||||
not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag)
|
||||
query predicate unexpectedOperand(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag |
|
||||
exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
not instr.getOpcode().hasOperand(tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (
|
||||
instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
|
||||
) and
|
||||
not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' has unexpected operand '" + tag.toString() +
|
||||
"' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has multiple operands with tag `tag`.
|
||||
*/
|
||||
query predicate duplicateOperand(
|
||||
Instruction instr, string message, IRFunction func, string funcText
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag, int operandCount |
|
||||
operandCount =
|
||||
@@ -58,8 +134,7 @@ module InstructionConsistency {
|
||||
message =
|
||||
"Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
|
||||
" in function '$@'." and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -67,100 +142,136 @@ module InstructionConsistency {
|
||||
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
|
||||
* the predecessor block `pred`.
|
||||
*/
|
||||
query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
|
||||
pred = instr.getBlock().getAPredecessor() and
|
||||
not exists(PhiInputOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getPredecessorBlock() = pred
|
||||
query predicate missingPhiOperand(
|
||||
PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRBlock pred |
|
||||
pred = instr.getBlock().getAPredecessor() and
|
||||
not exists(PhiInputOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getPredecessorBlock() = pred
|
||||
) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is missing an operand for predecessor block '" +
|
||||
pred.toString() + "' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingOperandType(Operand operand, string message) {
|
||||
exists(Language::Function func, Instruction use |
|
||||
query predicate missingOperandType(
|
||||
Operand operand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Instruction use |
|
||||
not exists(operand.getType()) and
|
||||
use = operand.getUse() and
|
||||
func = use.getEnclosingFunction() and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
|
||||
"' missing type in function '" + Language::getIdentityString(func) + "'."
|
||||
"' is missing a type in function '$@'." and
|
||||
irFunc = getOperandIRFunction(operand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate duplicateChiOperand(
|
||||
ChiInstruction chi, string message, IRFunction func, string funcText
|
||||
ChiInstruction chi, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
chi.getTotal() = chi.getPartial() and
|
||||
message =
|
||||
"Chi instruction for " + chi.getPartial().toString() +
|
||||
" has duplicate operands in function $@" and
|
||||
func = chi.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
" has duplicate operands in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(chi, irFuncText)
|
||||
}
|
||||
|
||||
query predicate sideEffectWithoutPrimary(
|
||||
SideEffectInstruction instr, string message, IRFunction func, string funcText
|
||||
SideEffectInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(instr.getPrimaryInstruction()) and
|
||||
message = "Side effect instruction missing primary instruction in function $@" and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
message =
|
||||
"Side effect instruction '" + instr + "' is missing a primary instruction in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
||||
*/
|
||||
query predicate instructionWithoutSuccessor(Instruction instr) {
|
||||
query predicate instructionWithoutSuccessor(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(instr.getASuccessor()) and
|
||||
not instr instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not instr instanceof PhiInstruction and
|
||||
not instr instanceof UnreachedInstruction
|
||||
not instr instanceof UnreachedInstruction and
|
||||
message = "Instruction '" + instr.toString() + "' has no successors in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there are multiple (`n`) edges of kind `kind` from `source`,
|
||||
* where `target` is among the targets of those edges.
|
||||
* Holds if there are multiple edges of the same kind from `source`.
|
||||
*/
|
||||
query predicate ambiguousSuccessors(Instruction source, EdgeKind kind, int n, Instruction target) {
|
||||
n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
|
||||
n > 1 and
|
||||
source.getSuccessor(kind) = target
|
||||
query predicate ambiguousSuccessors(
|
||||
Instruction source, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(EdgeKind kind, int n |
|
||||
n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
|
||||
n > 1 and
|
||||
message =
|
||||
"Instruction '" + source.toString() + "' has " + n.toString() + " successors of kind '" +
|
||||
kind.toString() + "' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(source, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` in `f` is part of a loop even though the AST of `f`
|
||||
* Holds if `instr` is part of a loop even though the AST of `instr`'s enclosing function
|
||||
* contains no element that can cause loops.
|
||||
*/
|
||||
query predicate unexplainedLoop(Language::Function f, Instruction instr) {
|
||||
exists(IRBlock block |
|
||||
instr.getBlock() = block and
|
||||
block.getEnclosingFunction() = f and
|
||||
block.getASuccessor+() = block
|
||||
) and
|
||||
not Language::hasPotentialLoop(f)
|
||||
query predicate unexplainedLoop(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Language::Function f |
|
||||
exists(IRBlock block |
|
||||
instr.getBlock() = block and
|
||||
block.getEnclosingFunction() = f and
|
||||
block.getASuccessor+() = block
|
||||
) and
|
||||
not Language::hasPotentialLoop(f) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is part of an unexplained loop in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `Phi` instruction is present in a block with fewer than two
|
||||
* predecessors.
|
||||
*/
|
||||
query predicate unnecessaryPhiInstruction(PhiInstruction instr) {
|
||||
count(instr.getBlock().getAPredecessor()) < 2
|
||||
query predicate unnecessaryPhiInstruction(
|
||||
PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int n |
|
||||
n = count(instr.getBlock().getAPredecessor()) and
|
||||
n < 2 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is in a block with only " + n.toString() +
|
||||
" predecessors in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a memory operand is connected to a definition with an unmodeled result.
|
||||
*/
|
||||
query predicate memoryOperandDefinitionIsUnmodeled(
|
||||
Instruction instr, string message, IRFunction func, string funcText
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(MemoryOperand operand, Instruction def |
|
||||
operand = instr.getAnOperand() and
|
||||
def = operand.getAnyDef() and
|
||||
not def.isResultModeled() and
|
||||
message = "Memory operand definition has unmodeled result in function '$@'" and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
message =
|
||||
"Memory operand definition on instruction '" + instr.toString() +
|
||||
"' has unmodeled result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -168,18 +279,37 @@ module InstructionConsistency {
|
||||
* Holds if operand `operand` consumes a value that was defined in
|
||||
* a different function.
|
||||
*/
|
||||
query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) {
|
||||
operand.getUse() = instr and
|
||||
operand.getAnyDef() = defInstr and
|
||||
instr.getEnclosingIRFunction() != defInstr.getEnclosingIRFunction()
|
||||
query predicate operandAcrossFunctions(
|
||||
Operand operand, string message, OptionalIRFunction useIRFunc, string useIRFuncText,
|
||||
OptionalIRFunction defIRFunc, string defIRFuncText
|
||||
) {
|
||||
exists(Instruction useInstr, Instruction defInstr |
|
||||
operand.getUse() = useInstr and
|
||||
operand.getAnyDef() = defInstr and
|
||||
useIRFunc = getInstructionIRFunction(useInstr, useIRFuncText) and
|
||||
defIRFunc = getInstructionIRFunction(defInstr, defIRFuncText) and
|
||||
useIRFunc != defIRFunc and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' is used on instruction '" + useInstr.toString() +
|
||||
"' in function '$@', but is defined on instruction '" + defInstr.toString() +
|
||||
"' in function '$@'."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is not in exactly one block.
|
||||
*/
|
||||
query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) {
|
||||
blockCount = count(instr.getBlock()) and
|
||||
blockCount != 1
|
||||
query predicate instructionWithoutUniqueBlock(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int blockCount |
|
||||
blockCount = count(instr.getBlock()) and
|
||||
blockCount != 1 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is a member of " + blockCount.toString() +
|
||||
" blocks in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate forwardEdge(IRBlock b1, IRBlock b2) {
|
||||
@@ -192,10 +322,11 @@ module InstructionConsistency {
|
||||
*
|
||||
* This check ensures we don't have too _few_ back edges.
|
||||
*/
|
||||
query predicate containsLoopOfForwardEdges(IRFunction f) {
|
||||
query predicate containsLoopOfForwardEdges(IRFunction f, string message) {
|
||||
exists(IRBlock block |
|
||||
forwardEdge+(block, block) and
|
||||
block.getEnclosingIRFunction() = f
|
||||
block.getEnclosingIRFunction() = f and
|
||||
message = "Function contains a loop consisting of only forward edges."
|
||||
)
|
||||
}
|
||||
|
||||
@@ -207,12 +338,19 @@ module InstructionConsistency {
|
||||
*
|
||||
* This check ensures we don't have too _many_ back edges.
|
||||
*/
|
||||
query predicate lostReachability(IRBlock block) {
|
||||
query predicate lostReachability(
|
||||
IRBlock block, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRFunction f, IRBlock entry |
|
||||
entry = f.getEntryBlock() and
|
||||
entry.getASuccessor+() = block and
|
||||
not forwardEdge+(entry, block) and
|
||||
not Language::hasGoto(f.getFunction())
|
||||
not Language::hasGoto(f.getFunction()) and
|
||||
message =
|
||||
"Block '" + block.toString() +
|
||||
"' is not reachable by traversing only forward edges in function '$@'." and
|
||||
irFunc = TPresentIRFunction(f) and
|
||||
irFuncText = irFunc.toString()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -220,16 +358,22 @@ module InstructionConsistency {
|
||||
* Holds if the number of back edges differs between the `Instruction` graph
|
||||
* and the `IRBlock` graph.
|
||||
*/
|
||||
query predicate backEdgeCountMismatch(Language::Function f, int fromInstr, int fromBlock) {
|
||||
fromInstr =
|
||||
count(Instruction i1, Instruction i2 |
|
||||
i1.getEnclosingFunction() = f and i1.getBackEdgeSuccessor(_) = i2
|
||||
) and
|
||||
fromBlock =
|
||||
count(IRBlock b1, IRBlock b2 |
|
||||
b1.getEnclosingFunction() = f and b1.getBackEdgeSuccessor(_) = b2
|
||||
) and
|
||||
fromInstr != fromBlock
|
||||
query predicate backEdgeCountMismatch(OptionalIRFunction irFunc, string message) {
|
||||
exists(int fromInstr, int fromBlock |
|
||||
fromInstr =
|
||||
count(Instruction i1, Instruction i2 |
|
||||
getInstructionIRFunction(i1) = irFunc and i1.getBackEdgeSuccessor(_) = i2
|
||||
) and
|
||||
fromBlock =
|
||||
count(IRBlock b1, IRBlock b2 |
|
||||
getBlockIRFunction(b1) = irFunc and b1.getBackEdgeSuccessor(_) = b2
|
||||
) and
|
||||
fromInstr != fromBlock and
|
||||
message =
|
||||
"The instruction graph for function '" + irFunc.toString() + "' contains " +
|
||||
fromInstr.toString() + " back edges, but the block graph contains " + fromBlock.toString()
|
||||
+ " back edges."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,7 +395,7 @@ module InstructionConsistency {
|
||||
* Holds if `useOperand` has a definition that does not dominate the use.
|
||||
*/
|
||||
query predicate useNotDominatedByDefinition(
|
||||
Operand useOperand, string message, IRFunction func, string funcText
|
||||
Operand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
|
||||
pointOfEvaluation(useOperand, useBlock, useIndex) and
|
||||
@@ -272,19 +416,17 @@ module InstructionConsistency {
|
||||
message =
|
||||
"Operand '" + useOperand.toString() +
|
||||
"' is not dominated by its definition in function '$@'." and
|
||||
func = useOperand.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
irFunc = getOperandIRFunction(useOperand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate switchInstructionWithoutDefaultEdge(
|
||||
SwitchInstruction switchInstr, string message, IRFunction func, string funcText
|
||||
SwitchInstruction switchInstr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(switchInstr.getDefaultSuccessor()) and
|
||||
message =
|
||||
"SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and
|
||||
func = switchInstr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
irFunc = getInstructionIRFunction(switchInstr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -305,18 +447,30 @@ module InstructionConsistency {
|
||||
instr.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
query predicate notMarkedAsConflated(Instruction instr) {
|
||||
query predicate notMarkedAsConflated(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
shouldBeConflated(instr) and
|
||||
not instr.isResultConflated()
|
||||
not instr.isResultConflated() and
|
||||
message =
|
||||
"Instruction '" + instr.toString() +
|
||||
"' should be marked as having a conflated result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
query predicate wronglyMarkedAsConflated(Instruction instr) {
|
||||
query predicate wronglyMarkedAsConflated(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
instr.isResultConflated() and
|
||||
not shouldBeConflated(instr)
|
||||
not shouldBeConflated(instr) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() +
|
||||
"' should not be marked as having a conflated result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
query predicate invalidOverlap(
|
||||
MemoryOperand useOperand, string message, IRFunction func, string funcText
|
||||
MemoryOperand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Overlap overlap |
|
||||
overlap = useOperand.getDefinitionOverlap() and
|
||||
@@ -324,8 +478,20 @@ module InstructionConsistency {
|
||||
message =
|
||||
"MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
|
||||
overlap.toString() + "'." and
|
||||
func = useOperand.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
irFunc = getOperandIRFunction(useOperand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate nonUniqueEnclosingIRFunction(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int irFuncCount |
|
||||
irFuncCount = count(instr.getEnclosingIRFunction()) and
|
||||
irFuncCount != 1 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' has " + irFuncCount.toString() +
|
||||
" results for `getEnclosingIRFunction()` in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,12 @@
|
||||
private import internal.IRInternal
|
||||
private import internal.IRFunctionImports as Imports
|
||||
import Imports::IRFunctionBase
|
||||
import Instruction
|
||||
|
||||
private newtype TIRFunction =
|
||||
MkIRFunction(Language::Function func) { Construction::functionHasIR(func) }
|
||||
|
||||
/**
|
||||
* Represents the IR for a function.
|
||||
* The IR for a function.
|
||||
*/
|
||||
class IRFunction extends TIRFunction {
|
||||
Language::Function func;
|
||||
|
||||
IRFunction() { this = MkIRFunction(func) }
|
||||
|
||||
final string toString() { result = "IR: " + func.toString() }
|
||||
|
||||
/**
|
||||
* Gets the function whose IR is represented.
|
||||
*/
|
||||
final Language::Function getFunction() { result = func }
|
||||
|
||||
/**
|
||||
* Gets the location of the function.
|
||||
*/
|
||||
final Language::Location getLocation() { result = func.getLocation() }
|
||||
|
||||
class IRFunction extends IRFunctionBase {
|
||||
/**
|
||||
* Gets the entry point for this function.
|
||||
*/
|
||||
|
||||
@@ -217,19 +217,23 @@ class IRThrowVariable extends IRTempVariable {
|
||||
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
|
||||
* function that accepts a variable number of arguments.
|
||||
*/
|
||||
class IREllipsisVariable extends IRTempVariable {
|
||||
class IREllipsisVariable extends IRTempVariable, IRParameter {
|
||||
IREllipsisVariable() { tag = EllipsisTempVar() }
|
||||
|
||||
final override string toString() { result = "#ellipsis" }
|
||||
|
||||
final override int getIndex() { result = func.getNumberOfParameters() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A temporary variable generated to hold the `this` pointer.
|
||||
*/
|
||||
class IRThisVariable extends IRTempVariable {
|
||||
class IRThisVariable extends IRTempVariable, IRParameter {
|
||||
IRThisVariable() { tag = ThisTempVar() }
|
||||
|
||||
final override string toString() { result = "#this" }
|
||||
|
||||
final override int getIndex() { result = -1 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -274,3 +278,29 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial
|
||||
|
||||
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR variable which acts like a function parameter, including positional parameters and the
|
||||
* temporary variables generated for `this` and ellipsis parameters.
|
||||
*/
|
||||
class IRParameter extends IRAutomaticVariable {
|
||||
IRParameter() {
|
||||
this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter
|
||||
or
|
||||
this = TIRTempVariable(_, _, ThisTempVar(), _)
|
||||
or
|
||||
this = TIRTempVariable(_, _, EllipsisTempVar(), _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the zero-based index of this parameter. The `this` parameter has index -1.
|
||||
*/
|
||||
int getIndex() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR variable representing a positional parameter.
|
||||
*/
|
||||
class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable {
|
||||
final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() }
|
||||
}
|
||||
|
||||
@@ -29,7 +29,13 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil
|
||||
/**
|
||||
* Represents a single operation in the IR.
|
||||
*/
|
||||
class Instruction extends Construction::TInstruction {
|
||||
class Instruction extends Construction::TStageInstruction {
|
||||
Instruction() {
|
||||
// The base `TStageInstruction` type is a superset of the actual instructions appearing in this
|
||||
// stage. This call lets the stage filter out the ones that are not reused from raw IR.
|
||||
Construction::hasInstruction(this)
|
||||
}
|
||||
|
||||
final string toString() { result = getOpcode().toString() + ": " + getAST().toString() }
|
||||
|
||||
/**
|
||||
@@ -194,14 +200,14 @@ class Instruction extends Construction::TInstruction {
|
||||
* conversion.
|
||||
*/
|
||||
final Language::Expr getConvertedResultExpression() {
|
||||
result = Construction::getInstructionConvertedResultExpression(this)
|
||||
result = Raw::getInstructionConvertedResultExpression(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any.
|
||||
*/
|
||||
final Language::Expr getUnconvertedResultExpression() {
|
||||
result = Construction::getInstructionUnconvertedResultExpression(this)
|
||||
result = Raw::getInstructionUnconvertedResultExpression(this)
|
||||
}
|
||||
|
||||
final Language::LanguageType getResultLanguageType() {
|
||||
@@ -212,6 +218,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* Gets the type of the result produced by this instruction. If the instruction does not produce
|
||||
* a result, its result type will be `IRVoidType`.
|
||||
*/
|
||||
cached
|
||||
final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
@@ -250,7 +257,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* result of the `Load` instruction is a prvalue of type `int`, representing
|
||||
* the integer value loaded from variable `x`.
|
||||
*/
|
||||
final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
|
||||
final predicate isGLValue() { getResultLanguageType().hasType(_, true) }
|
||||
|
||||
/**
|
||||
* Gets the size of the result produced by this instruction, in bytes. If the
|
||||
@@ -259,7 +266,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* If `this.isGLValue()` holds for this instruction, the value of
|
||||
* `getResultSize()` will always be the size of a pointer.
|
||||
*/
|
||||
final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
|
||||
final int getResultSize() { result = getResultLanguageType().getByteSize() }
|
||||
|
||||
/**
|
||||
* Gets the opcode that specifies the operation performed by this instruction.
|
||||
@@ -395,7 +402,7 @@ class Instruction extends Construction::TInstruction {
|
||||
class VariableInstruction extends Instruction {
|
||||
IRVariable var;
|
||||
|
||||
VariableInstruction() { var = Construction::getInstructionVariable(this) }
|
||||
VariableInstruction() { var = Raw::getInstructionVariable(this) }
|
||||
|
||||
override string getImmediateString() { result = var.toString() }
|
||||
|
||||
@@ -410,7 +417,7 @@ class VariableInstruction extends Instruction {
|
||||
class FieldInstruction extends Instruction {
|
||||
Language::Field field;
|
||||
|
||||
FieldInstruction() { field = Construction::getInstructionField(this) }
|
||||
FieldInstruction() { field = Raw::getInstructionField(this) }
|
||||
|
||||
final override string getImmediateString() { result = field.toString() }
|
||||
|
||||
@@ -420,7 +427,7 @@ class FieldInstruction extends Instruction {
|
||||
class FunctionInstruction extends Instruction {
|
||||
Language::Function funcSymbol;
|
||||
|
||||
FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) }
|
||||
FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }
|
||||
|
||||
final override string getImmediateString() { result = funcSymbol.toString() }
|
||||
|
||||
@@ -430,7 +437,7 @@ class FunctionInstruction extends Instruction {
|
||||
class ConstantValueInstruction extends Instruction {
|
||||
string value;
|
||||
|
||||
ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) }
|
||||
ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) }
|
||||
|
||||
final override string getImmediateString() { result = value }
|
||||
|
||||
@@ -440,7 +447,7 @@ class ConstantValueInstruction extends Instruction {
|
||||
class IndexedInstruction extends Instruction {
|
||||
int index;
|
||||
|
||||
IndexedInstruction() { index = Construction::getInstructionIndex(this) }
|
||||
IndexedInstruction() { index = Raw::getInstructionIndex(this) }
|
||||
|
||||
final override string getImmediateString() { result = index.toString() }
|
||||
|
||||
@@ -541,6 +548,11 @@ class ReturnIndirectionInstruction extends VariableInstruction {
|
||||
* function.
|
||||
*/
|
||||
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
|
||||
|
||||
/**
|
||||
* Holds if this instruction is the return indirection for `this`.
|
||||
*/
|
||||
final predicate isThisIndirection() { var instanceof IRThisVariable }
|
||||
}
|
||||
|
||||
class CopyInstruction extends Instruction {
|
||||
@@ -598,11 +610,16 @@ class ConstantInstruction extends ConstantValueInstruction {
|
||||
}
|
||||
|
||||
class IntegerConstantInstruction extends ConstantInstruction {
|
||||
IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType }
|
||||
IntegerConstantInstruction() {
|
||||
exists(IRType resultType |
|
||||
resultType = getResultIRType() and
|
||||
(resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class FloatConstantInstruction extends ConstantInstruction {
|
||||
FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
|
||||
FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType }
|
||||
}
|
||||
|
||||
class StringConstantInstruction extends VariableInstruction {
|
||||
@@ -699,7 +716,7 @@ class PointerArithmeticInstruction extends BinaryInstruction {
|
||||
|
||||
PointerArithmeticInstruction() {
|
||||
getOpcode() instanceof PointerArithmeticOpcode and
|
||||
elementSize = Construction::getInstructionElementSize(this)
|
||||
elementSize = Raw::getInstructionElementSize(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = elementSize.toString() }
|
||||
@@ -748,7 +765,7 @@ class InheritanceConversionInstruction extends UnaryInstruction {
|
||||
Language::Class derivedClass;
|
||||
|
||||
InheritanceConversionInstruction() {
|
||||
Construction::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
Raw::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
}
|
||||
|
||||
final override string getImmediateString() {
|
||||
@@ -1211,7 +1228,7 @@ class CatchByTypeInstruction extends CatchInstruction {
|
||||
|
||||
CatchByTypeInstruction() {
|
||||
getOpcode() instanceof Opcode::CatchByType and
|
||||
exceptionType = Construction::getInstructionExceptionType(this)
|
||||
exceptionType = Raw::getInstructionExceptionType(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = exceptionType.toString() }
|
||||
@@ -1357,7 +1374,7 @@ class BuiltInOperationInstruction extends Instruction {
|
||||
|
||||
BuiltInOperationInstruction() {
|
||||
getOpcode() instanceof BuiltInOperationOpcode and
|
||||
operation = Construction::getInstructionBuiltInOperation(this)
|
||||
operation = Raw::getInstructionBuiltInOperation(this)
|
||||
}
|
||||
|
||||
final Language::BuiltInOperation getBuiltInOperation() { result = operation }
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase
|
||||
@@ -1,3 +1,4 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import SSAConstruction as Construction
|
||||
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Raw
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import SSAConstructionInternal
|
||||
private import SSAConstructionImports
|
||||
private import SSAConstructionImports as Imports
|
||||
private import Imports::Opcode
|
||||
private import Imports::OperandTag
|
||||
private import Imports::Overlap
|
||||
private import Imports::TInstruction
|
||||
private import Imports::RawIR as RawIR
|
||||
private import SSAInstructions
|
||||
private import NewIR
|
||||
|
||||
private class OldBlock = Reachability::ReachableBlock;
|
||||
@@ -10,54 +16,47 @@ import Cached
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
predicate hasPhiInstructionCached(
|
||||
OldInstruction blockStartInstr, Alias::MemoryLocation defLocation
|
||||
) {
|
||||
exists(OldBlock oldBlock |
|
||||
definitionHasPhiNode(defLocation, oldBlock) and
|
||||
blockStartInstr = oldBlock.getFirstInstruction()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasChiInstructionCached(OldInstruction primaryInstruction) {
|
||||
hasChiNode(_, primaryInstruction)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasUnreachedInstructionCached(IRFunction irFunc) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
irFunc = oldInstruction.getEnclosingIRFunction() and
|
||||
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
|
||||
)
|
||||
}
|
||||
|
||||
class TStageInstruction =
|
||||
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction;
|
||||
|
||||
cached
|
||||
predicate hasInstruction(TStageInstruction instr) {
|
||||
instr instanceof TRawInstruction and instr instanceof OldInstruction
|
||||
or
|
||||
instr instanceof TPhiInstruction
|
||||
or
|
||||
instr instanceof TChiInstruction
|
||||
or
|
||||
instr instanceof TUnreachedInstruction
|
||||
}
|
||||
|
||||
private IRBlock getNewBlock(OldBlock oldBlock) {
|
||||
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
}
|
||||
|
||||
cached
|
||||
predicate functionHasIR(Language::Function func) {
|
||||
exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func)
|
||||
}
|
||||
|
||||
cached
|
||||
OldInstruction getOldInstruction(Instruction instr) { instr = WrappedInstruction(result) }
|
||||
|
||||
private IRVariable getNewIRVariable(OldIR::IRVariable var) {
|
||||
// This is just a type cast. Both classes derive from the same newtype.
|
||||
result = var
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TInstruction =
|
||||
WrappedInstruction(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction
|
||||
} or
|
||||
Phi(OldBlock block, Alias::MemoryLocation defLocation) {
|
||||
definitionHasPhiNode(defLocation, block)
|
||||
} or
|
||||
Chi(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction and
|
||||
hasChiNode(_, oldInstruction)
|
||||
} or
|
||||
Unreached(Language::Function function) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
function = oldInstruction.getEnclosingFunction() and
|
||||
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasTempVariable(
|
||||
Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type
|
||||
) {
|
||||
exists(OldIR::IRTempVariable var |
|
||||
var.getEnclosingFunction() = func and
|
||||
var.getAST() = ast and
|
||||
var.getTag() = tag and
|
||||
var.getLanguageType() = type
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasModeledMemoryResult(Instruction instruction) {
|
||||
exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or
|
||||
@@ -73,7 +72,7 @@ private module Cached {
|
||||
or
|
||||
// Chi instructions track virtual variables, and therefore a chi instruction is
|
||||
// conflated if it's associated with the aliased virtual variable.
|
||||
exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) |
|
||||
exists(OldInstruction oldInstruction | instruction = getChi(oldInstruction) |
|
||||
Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof
|
||||
Alias::AliasedVirtualVariable
|
||||
)
|
||||
@@ -81,7 +80,7 @@ private module Cached {
|
||||
// Phi instructions track locations, and therefore a phi instruction is
|
||||
// conflated if it's associated with a conflated location.
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = Phi(_, location) and
|
||||
instruction = getPhi(_, location) and
|
||||
not exists(location.getAllocation())
|
||||
)
|
||||
}
|
||||
@@ -128,7 +127,7 @@ private module Cached {
|
||||
hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result)
|
||||
)
|
||||
or
|
||||
instruction = Chi(getOldInstruction(result)) and
|
||||
instruction = getChi(getOldInstruction(result)) and
|
||||
tag instanceof ChiPartialOperandTag and
|
||||
overlap instanceof MustExactlyOverlap
|
||||
or
|
||||
@@ -172,13 +171,15 @@ private module Cached {
|
||||
|
||||
pragma[noopt]
|
||||
cached
|
||||
Instruction getPhiOperandDefinition(Phi instr, IRBlock newPredecessorBlock, Overlap overlap) {
|
||||
Instruction getPhiOperandDefinition(
|
||||
PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap
|
||||
) {
|
||||
exists(
|
||||
Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock,
|
||||
OldBlock predBlock, OldBlock defBlock, int defOffset, Alias::MemoryLocation actualDefLocation
|
||||
|
|
||||
hasPhiOperandDefinition(defLocation, useLocation, phiBlock, predBlock, defBlock, defOffset) and
|
||||
instr = Phi(phiBlock, useLocation) and
|
||||
instr = getPhi(phiBlock, useLocation) and
|
||||
newPredecessorBlock = getNewBlock(predBlock) and
|
||||
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
|
||||
overlap = Alias::getOverlap(actualDefLocation, useLocation)
|
||||
@@ -191,7 +192,7 @@ private module Cached {
|
||||
Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation,
|
||||
OldBlock defBlock, int defRank, int defOffset, OldBlock useBlock, int useRank
|
||||
|
|
||||
chiInstr = Chi(oldInstr) and
|
||||
chiInstr = getChi(oldInstr) and
|
||||
vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and
|
||||
hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, oldInstr) and
|
||||
@@ -203,21 +204,11 @@ private module Cached {
|
||||
cached
|
||||
Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
|
||||
exists(OldBlock oldBlock |
|
||||
instr = Phi(oldBlock, _) and
|
||||
instr = getPhi(oldBlock, _) and
|
||||
result = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getConvertedResultExpression()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/*
|
||||
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
|
||||
* that node is its successor in the new successor relation, and the Chi node's successors are
|
||||
@@ -228,20 +219,20 @@ private module Cached {
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
if hasChiNode(_, getOldInstruction(instruction))
|
||||
then
|
||||
result = Chi(getOldInstruction(instruction)) and
|
||||
result = getChi(getOldInstruction(instruction)) and
|
||||
kind instanceof GotoEdge
|
||||
else (
|
||||
exists(OldInstruction oldInstruction |
|
||||
oldInstruction = getOldInstruction(instruction) and
|
||||
(
|
||||
if Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind)
|
||||
then result = Unreached(instruction.getEnclosingFunction())
|
||||
then result = unreachedInstruction(instruction.getEnclosingIRFunction())
|
||||
else result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = Chi(oldInstruction) and
|
||||
instruction = getChi(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
)
|
||||
@@ -260,137 +251,73 @@ private module Cached {
|
||||
// `oldInstruction`, in which case the back edge should come out of the
|
||||
// chi node instead.
|
||||
if hasChiNode(_, oldInstruction)
|
||||
then instruction = Chi(oldInstruction)
|
||||
then instruction = getChi(oldInstruction)
|
||||
else instruction = getNewInstruction(oldInstruction)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::AST getInstructionAST(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction)
|
||||
or
|
||||
instruction = Chi(oldInstruction)
|
||||
|
|
||||
result = oldInstruction.getAST()
|
||||
Language::AST getInstructionAST(Instruction instr) {
|
||||
result = getOldInstruction(instr).getAST()
|
||||
or
|
||||
exists(RawIR::Instruction blockStartInstr |
|
||||
instr = phiInstruction(blockStartInstr, _) and
|
||||
result = blockStartInstr.getAST()
|
||||
)
|
||||
or
|
||||
exists(OldBlock block |
|
||||
instruction = Phi(block, _) and
|
||||
result = block.getFirstInstruction().getAST()
|
||||
exists(RawIR::Instruction primaryInstr |
|
||||
instr = chiInstruction(primaryInstr) and
|
||||
result = primaryInstr.getAST()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(result)
|
||||
exists(IRFunctionBase irFunc |
|
||||
instr = unreachedInstruction(irFunc) and result = irFunc.getFunction()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::LanguageType getInstructionResultType(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction) and
|
||||
result = oldInstruction.getResultLanguageType()
|
||||
Language::LanguageType getInstructionResultType(Instruction instr) {
|
||||
result = instr.(RawIR::Instruction).getResultLanguageType()
|
||||
or
|
||||
exists(Alias::MemoryLocation defLocation |
|
||||
instr = phiInstruction(_, defLocation) and
|
||||
result = defLocation.getType()
|
||||
)
|
||||
or
|
||||
exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar |
|
||||
instruction = Chi(oldInstruction) and
|
||||
hasChiNode(vvar, oldInstruction) and
|
||||
exists(Instruction primaryInstr, Alias::VirtualVariable vvar |
|
||||
instr = chiInstruction(primaryInstr) and
|
||||
hasChiNode(vvar, primaryInstr) and
|
||||
result = vvar.getType()
|
||||
)
|
||||
or
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = Phi(_, location) and
|
||||
result = location.getType()
|
||||
instr = unreachedInstruction(_) and result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instr) {
|
||||
result = getOldInstruction(instr).getOpcode()
|
||||
or
|
||||
instr = phiInstruction(_, _) and result instanceof Opcode::Phi
|
||||
or
|
||||
instr = chiInstruction(_) and result instanceof Opcode::Chi
|
||||
or
|
||||
instr = unreachedInstruction(_) and result instanceof Opcode::Unreached
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunctionBase getInstructionEnclosingIRFunction(Instruction instr) {
|
||||
result = getOldInstruction(instr).getEnclosingIRFunction()
|
||||
or
|
||||
exists(OldInstruction blockStartInstr |
|
||||
instr = phiInstruction(blockStartInstr, _) and
|
||||
result = blockStartInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(_) and
|
||||
result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction) and
|
||||
result = oldInstruction.getOpcode()
|
||||
exists(OldInstruction primaryInstr |
|
||||
instr = chiInstruction(primaryInstr) and result = primaryInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
instruction instanceof Chi and
|
||||
result instanceof Opcode::Chi
|
||||
or
|
||||
instruction instanceof Phi and
|
||||
result instanceof Opcode::Phi
|
||||
or
|
||||
instruction instanceof Unreached and
|
||||
result instanceof Opcode::Unreached
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction)
|
||||
or
|
||||
instruction = Chi(oldInstruction)
|
||||
|
|
||||
result.getFunction() = oldInstruction.getEnclosingFunction()
|
||||
)
|
||||
or
|
||||
exists(OldBlock block |
|
||||
instruction = Phi(block, _) and
|
||||
result.getFunction() = block.getEnclosingFunction()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(result.getFunction())
|
||||
}
|
||||
|
||||
cached
|
||||
IRVariable getInstructionVariable(Instruction instruction) {
|
||||
result =
|
||||
getNewIRVariable(getOldInstruction(instruction).(OldIR::VariableInstruction).getIRVariable())
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Field getInstructionField(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionIndex(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Function getInstructionFunction(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
|
||||
}
|
||||
|
||||
cached
|
||||
string getInstructionConstantValue(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::ConstantValueInstruction).getValue()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
|
||||
result =
|
||||
getOldInstruction(instruction).(OldIR::BuiltInOperationInstruction).getBuiltInOperation()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::LanguageType getInstructionExceptionType(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType()
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionElementSize(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::PointerArithmeticInstruction).getElementSize()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate getInstructionInheritance(
|
||||
Instruction instruction, Language::Class baseClass, Language::Class derivedClass
|
||||
) {
|
||||
exists(OldIR::InheritanceConversionInstruction oldInstr |
|
||||
oldInstr = getOldInstruction(instruction) and
|
||||
baseClass = oldInstr.getBaseClass() and
|
||||
derivedClass = oldInstr.getDerivedClass()
|
||||
)
|
||||
instr = unreachedInstruction(result)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -401,7 +328,7 @@ private module Cached {
|
||||
)
|
||||
or
|
||||
exists(OldIR::Instruction oldInstruction |
|
||||
instruction = Chi(oldInstruction) and
|
||||
instruction = getChi(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction)
|
||||
)
|
||||
}
|
||||
@@ -409,6 +336,14 @@ private module Cached {
|
||||
|
||||
private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr }
|
||||
|
||||
private OldInstruction getOldInstruction(Instruction instr) { instr = result }
|
||||
|
||||
private ChiInstruction getChi(OldInstruction primaryInstr) { result = chiInstruction(primaryInstr) }
|
||||
|
||||
private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) {
|
||||
result = phiInstruction(defBlock.getFirstInstruction(), defLocation)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `def` needs to have a `Chi` instruction inserted after it, to account for a partial definition
|
||||
* of a virtual variable. The `Chi` instruction provides a definition of the entire virtual variable of which the
|
||||
@@ -588,7 +523,7 @@ module DefUse {
|
||||
|
|
||||
// An odd offset corresponds to the `Chi` instruction.
|
||||
defOffset = oldOffset * 2 + 1 and
|
||||
result = Chi(oldInstr) and
|
||||
result = getChi(oldInstr) and
|
||||
(
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr) or
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable()
|
||||
@@ -607,7 +542,7 @@ module DefUse {
|
||||
or
|
||||
defOffset = -1 and
|
||||
hasDefinition(_, defLocation, defBlock, defOffset) and
|
||||
result = Phi(defBlock, defLocation) and
|
||||
result = getPhi(defBlock, defLocation) and
|
||||
actualDefLocation = defLocation
|
||||
}
|
||||
|
||||
@@ -891,7 +826,7 @@ private module CachedForDebugging {
|
||||
)
|
||||
or
|
||||
exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity |
|
||||
instr = Phi(phiBlock, location) and
|
||||
instr = getPhi(phiBlock, location) and
|
||||
result =
|
||||
"Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and
|
||||
if location instanceof Alias::VirtualVariable
|
||||
@@ -901,7 +836,7 @@ private module CachedForDebugging {
|
||||
else specificity = "s"
|
||||
)
|
||||
or
|
||||
instr = Unreached(_) and
|
||||
instr = unreachedInstruction(_) and
|
||||
result = "Unreached"
|
||||
}
|
||||
|
||||
@@ -961,3 +896,19 @@ module SSAConsistency {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the portion of the parameterized IR interface that is used to construct the SSA stages
|
||||
* of the IR. The raw stage of the IR does not expose these predicates.
|
||||
* These predicates are all just aliases for predicates defined in the `Cached` module. This ensures
|
||||
* that all of SSA construction will be evaluated in the same stage.
|
||||
*/
|
||||
module SSA {
|
||||
class MemoryLocation = Alias::MemoryLocation;
|
||||
|
||||
predicate hasPhiInstruction = Cached::hasPhiInstructionCached/2;
|
||||
|
||||
predicate hasChiInstruction = Cached::hasChiInstructionCached/1;
|
||||
|
||||
predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import semmle.code.cpp.ir.implementation.Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
import semmle.code.cpp.ir.implementation.Opcode as Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
import semmle.code.cpp.ir.internal.Overlap as Overlap
|
||||
import semmle.code.cpp.ir.implementation.internal.TInstruction as TInstruction
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as RawIR
|
||||
|
||||
@@ -2,5 +2,6 @@ import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as OldIR
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.ReachableBlock as Reachability
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.Dominance as Dominance
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as NewIR
|
||||
import semmle.code.cpp.ir.implementation.internal.TInstruction::AliasedSSAInstructions as SSAInstructions
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import AliasedSSA as Alias
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Provides a base class, `IRFunctionBase`, for the stage-independent portions of `IRFunction`.
|
||||
*/
|
||||
|
||||
private import IRFunctionBaseInternal
|
||||
|
||||
private newtype TIRFunction =
|
||||
MkIRFunction(Language::Function func) { IRConstruction::Raw::functionHasIR(func) }
|
||||
|
||||
/**
|
||||
* The IR for a function. This base class contains only the predicates that are the same between all
|
||||
* phases of the IR. Each instantiation of `IRFunction` extends this class.
|
||||
*/
|
||||
class IRFunctionBase extends TIRFunction {
|
||||
Language::Function func;
|
||||
|
||||
IRFunctionBase() { this = MkIRFunction(func) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = "IR: " + func.toString() }
|
||||
|
||||
/** Gets the function whose IR is represented. */
|
||||
final Language::Function getFunction() { result = func }
|
||||
|
||||
/** Gets the location of the function. */
|
||||
final Language::Location getLocation() { result = func.getLocation() }
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
|
||||
@@ -1,5 +1,5 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as Construction
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Construction
|
||||
private import semmle.code.cpp.ir.implementation.TempVariableTag as TempVariableTag_
|
||||
|
||||
module Imports {
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
private import TInstructionInternal
|
||||
private import IRFunctionBase
|
||||
private import TInstructionImports as Imports
|
||||
private import Imports::IRType
|
||||
private import Imports::Opcode
|
||||
|
||||
/**
|
||||
* An IR instruction. `TInstruction` is shared across all phases of the IR. There are individual
|
||||
* branches of this type for instructions created directly from the AST (`TRawInstruction`) and for
|
||||
* instructions added by each stage of SSA construction (`T*PhiInstruction`, `T*ChiInstruction`,
|
||||
* `T*UnreachedInstruction`). Each stage then defines a `TStageInstruction` type that is a union of
|
||||
* all of the branches that can appear in that particular stage. The public `Instruction` class for
|
||||
* each phase extends the `TStageInstruction` type for that stage.
|
||||
*/
|
||||
cached
|
||||
newtype TInstruction =
|
||||
TRawInstruction(
|
||||
IRConstruction::Raw::InstructionTag1 tag1, IRConstruction::Raw::InstructionTag2 tag2
|
||||
) {
|
||||
IRConstruction::Raw::hasInstruction(tag1, tag2)
|
||||
} or
|
||||
TUnaliasedSSAPhiInstruction(
|
||||
TRawInstruction blockStartInstr, UnaliasedSSA::SSA::MemoryLocation memoryLocation
|
||||
) {
|
||||
UnaliasedSSA::SSA::hasPhiInstruction(blockStartInstr, memoryLocation)
|
||||
} or
|
||||
TUnaliasedSSAChiInstruction(TRawInstruction primaryInstruction) { none() } or
|
||||
TUnaliasedSSAUnreachedInstruction(IRFunctionBase irFunc) {
|
||||
UnaliasedSSA::SSA::hasUnreachedInstruction(irFunc)
|
||||
} or
|
||||
TAliasedSSAPhiInstruction(
|
||||
TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation
|
||||
) {
|
||||
AliasedSSA::SSA::hasPhiInstruction(blockStartInstr, memoryLocation)
|
||||
} or
|
||||
TAliasedSSAChiInstruction(TRawInstruction primaryInstruction) {
|
||||
AliasedSSA::SSA::hasChiInstruction(primaryInstruction)
|
||||
} or
|
||||
TAliasedSSAUnreachedInstruction(IRFunctionBase irFunc) {
|
||||
AliasedSSA::SSA::hasUnreachedInstruction(irFunc)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides wrappers for the constructors of each branch of `TInstruction` that is used by the
|
||||
* unaliased SSA stage.
|
||||
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
|
||||
* a class alias.
|
||||
*/
|
||||
module UnaliasedSSAInstructions {
|
||||
class TPhiInstruction = TUnaliasedSSAPhiInstruction;
|
||||
|
||||
TPhiInstruction phiInstruction(
|
||||
TRawInstruction blockStartInstr, UnaliasedSSA::SSA::MemoryLocation memoryLocation
|
||||
) {
|
||||
result = TUnaliasedSSAPhiInstruction(blockStartInstr, memoryLocation)
|
||||
}
|
||||
|
||||
class TChiInstruction = TUnaliasedSSAChiInstruction;
|
||||
|
||||
TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
|
||||
result = TUnaliasedSSAChiInstruction(primaryInstruction)
|
||||
}
|
||||
|
||||
class TUnreachedInstruction = TUnaliasedSSAUnreachedInstruction;
|
||||
|
||||
TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) {
|
||||
result = TUnaliasedSSAUnreachedInstruction(irFunc)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides wrappers for the constructors of each branch of `TInstruction` that is used by the
|
||||
* aliased SSA stage.
|
||||
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
|
||||
* a class alias.
|
||||
*/
|
||||
module AliasedSSAInstructions {
|
||||
class TPhiInstruction = TAliasedSSAPhiInstruction;
|
||||
|
||||
TPhiInstruction phiInstruction(
|
||||
TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation
|
||||
) {
|
||||
result = TAliasedSSAPhiInstruction(blockStartInstr, memoryLocation)
|
||||
}
|
||||
|
||||
class TChiInstruction = TAliasedSSAChiInstruction;
|
||||
|
||||
TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
|
||||
result = TAliasedSSAChiInstruction(primaryInstruction)
|
||||
}
|
||||
|
||||
class TUnreachedInstruction = TAliasedSSAUnreachedInstruction;
|
||||
|
||||
TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) {
|
||||
result = TAliasedSSAUnreachedInstruction(irFunc)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.implementation.Opcode as Opcode
|
||||
@@ -0,0 +1,4 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction as UnaliasedSSA
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.internal.SSAConstruction as AliasedSSA
|
||||
@@ -8,10 +8,79 @@ module InstructionConsistency {
|
||||
private import Imports::Overlap
|
||||
private import internal.IRInternal
|
||||
|
||||
private newtype TOptionalIRFunction =
|
||||
TPresentIRFunction(IRFunction irFunc) or
|
||||
TMissingIRFunction()
|
||||
|
||||
/**
|
||||
* An `IRFunction` that might not exist. This is used so that we can produce consistency failures
|
||||
* for IR that also incorrectly lacks a `getEnclosingIRFunction()`.
|
||||
*/
|
||||
abstract private class OptionalIRFunction extends TOptionalIRFunction {
|
||||
abstract string toString();
|
||||
|
||||
abstract Language::Location getLocation();
|
||||
}
|
||||
|
||||
private class PresentIRFunction extends OptionalIRFunction, TPresentIRFunction {
|
||||
private IRFunction irFunc;
|
||||
|
||||
PresentIRFunction() { this = TPresentIRFunction(irFunc) }
|
||||
|
||||
override string toString() {
|
||||
result = concat(Language::getIdentityString(irFunc.getFunction()), "; ")
|
||||
}
|
||||
|
||||
override Language::Location getLocation() {
|
||||
// To avoid an overwhelming number of results when the extractor merges functions with the
|
||||
// same name, just pick a single location.
|
||||
result =
|
||||
rank[1](Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private class MissingIRFunction extends OptionalIRFunction, TMissingIRFunction {
|
||||
override string toString() { result = "<Missing IRFunction>" }
|
||||
|
||||
override Language::Location getLocation() { result instanceof Language::UnknownDefaultLocation }
|
||||
}
|
||||
|
||||
private OptionalIRFunction getInstructionIRFunction(Instruction instr) {
|
||||
result = TPresentIRFunction(instr.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(instr.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private OptionalIRFunction getInstructionIRFunction(Instruction instr, string irFuncText) {
|
||||
result = getInstructionIRFunction(instr) and
|
||||
irFuncText = result.toString()
|
||||
}
|
||||
|
||||
private OptionalIRFunction getOperandIRFunction(Operand operand) {
|
||||
result = TPresentIRFunction(operand.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(operand.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private OptionalIRFunction getOperandIRFunction(Operand operand, string irFuncText) {
|
||||
result = getOperandIRFunction(operand) and
|
||||
irFuncText = result.toString()
|
||||
}
|
||||
|
||||
private OptionalIRFunction getBlockIRFunction(IRBlock block) {
|
||||
result = TPresentIRFunction(block.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(block.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
|
||||
*/
|
||||
query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) {
|
||||
query predicate missingOperand(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag |
|
||||
instr.getOpcode().hasOperand(tag) and
|
||||
not exists(NonPhiOperand operand |
|
||||
@@ -21,32 +90,39 @@ module InstructionConsistency {
|
||||
message =
|
||||
"Instruction '" + instr.getOpcode().toString() +
|
||||
"' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has an unexpected operand with tag `tag`.
|
||||
*/
|
||||
query predicate unexpectedOperand(Instruction instr, OperandTag tag) {
|
||||
exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
not instr.getOpcode().hasOperand(tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (
|
||||
instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
|
||||
) and
|
||||
not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag)
|
||||
query predicate unexpectedOperand(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag |
|
||||
exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
not instr.getOpcode().hasOperand(tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (
|
||||
instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
|
||||
) and
|
||||
not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' has unexpected operand '" + tag.toString() +
|
||||
"' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has multiple operands with tag `tag`.
|
||||
*/
|
||||
query predicate duplicateOperand(
|
||||
Instruction instr, string message, IRFunction func, string funcText
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag, int operandCount |
|
||||
operandCount =
|
||||
@@ -58,8 +134,7 @@ module InstructionConsistency {
|
||||
message =
|
||||
"Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
|
||||
" in function '$@'." and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -67,100 +142,136 @@ module InstructionConsistency {
|
||||
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
|
||||
* the predecessor block `pred`.
|
||||
*/
|
||||
query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
|
||||
pred = instr.getBlock().getAPredecessor() and
|
||||
not exists(PhiInputOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getPredecessorBlock() = pred
|
||||
query predicate missingPhiOperand(
|
||||
PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRBlock pred |
|
||||
pred = instr.getBlock().getAPredecessor() and
|
||||
not exists(PhiInputOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getPredecessorBlock() = pred
|
||||
) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is missing an operand for predecessor block '" +
|
||||
pred.toString() + "' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingOperandType(Operand operand, string message) {
|
||||
exists(Language::Function func, Instruction use |
|
||||
query predicate missingOperandType(
|
||||
Operand operand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Instruction use |
|
||||
not exists(operand.getType()) and
|
||||
use = operand.getUse() and
|
||||
func = use.getEnclosingFunction() and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
|
||||
"' missing type in function '" + Language::getIdentityString(func) + "'."
|
||||
"' is missing a type in function '$@'." and
|
||||
irFunc = getOperandIRFunction(operand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate duplicateChiOperand(
|
||||
ChiInstruction chi, string message, IRFunction func, string funcText
|
||||
ChiInstruction chi, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
chi.getTotal() = chi.getPartial() and
|
||||
message =
|
||||
"Chi instruction for " + chi.getPartial().toString() +
|
||||
" has duplicate operands in function $@" and
|
||||
func = chi.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
" has duplicate operands in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(chi, irFuncText)
|
||||
}
|
||||
|
||||
query predicate sideEffectWithoutPrimary(
|
||||
SideEffectInstruction instr, string message, IRFunction func, string funcText
|
||||
SideEffectInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(instr.getPrimaryInstruction()) and
|
||||
message = "Side effect instruction missing primary instruction in function $@" and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
message =
|
||||
"Side effect instruction '" + instr + "' is missing a primary instruction in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
||||
*/
|
||||
query predicate instructionWithoutSuccessor(Instruction instr) {
|
||||
query predicate instructionWithoutSuccessor(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(instr.getASuccessor()) and
|
||||
not instr instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not instr instanceof PhiInstruction and
|
||||
not instr instanceof UnreachedInstruction
|
||||
not instr instanceof UnreachedInstruction and
|
||||
message = "Instruction '" + instr.toString() + "' has no successors in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there are multiple (`n`) edges of kind `kind` from `source`,
|
||||
* where `target` is among the targets of those edges.
|
||||
* Holds if there are multiple edges of the same kind from `source`.
|
||||
*/
|
||||
query predicate ambiguousSuccessors(Instruction source, EdgeKind kind, int n, Instruction target) {
|
||||
n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
|
||||
n > 1 and
|
||||
source.getSuccessor(kind) = target
|
||||
query predicate ambiguousSuccessors(
|
||||
Instruction source, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(EdgeKind kind, int n |
|
||||
n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
|
||||
n > 1 and
|
||||
message =
|
||||
"Instruction '" + source.toString() + "' has " + n.toString() + " successors of kind '" +
|
||||
kind.toString() + "' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(source, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` in `f` is part of a loop even though the AST of `f`
|
||||
* Holds if `instr` is part of a loop even though the AST of `instr`'s enclosing function
|
||||
* contains no element that can cause loops.
|
||||
*/
|
||||
query predicate unexplainedLoop(Language::Function f, Instruction instr) {
|
||||
exists(IRBlock block |
|
||||
instr.getBlock() = block and
|
||||
block.getEnclosingFunction() = f and
|
||||
block.getASuccessor+() = block
|
||||
) and
|
||||
not Language::hasPotentialLoop(f)
|
||||
query predicate unexplainedLoop(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Language::Function f |
|
||||
exists(IRBlock block |
|
||||
instr.getBlock() = block and
|
||||
block.getEnclosingFunction() = f and
|
||||
block.getASuccessor+() = block
|
||||
) and
|
||||
not Language::hasPotentialLoop(f) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is part of an unexplained loop in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `Phi` instruction is present in a block with fewer than two
|
||||
* predecessors.
|
||||
*/
|
||||
query predicate unnecessaryPhiInstruction(PhiInstruction instr) {
|
||||
count(instr.getBlock().getAPredecessor()) < 2
|
||||
query predicate unnecessaryPhiInstruction(
|
||||
PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int n |
|
||||
n = count(instr.getBlock().getAPredecessor()) and
|
||||
n < 2 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is in a block with only " + n.toString() +
|
||||
" predecessors in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a memory operand is connected to a definition with an unmodeled result.
|
||||
*/
|
||||
query predicate memoryOperandDefinitionIsUnmodeled(
|
||||
Instruction instr, string message, IRFunction func, string funcText
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(MemoryOperand operand, Instruction def |
|
||||
operand = instr.getAnOperand() and
|
||||
def = operand.getAnyDef() and
|
||||
not def.isResultModeled() and
|
||||
message = "Memory operand definition has unmodeled result in function '$@'" and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
message =
|
||||
"Memory operand definition on instruction '" + instr.toString() +
|
||||
"' has unmodeled result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -168,18 +279,37 @@ module InstructionConsistency {
|
||||
* Holds if operand `operand` consumes a value that was defined in
|
||||
* a different function.
|
||||
*/
|
||||
query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) {
|
||||
operand.getUse() = instr and
|
||||
operand.getAnyDef() = defInstr and
|
||||
instr.getEnclosingIRFunction() != defInstr.getEnclosingIRFunction()
|
||||
query predicate operandAcrossFunctions(
|
||||
Operand operand, string message, OptionalIRFunction useIRFunc, string useIRFuncText,
|
||||
OptionalIRFunction defIRFunc, string defIRFuncText
|
||||
) {
|
||||
exists(Instruction useInstr, Instruction defInstr |
|
||||
operand.getUse() = useInstr and
|
||||
operand.getAnyDef() = defInstr and
|
||||
useIRFunc = getInstructionIRFunction(useInstr, useIRFuncText) and
|
||||
defIRFunc = getInstructionIRFunction(defInstr, defIRFuncText) and
|
||||
useIRFunc != defIRFunc and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' is used on instruction '" + useInstr.toString() +
|
||||
"' in function '$@', but is defined on instruction '" + defInstr.toString() +
|
||||
"' in function '$@'."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is not in exactly one block.
|
||||
*/
|
||||
query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) {
|
||||
blockCount = count(instr.getBlock()) and
|
||||
blockCount != 1
|
||||
query predicate instructionWithoutUniqueBlock(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int blockCount |
|
||||
blockCount = count(instr.getBlock()) and
|
||||
blockCount != 1 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is a member of " + blockCount.toString() +
|
||||
" blocks in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate forwardEdge(IRBlock b1, IRBlock b2) {
|
||||
@@ -192,10 +322,11 @@ module InstructionConsistency {
|
||||
*
|
||||
* This check ensures we don't have too _few_ back edges.
|
||||
*/
|
||||
query predicate containsLoopOfForwardEdges(IRFunction f) {
|
||||
query predicate containsLoopOfForwardEdges(IRFunction f, string message) {
|
||||
exists(IRBlock block |
|
||||
forwardEdge+(block, block) and
|
||||
block.getEnclosingIRFunction() = f
|
||||
block.getEnclosingIRFunction() = f and
|
||||
message = "Function contains a loop consisting of only forward edges."
|
||||
)
|
||||
}
|
||||
|
||||
@@ -207,12 +338,19 @@ module InstructionConsistency {
|
||||
*
|
||||
* This check ensures we don't have too _many_ back edges.
|
||||
*/
|
||||
query predicate lostReachability(IRBlock block) {
|
||||
query predicate lostReachability(
|
||||
IRBlock block, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRFunction f, IRBlock entry |
|
||||
entry = f.getEntryBlock() and
|
||||
entry.getASuccessor+() = block and
|
||||
not forwardEdge+(entry, block) and
|
||||
not Language::hasGoto(f.getFunction())
|
||||
not Language::hasGoto(f.getFunction()) and
|
||||
message =
|
||||
"Block '" + block.toString() +
|
||||
"' is not reachable by traversing only forward edges in function '$@'." and
|
||||
irFunc = TPresentIRFunction(f) and
|
||||
irFuncText = irFunc.toString()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -220,16 +358,22 @@ module InstructionConsistency {
|
||||
* Holds if the number of back edges differs between the `Instruction` graph
|
||||
* and the `IRBlock` graph.
|
||||
*/
|
||||
query predicate backEdgeCountMismatch(Language::Function f, int fromInstr, int fromBlock) {
|
||||
fromInstr =
|
||||
count(Instruction i1, Instruction i2 |
|
||||
i1.getEnclosingFunction() = f and i1.getBackEdgeSuccessor(_) = i2
|
||||
) and
|
||||
fromBlock =
|
||||
count(IRBlock b1, IRBlock b2 |
|
||||
b1.getEnclosingFunction() = f and b1.getBackEdgeSuccessor(_) = b2
|
||||
) and
|
||||
fromInstr != fromBlock
|
||||
query predicate backEdgeCountMismatch(OptionalIRFunction irFunc, string message) {
|
||||
exists(int fromInstr, int fromBlock |
|
||||
fromInstr =
|
||||
count(Instruction i1, Instruction i2 |
|
||||
getInstructionIRFunction(i1) = irFunc and i1.getBackEdgeSuccessor(_) = i2
|
||||
) and
|
||||
fromBlock =
|
||||
count(IRBlock b1, IRBlock b2 |
|
||||
getBlockIRFunction(b1) = irFunc and b1.getBackEdgeSuccessor(_) = b2
|
||||
) and
|
||||
fromInstr != fromBlock and
|
||||
message =
|
||||
"The instruction graph for function '" + irFunc.toString() + "' contains " +
|
||||
fromInstr.toString() + " back edges, but the block graph contains " + fromBlock.toString()
|
||||
+ " back edges."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,7 +395,7 @@ module InstructionConsistency {
|
||||
* Holds if `useOperand` has a definition that does not dominate the use.
|
||||
*/
|
||||
query predicate useNotDominatedByDefinition(
|
||||
Operand useOperand, string message, IRFunction func, string funcText
|
||||
Operand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
|
||||
pointOfEvaluation(useOperand, useBlock, useIndex) and
|
||||
@@ -272,19 +416,17 @@ module InstructionConsistency {
|
||||
message =
|
||||
"Operand '" + useOperand.toString() +
|
||||
"' is not dominated by its definition in function '$@'." and
|
||||
func = useOperand.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
irFunc = getOperandIRFunction(useOperand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate switchInstructionWithoutDefaultEdge(
|
||||
SwitchInstruction switchInstr, string message, IRFunction func, string funcText
|
||||
SwitchInstruction switchInstr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(switchInstr.getDefaultSuccessor()) and
|
||||
message =
|
||||
"SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and
|
||||
func = switchInstr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
irFunc = getInstructionIRFunction(switchInstr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -305,18 +447,30 @@ module InstructionConsistency {
|
||||
instr.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
query predicate notMarkedAsConflated(Instruction instr) {
|
||||
query predicate notMarkedAsConflated(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
shouldBeConflated(instr) and
|
||||
not instr.isResultConflated()
|
||||
not instr.isResultConflated() and
|
||||
message =
|
||||
"Instruction '" + instr.toString() +
|
||||
"' should be marked as having a conflated result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
query predicate wronglyMarkedAsConflated(Instruction instr) {
|
||||
query predicate wronglyMarkedAsConflated(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
instr.isResultConflated() and
|
||||
not shouldBeConflated(instr)
|
||||
not shouldBeConflated(instr) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() +
|
||||
"' should not be marked as having a conflated result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
query predicate invalidOverlap(
|
||||
MemoryOperand useOperand, string message, IRFunction func, string funcText
|
||||
MemoryOperand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Overlap overlap |
|
||||
overlap = useOperand.getDefinitionOverlap() and
|
||||
@@ -324,8 +478,20 @@ module InstructionConsistency {
|
||||
message =
|
||||
"MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
|
||||
overlap.toString() + "'." and
|
||||
func = useOperand.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
irFunc = getOperandIRFunction(useOperand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate nonUniqueEnclosingIRFunction(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int irFuncCount |
|
||||
irFuncCount = count(instr.getEnclosingIRFunction()) and
|
||||
irFuncCount != 1 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' has " + irFuncCount.toString() +
|
||||
" results for `getEnclosingIRFunction()` in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,12 @@
|
||||
private import internal.IRInternal
|
||||
private import internal.IRFunctionImports as Imports
|
||||
import Imports::IRFunctionBase
|
||||
import Instruction
|
||||
|
||||
private newtype TIRFunction =
|
||||
MkIRFunction(Language::Function func) { Construction::functionHasIR(func) }
|
||||
|
||||
/**
|
||||
* Represents the IR for a function.
|
||||
* The IR for a function.
|
||||
*/
|
||||
class IRFunction extends TIRFunction {
|
||||
Language::Function func;
|
||||
|
||||
IRFunction() { this = MkIRFunction(func) }
|
||||
|
||||
final string toString() { result = "IR: " + func.toString() }
|
||||
|
||||
/**
|
||||
* Gets the function whose IR is represented.
|
||||
*/
|
||||
final Language::Function getFunction() { result = func }
|
||||
|
||||
/**
|
||||
* Gets the location of the function.
|
||||
*/
|
||||
final Language::Location getLocation() { result = func.getLocation() }
|
||||
|
||||
class IRFunction extends IRFunctionBase {
|
||||
/**
|
||||
* Gets the entry point for this function.
|
||||
*/
|
||||
|
||||
@@ -217,19 +217,23 @@ class IRThrowVariable extends IRTempVariable {
|
||||
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
|
||||
* function that accepts a variable number of arguments.
|
||||
*/
|
||||
class IREllipsisVariable extends IRTempVariable {
|
||||
class IREllipsisVariable extends IRTempVariable, IRParameter {
|
||||
IREllipsisVariable() { tag = EllipsisTempVar() }
|
||||
|
||||
final override string toString() { result = "#ellipsis" }
|
||||
|
||||
final override int getIndex() { result = func.getNumberOfParameters() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A temporary variable generated to hold the `this` pointer.
|
||||
*/
|
||||
class IRThisVariable extends IRTempVariable {
|
||||
class IRThisVariable extends IRTempVariable, IRParameter {
|
||||
IRThisVariable() { tag = ThisTempVar() }
|
||||
|
||||
final override string toString() { result = "#this" }
|
||||
|
||||
final override int getIndex() { result = -1 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -274,3 +278,29 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial
|
||||
|
||||
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR variable which acts like a function parameter, including positional parameters and the
|
||||
* temporary variables generated for `this` and ellipsis parameters.
|
||||
*/
|
||||
class IRParameter extends IRAutomaticVariable {
|
||||
IRParameter() {
|
||||
this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter
|
||||
or
|
||||
this = TIRTempVariable(_, _, ThisTempVar(), _)
|
||||
or
|
||||
this = TIRTempVariable(_, _, EllipsisTempVar(), _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the zero-based index of this parameter. The `this` parameter has index -1.
|
||||
*/
|
||||
int getIndex() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR variable representing a positional parameter.
|
||||
*/
|
||||
class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable {
|
||||
final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() }
|
||||
}
|
||||
|
||||
@@ -29,7 +29,13 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil
|
||||
/**
|
||||
* Represents a single operation in the IR.
|
||||
*/
|
||||
class Instruction extends Construction::TInstruction {
|
||||
class Instruction extends Construction::TStageInstruction {
|
||||
Instruction() {
|
||||
// The base `TStageInstruction` type is a superset of the actual instructions appearing in this
|
||||
// stage. This call lets the stage filter out the ones that are not reused from raw IR.
|
||||
Construction::hasInstruction(this)
|
||||
}
|
||||
|
||||
final string toString() { result = getOpcode().toString() + ": " + getAST().toString() }
|
||||
|
||||
/**
|
||||
@@ -194,14 +200,14 @@ class Instruction extends Construction::TInstruction {
|
||||
* conversion.
|
||||
*/
|
||||
final Language::Expr getConvertedResultExpression() {
|
||||
result = Construction::getInstructionConvertedResultExpression(this)
|
||||
result = Raw::getInstructionConvertedResultExpression(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any.
|
||||
*/
|
||||
final Language::Expr getUnconvertedResultExpression() {
|
||||
result = Construction::getInstructionUnconvertedResultExpression(this)
|
||||
result = Raw::getInstructionUnconvertedResultExpression(this)
|
||||
}
|
||||
|
||||
final Language::LanguageType getResultLanguageType() {
|
||||
@@ -212,6 +218,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* Gets the type of the result produced by this instruction. If the instruction does not produce
|
||||
* a result, its result type will be `IRVoidType`.
|
||||
*/
|
||||
cached
|
||||
final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
@@ -250,7 +257,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* result of the `Load` instruction is a prvalue of type `int`, representing
|
||||
* the integer value loaded from variable `x`.
|
||||
*/
|
||||
final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
|
||||
final predicate isGLValue() { getResultLanguageType().hasType(_, true) }
|
||||
|
||||
/**
|
||||
* Gets the size of the result produced by this instruction, in bytes. If the
|
||||
@@ -259,7 +266,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* If `this.isGLValue()` holds for this instruction, the value of
|
||||
* `getResultSize()` will always be the size of a pointer.
|
||||
*/
|
||||
final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
|
||||
final int getResultSize() { result = getResultLanguageType().getByteSize() }
|
||||
|
||||
/**
|
||||
* Gets the opcode that specifies the operation performed by this instruction.
|
||||
@@ -395,7 +402,7 @@ class Instruction extends Construction::TInstruction {
|
||||
class VariableInstruction extends Instruction {
|
||||
IRVariable var;
|
||||
|
||||
VariableInstruction() { var = Construction::getInstructionVariable(this) }
|
||||
VariableInstruction() { var = Raw::getInstructionVariable(this) }
|
||||
|
||||
override string getImmediateString() { result = var.toString() }
|
||||
|
||||
@@ -410,7 +417,7 @@ class VariableInstruction extends Instruction {
|
||||
class FieldInstruction extends Instruction {
|
||||
Language::Field field;
|
||||
|
||||
FieldInstruction() { field = Construction::getInstructionField(this) }
|
||||
FieldInstruction() { field = Raw::getInstructionField(this) }
|
||||
|
||||
final override string getImmediateString() { result = field.toString() }
|
||||
|
||||
@@ -420,7 +427,7 @@ class FieldInstruction extends Instruction {
|
||||
class FunctionInstruction extends Instruction {
|
||||
Language::Function funcSymbol;
|
||||
|
||||
FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) }
|
||||
FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }
|
||||
|
||||
final override string getImmediateString() { result = funcSymbol.toString() }
|
||||
|
||||
@@ -430,7 +437,7 @@ class FunctionInstruction extends Instruction {
|
||||
class ConstantValueInstruction extends Instruction {
|
||||
string value;
|
||||
|
||||
ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) }
|
||||
ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) }
|
||||
|
||||
final override string getImmediateString() { result = value }
|
||||
|
||||
@@ -440,7 +447,7 @@ class ConstantValueInstruction extends Instruction {
|
||||
class IndexedInstruction extends Instruction {
|
||||
int index;
|
||||
|
||||
IndexedInstruction() { index = Construction::getInstructionIndex(this) }
|
||||
IndexedInstruction() { index = Raw::getInstructionIndex(this) }
|
||||
|
||||
final override string getImmediateString() { result = index.toString() }
|
||||
|
||||
@@ -541,6 +548,11 @@ class ReturnIndirectionInstruction extends VariableInstruction {
|
||||
* function.
|
||||
*/
|
||||
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
|
||||
|
||||
/**
|
||||
* Holds if this instruction is the return indirection for `this`.
|
||||
*/
|
||||
final predicate isThisIndirection() { var instanceof IRThisVariable }
|
||||
}
|
||||
|
||||
class CopyInstruction extends Instruction {
|
||||
@@ -598,11 +610,16 @@ class ConstantInstruction extends ConstantValueInstruction {
|
||||
}
|
||||
|
||||
class IntegerConstantInstruction extends ConstantInstruction {
|
||||
IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType }
|
||||
IntegerConstantInstruction() {
|
||||
exists(IRType resultType |
|
||||
resultType = getResultIRType() and
|
||||
(resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class FloatConstantInstruction extends ConstantInstruction {
|
||||
FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
|
||||
FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType }
|
||||
}
|
||||
|
||||
class StringConstantInstruction extends VariableInstruction {
|
||||
@@ -699,7 +716,7 @@ class PointerArithmeticInstruction extends BinaryInstruction {
|
||||
|
||||
PointerArithmeticInstruction() {
|
||||
getOpcode() instanceof PointerArithmeticOpcode and
|
||||
elementSize = Construction::getInstructionElementSize(this)
|
||||
elementSize = Raw::getInstructionElementSize(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = elementSize.toString() }
|
||||
@@ -748,7 +765,7 @@ class InheritanceConversionInstruction extends UnaryInstruction {
|
||||
Language::Class derivedClass;
|
||||
|
||||
InheritanceConversionInstruction() {
|
||||
Construction::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
Raw::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
}
|
||||
|
||||
final override string getImmediateString() {
|
||||
@@ -1211,7 +1228,7 @@ class CatchByTypeInstruction extends CatchInstruction {
|
||||
|
||||
CatchByTypeInstruction() {
|
||||
getOpcode() instanceof Opcode::CatchByType and
|
||||
exceptionType = Construction::getInstructionExceptionType(this)
|
||||
exceptionType = Raw::getInstructionExceptionType(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = exceptionType.toString() }
|
||||
@@ -1357,7 +1374,7 @@ class BuiltInOperationInstruction extends Instruction {
|
||||
|
||||
BuiltInOperationInstruction() {
|
||||
getOpcode() instanceof BuiltInOperationOpcode and
|
||||
operation = Construction::getInstructionBuiltInOperation(this)
|
||||
operation = Raw::getInstructionBuiltInOperation(this)
|
||||
}
|
||||
|
||||
final Language::BuiltInOperation getBuiltInOperation() { result = operation }
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
private import cpp
|
||||
import semmle.code.cpp.ir.implementation.raw.IR
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.implementation.internal.IRFunctionBase
|
||||
private import semmle.code.cpp.ir.implementation.internal.TInstruction
|
||||
private import semmle.code.cpp.ir.implementation.internal.TIRVariable
|
||||
private import semmle.code.cpp.ir.internal.CppType
|
||||
private import semmle.code.cpp.ir.internal.Overlap
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
@@ -12,34 +15,36 @@ private import TranslatedStmt
|
||||
private import TranslatedFunction
|
||||
|
||||
TranslatedElement getInstructionTranslatedElement(Instruction instruction) {
|
||||
instruction = MkInstruction(result, _)
|
||||
instruction = TRawInstruction(result, _)
|
||||
}
|
||||
|
||||
InstructionTag getInstructionTag(Instruction instruction) { instruction = MkInstruction(_, result) }
|
||||
|
||||
import Cached
|
||||
InstructionTag getInstructionTag(Instruction instruction) {
|
||||
instruction = TRawInstruction(_, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the portion of the parameterized IR interface that is used to construct the initial
|
||||
* "raw" stage of the IR. The other stages of the IR do not expose these predicates.
|
||||
*/
|
||||
cached
|
||||
private module Cached {
|
||||
module Raw {
|
||||
class InstructionTag1 = TranslatedElement;
|
||||
|
||||
class InstructionTag2 = InstructionTag;
|
||||
|
||||
cached
|
||||
predicate functionHasIR(Function func) { exists(getTranslatedFunction(func)) }
|
||||
|
||||
cached
|
||||
newtype TInstruction =
|
||||
MkInstruction(TranslatedElement element, InstructionTag tag) {
|
||||
element.hasInstruction(_, tag, _)
|
||||
}
|
||||
predicate hasInstruction(TranslatedElement element, InstructionTag tag) {
|
||||
element.hasInstruction(_, tag, _)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasUserVariable(Function func, Variable var, CppType type) {
|
||||
getTranslatedFunction(func).hasUserVariable(var, type)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasThisVariable(Function func, CppType type) {
|
||||
type = getTypeForGLValue(getTranslatedFunction(func).getThisType())
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, CppType type) {
|
||||
exists(TranslatedElement element |
|
||||
@@ -64,232 +69,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasModeledMemoryResult(Instruction instruction) { none() }
|
||||
|
||||
cached
|
||||
predicate hasConflatedMemoryResult(Instruction instruction) {
|
||||
instruction instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
cached
|
||||
Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
exists(TranslatedExpr translatedExpr |
|
||||
translatedExpr = getTranslatedExpr(result) and
|
||||
instruction = translatedExpr.getResult() and
|
||||
// Only associate `instruction` with this expression if the translated
|
||||
// expression actually produced the instruction; not if it merely
|
||||
// forwarded the result of another translated expression.
|
||||
instruction = translatedExpr.getInstruction(_)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
result = getInstructionConvertedResultExpression(instruction).getUnconverted()
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionRegisterOperand(getInstructionTag(instruction), tag)
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getMemoryOperandDefinition(
|
||||
Instruction instruction, MemoryOperandTag tag, Overlap overlap
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets a non-phi instruction that defines an operand of `instr`. */
|
||||
private Instruction getNonPhiOperandDef(Instruction instr) {
|
||||
result = getRegisterOperandDefinition(instr, _)
|
||||
or
|
||||
result = getMemoryOperandDefinition(instr, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a non-phi instruction that defines an operand of `instr` but only if
|
||||
* both `instr` and the result have neighbor on the other side of the edge
|
||||
* between them. This is a necessary condition for being in a cycle, and it
|
||||
* removes about two thirds of the tuples that would otherwise be in this
|
||||
* predicate.
|
||||
*/
|
||||
private Instruction getNonPhiOperandDefOfIntermediate(Instruction instr) {
|
||||
result = getNonPhiOperandDef(instr) and
|
||||
exists(getNonPhiOperandDef(result)) and
|
||||
instr = getNonPhiOperandDef(_)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
|
||||
* through a phi instruction and therefore should be impossible.
|
||||
*
|
||||
* If such cycles are present, either due to a programming error in the IR
|
||||
* generation or due to a malformed database, it can cause infinite loops in
|
||||
* analyses that assume a cycle-free graph of non-phi operands. Therefore it's
|
||||
* better to remove these operands than to leave cycles in the operand graph.
|
||||
*/
|
||||
pragma[noopt]
|
||||
cached
|
||||
predicate isInCycle(Instruction instr) {
|
||||
instr instanceof Instruction and
|
||||
getNonPhiOperandDefOfIntermediate+(instr) = instr
|
||||
}
|
||||
|
||||
cached
|
||||
CppType getInstructionOperandType(Instruction instruction, TypedOperandTag tag) {
|
||||
// For all `LoadInstruction`s, the operand type of the `LoadOperand` is the same as
|
||||
// the result type of the load.
|
||||
tag instanceof LoadOperandTag and
|
||||
result = instruction.(LoadInstruction).getResultLanguageType()
|
||||
or
|
||||
not instruction instanceof LoadInstruction and
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionMemoryOperandType(getInstructionTag(instruction), tag)
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getPhiOperandDefinition(
|
||||
PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getPhiInstructionBlockStart(PhiInstruction instr) { none() }
|
||||
|
||||
cached
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionSuccessor(getInstructionTag(instruction), kind)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the CFG edge (`sourceElement`, `sourceTag`) ---`kind`-->
|
||||
* `targetInstruction` is a back edge under the condition that
|
||||
* `requiredAncestor` is an ancestor of `sourceElement`.
|
||||
*/
|
||||
private predicate backEdgeCandidate(
|
||||
TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor,
|
||||
Instruction targetInstruction, EdgeKind kind
|
||||
) {
|
||||
// While loop:
|
||||
// Any edge from within the body of the loop to the condition of the loop
|
||||
// is a back edge. This includes edges from `continue` and the fall-through
|
||||
// edge(s) after the last instruction(s) in the body.
|
||||
exists(TranslatedWhileStmt s |
|
||||
targetInstruction = s.getFirstConditionInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
requiredAncestor = s.getBody()
|
||||
)
|
||||
or
|
||||
// Do-while loop:
|
||||
// The back edge should be the edge(s) from the condition to the
|
||||
// body. This ensures that it's the back edge that will be pruned in a `do
|
||||
// { ... } while (0)` statement. Note that all `continue` statements in a
|
||||
// do-while loop produce forward edges.
|
||||
exists(TranslatedDoStmt s |
|
||||
targetInstruction = s.getBody().getFirstInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
requiredAncestor = s.getCondition()
|
||||
)
|
||||
or
|
||||
// For loop:
|
||||
// Any edge from within the body or update of the loop to the condition of
|
||||
// the loop is a back edge. When there is no loop update expression, this
|
||||
// includes edges from `continue` and the fall-through edge(s) after the
|
||||
// last instruction(s) in the body. A for loop may not have a condition, in
|
||||
// which case `getFirstConditionInstruction` returns the body instead.
|
||||
exists(TranslatedForStmt s |
|
||||
targetInstruction = s.getFirstConditionInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
(
|
||||
requiredAncestor = s.getUpdate()
|
||||
or
|
||||
not exists(s.getUpdate()) and
|
||||
requiredAncestor = s.getBody()
|
||||
)
|
||||
)
|
||||
or
|
||||
// Range-based for loop:
|
||||
// Any edge from within the update of the loop to the condition of
|
||||
// the loop is a back edge.
|
||||
exists(TranslatedRangeBasedForStmt s |
|
||||
targetInstruction = s.getCondition().getFirstInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
requiredAncestor = s.getUpdate()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate jumpSourceHasAncestor(TranslatedElement jumpSource, TranslatedElement ancestor) {
|
||||
backEdgeCandidate(jumpSource, _, _, _, _) and
|
||||
ancestor = jumpSource
|
||||
or
|
||||
// For performance, we don't want a fastTC here
|
||||
jumpSourceHasAncestor(jumpSource, ancestor.getAChild())
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getInstructionBackEdgeSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
exists(
|
||||
TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor
|
||||
|
|
||||
backEdgeCandidate(sourceElement, sourceTag, requiredAncestor, result, kind) and
|
||||
jumpSourceHasAncestor(sourceElement, requiredAncestor) and
|
||||
instruction = sourceElement.getInstruction(sourceTag)
|
||||
)
|
||||
or
|
||||
// Goto statement:
|
||||
// As a conservative approximation, any edge out of `goto` is a back edge
|
||||
// unless it goes strictly forward in the program text. A `goto` whose
|
||||
// source and target are both inside a macro will be seen as having the
|
||||
// same location for source and target, so we conservatively assume that
|
||||
// such a `goto` creates a back edge.
|
||||
exists(TranslatedElement s, GotoStmt goto |
|
||||
not isStrictlyForwardGoto(goto) and
|
||||
goto = s.getAST() and
|
||||
exists(InstructionTag tag |
|
||||
result = s.getInstructionSuccessor(tag, kind) and
|
||||
instruction = s.getInstruction(tag)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `goto` jumps strictly forward in the program text. */
|
||||
private predicate isStrictlyForwardGoto(GotoStmt goto) {
|
||||
goto.getLocation().isBefore(goto.getTarget().getLocation())
|
||||
}
|
||||
|
||||
cached
|
||||
Locatable getInstructionAST(Instruction instruction) {
|
||||
result = getInstructionTranslatedElement(instruction).getAST()
|
||||
}
|
||||
|
||||
cached
|
||||
CppType getInstructionResultType(Instruction instruction) {
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.hasInstruction(_, getInstructionTag(instruction), result)
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instruction) {
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.hasInstruction(result, getInstructionTag(instruction), _)
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
|
||||
result.getFunction() = getInstructionTranslatedElement(instruction).getFunction()
|
||||
}
|
||||
|
||||
cached
|
||||
IRVariable getInstructionVariable(Instruction instruction) {
|
||||
TIRVariable getInstructionVariable(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
element = getInstructionTranslatedElement(instruction) and
|
||||
tag = getInstructionTag(instruction) and
|
||||
@@ -302,10 +82,9 @@ private module Cached {
|
||||
|
||||
cached
|
||||
Field getInstructionField(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionField(tag)
|
||||
)
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionField(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -324,10 +103,9 @@ private module Cached {
|
||||
|
||||
cached
|
||||
int getInstructionIndex(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionIndex(tag)
|
||||
)
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionIndex(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -350,20 +128,11 @@ private module Cached {
|
||||
.getInstructionInheritance(getInstructionTag(instruction), baseClass, derivedClass)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate instructionOrigin(
|
||||
Instruction instruction, TranslatedElement element, InstructionTag tag
|
||||
) {
|
||||
element = getInstructionTranslatedElement(instruction) and
|
||||
tag = getInstructionTag(instruction)
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionElementSize(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionElementSize(tag)
|
||||
)
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionElementSize(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -372,22 +141,225 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionResultSize(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionResultSize(tag)
|
||||
Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
exists(TranslatedExpr translatedExpr |
|
||||
translatedExpr = getTranslatedExpr(result) and
|
||||
instruction = translatedExpr.getResult() and
|
||||
// Only associate `instruction` with this expression if the translated
|
||||
// expression actually produced the instruction; not if it merely
|
||||
// forwarded the result of another translated expression.
|
||||
instruction = translatedExpr.getInstruction(_)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getPrimaryInstructionForSideEffect(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getPrimaryInstructionForSideEffect(tag)
|
||||
)
|
||||
Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
result = getInstructionConvertedResultExpression(instruction).getUnconverted()
|
||||
}
|
||||
}
|
||||
|
||||
class TStageInstruction = TRawInstruction;
|
||||
|
||||
predicate hasInstruction(TRawInstruction instr) { any() }
|
||||
|
||||
predicate hasModeledMemoryResult(Instruction instruction) { none() }
|
||||
|
||||
predicate hasConflatedMemoryResult(Instruction instruction) {
|
||||
instruction instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionRegisterOperand(getInstructionTag(instruction), tag)
|
||||
}
|
||||
|
||||
Instruction getMemoryOperandDefinition(
|
||||
Instruction instruction, MemoryOperandTag tag, Overlap overlap
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets a non-phi instruction that defines an operand of `instr`. */
|
||||
private Instruction getNonPhiOperandDef(Instruction instr) {
|
||||
result = getRegisterOperandDefinition(instr, _)
|
||||
or
|
||||
result = getMemoryOperandDefinition(instr, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a non-phi instruction that defines an operand of `instr` but only if
|
||||
* both `instr` and the result have neighbor on the other side of the edge
|
||||
* between them. This is a necessary condition for being in a cycle, and it
|
||||
* removes about two thirds of the tuples that would otherwise be in this
|
||||
* predicate.
|
||||
*/
|
||||
private Instruction getNonPhiOperandDefOfIntermediate(Instruction instr) {
|
||||
result = getNonPhiOperandDef(instr) and
|
||||
exists(getNonPhiOperandDef(result)) and
|
||||
instr = getNonPhiOperandDef(_)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
|
||||
* through a phi instruction and therefore should be impossible.
|
||||
*
|
||||
* If such cycles are present, either due to a programming error in the IR
|
||||
* generation or due to a malformed database, it can cause infinite loops in
|
||||
* analyses that assume a cycle-free graph of non-phi operands. Therefore it's
|
||||
* better to remove these operands than to leave cycles in the operand graph.
|
||||
*/
|
||||
pragma[noopt]
|
||||
predicate isInCycle(Instruction instr) {
|
||||
instr instanceof Instruction and
|
||||
getNonPhiOperandDefOfIntermediate+(instr) = instr
|
||||
}
|
||||
|
||||
CppType getInstructionOperandType(Instruction instruction, TypedOperandTag tag) {
|
||||
// For all `LoadInstruction`s, the operand type of the `LoadOperand` is the same as
|
||||
// the result type of the load.
|
||||
tag instanceof LoadOperandTag and
|
||||
result = instruction.(LoadInstruction).getResultLanguageType()
|
||||
or
|
||||
not instruction instanceof LoadInstruction and
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionMemoryOperandType(getInstructionTag(instruction), tag)
|
||||
}
|
||||
|
||||
Instruction getPhiOperandDefinition(
|
||||
PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
Instruction getPhiInstructionBlockStart(PhiInstruction instr) { none() }
|
||||
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionSuccessor(getInstructionTag(instruction), kind)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the CFG edge (`sourceElement`, `sourceTag`) ---`kind`-->
|
||||
* `targetInstruction` is a back edge under the condition that
|
||||
* `requiredAncestor` is an ancestor of `sourceElement`.
|
||||
*/
|
||||
private predicate backEdgeCandidate(
|
||||
TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor,
|
||||
Instruction targetInstruction, EdgeKind kind
|
||||
) {
|
||||
// While loop:
|
||||
// Any edge from within the body of the loop to the condition of the loop
|
||||
// is a back edge. This includes edges from `continue` and the fall-through
|
||||
// edge(s) after the last instruction(s) in the body.
|
||||
exists(TranslatedWhileStmt s |
|
||||
targetInstruction = s.getFirstConditionInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
requiredAncestor = s.getBody()
|
||||
)
|
||||
or
|
||||
// Do-while loop:
|
||||
// The back edge should be the edge(s) from the condition to the
|
||||
// body. This ensures that it's the back edge that will be pruned in a `do
|
||||
// { ... } while (0)` statement. Note that all `continue` statements in a
|
||||
// do-while loop produce forward edges.
|
||||
exists(TranslatedDoStmt s |
|
||||
targetInstruction = s.getBody().getFirstInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
requiredAncestor = s.getCondition()
|
||||
)
|
||||
or
|
||||
// For loop:
|
||||
// Any edge from within the body or update of the loop to the condition of
|
||||
// the loop is a back edge. When there is no loop update expression, this
|
||||
// includes edges from `continue` and the fall-through edge(s) after the
|
||||
// last instruction(s) in the body. A for loop may not have a condition, in
|
||||
// which case `getFirstConditionInstruction` returns the body instead.
|
||||
exists(TranslatedForStmt s |
|
||||
targetInstruction = s.getFirstConditionInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
(
|
||||
requiredAncestor = s.getUpdate()
|
||||
or
|
||||
not exists(s.getUpdate()) and
|
||||
requiredAncestor = s.getBody()
|
||||
)
|
||||
)
|
||||
or
|
||||
// Range-based for loop:
|
||||
// Any edge from within the update of the loop to the condition of
|
||||
// the loop is a back edge.
|
||||
exists(TranslatedRangeBasedForStmt s |
|
||||
targetInstruction = s.getCondition().getFirstInstruction() and
|
||||
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
|
||||
requiredAncestor = s.getUpdate()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate jumpSourceHasAncestor(TranslatedElement jumpSource, TranslatedElement ancestor) {
|
||||
backEdgeCandidate(jumpSource, _, _, _, _) and
|
||||
ancestor = jumpSource
|
||||
or
|
||||
// For performance, we don't want a fastTC here
|
||||
jumpSourceHasAncestor(jumpSource, ancestor.getAChild())
|
||||
}
|
||||
|
||||
Instruction getInstructionBackEdgeSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
exists(
|
||||
TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor
|
||||
|
|
||||
backEdgeCandidate(sourceElement, sourceTag, requiredAncestor, result, kind) and
|
||||
jumpSourceHasAncestor(sourceElement, requiredAncestor) and
|
||||
instruction = sourceElement.getInstruction(sourceTag)
|
||||
)
|
||||
or
|
||||
// Goto statement:
|
||||
// As a conservative approximation, any edge out of `goto` is a back edge
|
||||
// unless it goes strictly forward in the program text. A `goto` whose
|
||||
// source and target are both inside a macro will be seen as having the
|
||||
// same location for source and target, so we conservatively assume that
|
||||
// such a `goto` creates a back edge.
|
||||
exists(TranslatedElement s, GotoStmt goto |
|
||||
not isStrictlyForwardGoto(goto) and
|
||||
goto = s.getAST() and
|
||||
exists(InstructionTag tag |
|
||||
result = s.getInstructionSuccessor(tag, kind) and
|
||||
instruction = s.getInstruction(tag)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `goto` jumps strictly forward in the program text. */
|
||||
private predicate isStrictlyForwardGoto(GotoStmt goto) {
|
||||
goto.getLocation().isBefore(goto.getTarget().getLocation())
|
||||
}
|
||||
|
||||
Locatable getInstructionAST(TStageInstruction instr) {
|
||||
result = getInstructionTranslatedElement(instr).getAST()
|
||||
}
|
||||
|
||||
CppType getInstructionResultType(TStageInstruction instr) {
|
||||
getInstructionTranslatedElement(instr).hasInstruction(_, getInstructionTag(instr), result)
|
||||
}
|
||||
|
||||
Opcode getInstructionOpcode(TStageInstruction instr) {
|
||||
getInstructionTranslatedElement(instr).hasInstruction(result, getInstructionTag(instr), _)
|
||||
}
|
||||
|
||||
IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) {
|
||||
result.getFunction() = getInstructionTranslatedElement(instr).getFunction()
|
||||
}
|
||||
|
||||
Instruction getPrimaryInstructionForSideEffect(SideEffectInstruction instruction) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getPrimaryInstructionForSideEffect(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
import CachedForDebugging
|
||||
|
||||
cached
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase
|
||||
@@ -1,3 +1,4 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import IRConstruction as Construction
|
||||
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration
|
||||
import IRConstruction::Raw as Raw
|
||||
|
||||
@@ -415,8 +415,11 @@ newtype TTranslatedElement =
|
||||
} or
|
||||
TTranslatedEllipsisParameter(Function func) { translateFunction(func) and func.isVarargs() } or
|
||||
TTranslatedReadEffects(Function func) { translateFunction(func) } or
|
||||
TTranslatedThisReadEffect(Function func) {
|
||||
translateFunction(func) and func.isMember() and not func.isStatic()
|
||||
} or
|
||||
// The read side effects in a function's return block
|
||||
TTranslatedReadEffect(Parameter param) {
|
||||
TTranslatedParameterReadEffect(Parameter param) {
|
||||
translateFunction(param.getFunction()) and
|
||||
exists(Type t | t = param.getUnspecifiedType() |
|
||||
t instanceof ArrayType or
|
||||
@@ -463,7 +466,7 @@ newtype TTranslatedElement =
|
||||
)
|
||||
} or
|
||||
// The side effects of an allocation, i.e. `new`, `new[]` or `malloc`
|
||||
TTranslatedAllocationSideEffects(AllocationExpr expr) or
|
||||
TTranslatedAllocationSideEffects(AllocationExpr expr) { not ignoreExpr(expr) } or
|
||||
// A precise side effect of an argument to a `Call`
|
||||
TTranslatedArgumentSideEffect(Call call, Expr expr, int n, boolean isWrite) {
|
||||
(
|
||||
@@ -702,12 +705,8 @@ abstract class TranslatedElement extends TTranslatedElement {
|
||||
int getInstructionElementSize(InstructionTag tag) { none() }
|
||||
|
||||
/**
|
||||
* If the instruction specified by `tag` has a result of type `UnknownType`,
|
||||
* gets the size of the result in bytes. If the result does not have a knonwn
|
||||
* constant size, this predicate does not hold.
|
||||
* Holds if the generated IR refers to an opaque type with size `byteSize`.
|
||||
*/
|
||||
int getInstructionResultSize(InstructionTag tag) { none() }
|
||||
|
||||
predicate needsUnknownOpaqueType(int byteSize) { none() }
|
||||
|
||||
/**
|
||||
|
||||
@@ -676,14 +676,17 @@ class TranslatedReadEffects extends TranslatedElement, TTranslatedReadEffects {
|
||||
override string toString() { result = "read effects: " + func.toString() }
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
result = getTranslatedReadEffect(func.getParameter(id))
|
||||
result = getTranslatedThisReadEffect(func) and
|
||||
id = -1
|
||||
or
|
||||
result = getTranslatedParameterReadEffect(func.getParameter(id))
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction() {
|
||||
if exists(getAChild())
|
||||
then
|
||||
result =
|
||||
min(TranslatedReadEffect child, int id | child = getChild(id) | child order by id)
|
||||
min(TranslatedElement child, int id | child = getChild(id) | child order by id)
|
||||
.getFirstInstruction()
|
||||
else result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
@@ -709,17 +712,15 @@ class TranslatedReadEffects extends TranslatedElement, TTranslatedReadEffects {
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
|
||||
}
|
||||
|
||||
private TranslatedReadEffect getTranslatedReadEffect(Parameter param) { result.getAST() = param }
|
||||
private TranslatedThisReadEffect getTranslatedThisReadEffect(Function func) {
|
||||
result.getAST() = func
|
||||
}
|
||||
|
||||
class TranslatedReadEffect extends TranslatedElement, TTranslatedReadEffect {
|
||||
Parameter param;
|
||||
|
||||
TranslatedReadEffect() { this = TTranslatedReadEffect(param) }
|
||||
|
||||
override Locatable getAST() { result = param }
|
||||
|
||||
override string toString() { result = "read effect: " + param.toString() }
|
||||
private TranslatedParameterReadEffect getTranslatedParameterReadEffect(Parameter param) {
|
||||
result.getAST() = param
|
||||
}
|
||||
|
||||
abstract class TranslatedReadEffect extends TranslatedElement {
|
||||
override TranslatedElement getChild(int id) { none() }
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) { none() }
|
||||
@@ -732,20 +733,12 @@ class TranslatedReadEffect extends TranslatedElement, TTranslatedReadEffect {
|
||||
|
||||
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
|
||||
|
||||
override Function getFunction() { result = param.getFunction() }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
opcode instanceof Opcode::ReturnIndirection and
|
||||
tag = OnlyInstructionTag() and
|
||||
resultType = getVoidType()
|
||||
}
|
||||
|
||||
final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
operandTag = addressOperand() and
|
||||
result = getTranslatedParameter(param).getInstruction(InitializerIndirectAddressTag())
|
||||
}
|
||||
|
||||
final override CppType getInstructionMemoryOperandType(
|
||||
InstructionTag tag, TypedOperandTag operandTag
|
||||
) {
|
||||
@@ -753,6 +746,47 @@ class TranslatedReadEffect extends TranslatedElement, TTranslatedReadEffect {
|
||||
operandTag = sideEffectOperand() and
|
||||
result = getUnknownType()
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedThisReadEffect extends TranslatedReadEffect, TTranslatedThisReadEffect {
|
||||
Function func;
|
||||
|
||||
TranslatedThisReadEffect() { this = TTranslatedThisReadEffect(func) }
|
||||
|
||||
override Locatable getAST() { result = func }
|
||||
|
||||
override Function getFunction() { result = func }
|
||||
|
||||
override string toString() { result = "read effect: this" }
|
||||
|
||||
final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
operandTag = addressOperand() and
|
||||
result = getTranslatedThisParameter(func).getInstruction(InitializerIndirectAddressTag())
|
||||
}
|
||||
|
||||
final override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = getTranslatedFunction(func).getThisVariable()
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedParameterReadEffect extends TranslatedReadEffect, TTranslatedParameterReadEffect {
|
||||
Parameter param;
|
||||
|
||||
TranslatedParameterReadEffect() { this = TTranslatedParameterReadEffect(param) }
|
||||
|
||||
override Locatable getAST() { result = param }
|
||||
|
||||
override string toString() { result = "read effect: " + param.toString() }
|
||||
|
||||
override Function getFunction() { result = param.getFunction() }
|
||||
|
||||
final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
operandTag = addressOperand() and
|
||||
result = getTranslatedParameter(param).getInstruction(InitializerIndirectAddressTag())
|
||||
}
|
||||
|
||||
final override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
|
||||
@@ -415,17 +415,6 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
|
||||
)
|
||||
}
|
||||
|
||||
override int getInstructionResultSize(InstructionTag tag) {
|
||||
exists(int elementCount |
|
||||
zeroInitRange(_, elementCount) and
|
||||
(
|
||||
tag = ZeroPadStringConstantTag() or
|
||||
tag = ZeroPadStringStoreTag()
|
||||
) and
|
||||
result = elementCount * getElementType().getSize()
|
||||
)
|
||||
}
|
||||
|
||||
private Type getElementType() {
|
||||
result = getContext().getTargetType().getUnspecifiedType().(ArrayType).getBaseType()
|
||||
}
|
||||
@@ -772,15 +761,6 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
|
||||
result = getZeroValue(getElementType())
|
||||
}
|
||||
|
||||
override int getInstructionResultSize(InstructionTag tag) {
|
||||
elementCount > 1 and
|
||||
(
|
||||
tag = getElementDefaultValueTag() or
|
||||
tag = getElementDefaultValueStoreTag()
|
||||
) and
|
||||
result = elementCount * getElementType().getSize()
|
||||
}
|
||||
|
||||
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
result = TranslatedElementInitialization.super.getInstructionRegisterOperand(tag, operandTag)
|
||||
or
|
||||
|
||||
@@ -8,10 +8,79 @@ module InstructionConsistency {
|
||||
private import Imports::Overlap
|
||||
private import internal.IRInternal
|
||||
|
||||
private newtype TOptionalIRFunction =
|
||||
TPresentIRFunction(IRFunction irFunc) or
|
||||
TMissingIRFunction()
|
||||
|
||||
/**
|
||||
* An `IRFunction` that might not exist. This is used so that we can produce consistency failures
|
||||
* for IR that also incorrectly lacks a `getEnclosingIRFunction()`.
|
||||
*/
|
||||
abstract private class OptionalIRFunction extends TOptionalIRFunction {
|
||||
abstract string toString();
|
||||
|
||||
abstract Language::Location getLocation();
|
||||
}
|
||||
|
||||
private class PresentIRFunction extends OptionalIRFunction, TPresentIRFunction {
|
||||
private IRFunction irFunc;
|
||||
|
||||
PresentIRFunction() { this = TPresentIRFunction(irFunc) }
|
||||
|
||||
override string toString() {
|
||||
result = concat(Language::getIdentityString(irFunc.getFunction()), "; ")
|
||||
}
|
||||
|
||||
override Language::Location getLocation() {
|
||||
// To avoid an overwhelming number of results when the extractor merges functions with the
|
||||
// same name, just pick a single location.
|
||||
result =
|
||||
rank[1](Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private class MissingIRFunction extends OptionalIRFunction, TMissingIRFunction {
|
||||
override string toString() { result = "<Missing IRFunction>" }
|
||||
|
||||
override Language::Location getLocation() { result instanceof Language::UnknownDefaultLocation }
|
||||
}
|
||||
|
||||
private OptionalIRFunction getInstructionIRFunction(Instruction instr) {
|
||||
result = TPresentIRFunction(instr.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(instr.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private OptionalIRFunction getInstructionIRFunction(Instruction instr, string irFuncText) {
|
||||
result = getInstructionIRFunction(instr) and
|
||||
irFuncText = result.toString()
|
||||
}
|
||||
|
||||
private OptionalIRFunction getOperandIRFunction(Operand operand) {
|
||||
result = TPresentIRFunction(operand.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(operand.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private OptionalIRFunction getOperandIRFunction(Operand operand, string irFuncText) {
|
||||
result = getOperandIRFunction(operand) and
|
||||
irFuncText = result.toString()
|
||||
}
|
||||
|
||||
private OptionalIRFunction getBlockIRFunction(IRBlock block) {
|
||||
result = TPresentIRFunction(block.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(block.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
|
||||
*/
|
||||
query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) {
|
||||
query predicate missingOperand(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag |
|
||||
instr.getOpcode().hasOperand(tag) and
|
||||
not exists(NonPhiOperand operand |
|
||||
@@ -21,32 +90,39 @@ module InstructionConsistency {
|
||||
message =
|
||||
"Instruction '" + instr.getOpcode().toString() +
|
||||
"' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has an unexpected operand with tag `tag`.
|
||||
*/
|
||||
query predicate unexpectedOperand(Instruction instr, OperandTag tag) {
|
||||
exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
not instr.getOpcode().hasOperand(tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (
|
||||
instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
|
||||
) and
|
||||
not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag)
|
||||
query predicate unexpectedOperand(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag |
|
||||
exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
not instr.getOpcode().hasOperand(tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (
|
||||
instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
|
||||
) and
|
||||
not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' has unexpected operand '" + tag.toString() +
|
||||
"' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has multiple operands with tag `tag`.
|
||||
*/
|
||||
query predicate duplicateOperand(
|
||||
Instruction instr, string message, IRFunction func, string funcText
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag, int operandCount |
|
||||
operandCount =
|
||||
@@ -58,8 +134,7 @@ module InstructionConsistency {
|
||||
message =
|
||||
"Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
|
||||
" in function '$@'." and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -67,100 +142,136 @@ module InstructionConsistency {
|
||||
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
|
||||
* the predecessor block `pred`.
|
||||
*/
|
||||
query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
|
||||
pred = instr.getBlock().getAPredecessor() and
|
||||
not exists(PhiInputOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getPredecessorBlock() = pred
|
||||
query predicate missingPhiOperand(
|
||||
PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRBlock pred |
|
||||
pred = instr.getBlock().getAPredecessor() and
|
||||
not exists(PhiInputOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getPredecessorBlock() = pred
|
||||
) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is missing an operand for predecessor block '" +
|
||||
pred.toString() + "' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingOperandType(Operand operand, string message) {
|
||||
exists(Language::Function func, Instruction use |
|
||||
query predicate missingOperandType(
|
||||
Operand operand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Instruction use |
|
||||
not exists(operand.getType()) and
|
||||
use = operand.getUse() and
|
||||
func = use.getEnclosingFunction() and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
|
||||
"' missing type in function '" + Language::getIdentityString(func) + "'."
|
||||
"' is missing a type in function '$@'." and
|
||||
irFunc = getOperandIRFunction(operand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate duplicateChiOperand(
|
||||
ChiInstruction chi, string message, IRFunction func, string funcText
|
||||
ChiInstruction chi, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
chi.getTotal() = chi.getPartial() and
|
||||
message =
|
||||
"Chi instruction for " + chi.getPartial().toString() +
|
||||
" has duplicate operands in function $@" and
|
||||
func = chi.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
" has duplicate operands in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(chi, irFuncText)
|
||||
}
|
||||
|
||||
query predicate sideEffectWithoutPrimary(
|
||||
SideEffectInstruction instr, string message, IRFunction func, string funcText
|
||||
SideEffectInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(instr.getPrimaryInstruction()) and
|
||||
message = "Side effect instruction missing primary instruction in function $@" and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
message =
|
||||
"Side effect instruction '" + instr + "' is missing a primary instruction in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
||||
*/
|
||||
query predicate instructionWithoutSuccessor(Instruction instr) {
|
||||
query predicate instructionWithoutSuccessor(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(instr.getASuccessor()) and
|
||||
not instr instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not instr instanceof PhiInstruction and
|
||||
not instr instanceof UnreachedInstruction
|
||||
not instr instanceof UnreachedInstruction and
|
||||
message = "Instruction '" + instr.toString() + "' has no successors in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there are multiple (`n`) edges of kind `kind` from `source`,
|
||||
* where `target` is among the targets of those edges.
|
||||
* Holds if there are multiple edges of the same kind from `source`.
|
||||
*/
|
||||
query predicate ambiguousSuccessors(Instruction source, EdgeKind kind, int n, Instruction target) {
|
||||
n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
|
||||
n > 1 and
|
||||
source.getSuccessor(kind) = target
|
||||
query predicate ambiguousSuccessors(
|
||||
Instruction source, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(EdgeKind kind, int n |
|
||||
n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
|
||||
n > 1 and
|
||||
message =
|
||||
"Instruction '" + source.toString() + "' has " + n.toString() + " successors of kind '" +
|
||||
kind.toString() + "' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(source, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` in `f` is part of a loop even though the AST of `f`
|
||||
* Holds if `instr` is part of a loop even though the AST of `instr`'s enclosing function
|
||||
* contains no element that can cause loops.
|
||||
*/
|
||||
query predicate unexplainedLoop(Language::Function f, Instruction instr) {
|
||||
exists(IRBlock block |
|
||||
instr.getBlock() = block and
|
||||
block.getEnclosingFunction() = f and
|
||||
block.getASuccessor+() = block
|
||||
) and
|
||||
not Language::hasPotentialLoop(f)
|
||||
query predicate unexplainedLoop(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Language::Function f |
|
||||
exists(IRBlock block |
|
||||
instr.getBlock() = block and
|
||||
block.getEnclosingFunction() = f and
|
||||
block.getASuccessor+() = block
|
||||
) and
|
||||
not Language::hasPotentialLoop(f) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is part of an unexplained loop in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `Phi` instruction is present in a block with fewer than two
|
||||
* predecessors.
|
||||
*/
|
||||
query predicate unnecessaryPhiInstruction(PhiInstruction instr) {
|
||||
count(instr.getBlock().getAPredecessor()) < 2
|
||||
query predicate unnecessaryPhiInstruction(
|
||||
PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int n |
|
||||
n = count(instr.getBlock().getAPredecessor()) and
|
||||
n < 2 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is in a block with only " + n.toString() +
|
||||
" predecessors in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a memory operand is connected to a definition with an unmodeled result.
|
||||
*/
|
||||
query predicate memoryOperandDefinitionIsUnmodeled(
|
||||
Instruction instr, string message, IRFunction func, string funcText
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(MemoryOperand operand, Instruction def |
|
||||
operand = instr.getAnOperand() and
|
||||
def = operand.getAnyDef() and
|
||||
not def.isResultModeled() and
|
||||
message = "Memory operand definition has unmodeled result in function '$@'" and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
message =
|
||||
"Memory operand definition on instruction '" + instr.toString() +
|
||||
"' has unmodeled result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -168,18 +279,37 @@ module InstructionConsistency {
|
||||
* Holds if operand `operand` consumes a value that was defined in
|
||||
* a different function.
|
||||
*/
|
||||
query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) {
|
||||
operand.getUse() = instr and
|
||||
operand.getAnyDef() = defInstr and
|
||||
instr.getEnclosingIRFunction() != defInstr.getEnclosingIRFunction()
|
||||
query predicate operandAcrossFunctions(
|
||||
Operand operand, string message, OptionalIRFunction useIRFunc, string useIRFuncText,
|
||||
OptionalIRFunction defIRFunc, string defIRFuncText
|
||||
) {
|
||||
exists(Instruction useInstr, Instruction defInstr |
|
||||
operand.getUse() = useInstr and
|
||||
operand.getAnyDef() = defInstr and
|
||||
useIRFunc = getInstructionIRFunction(useInstr, useIRFuncText) and
|
||||
defIRFunc = getInstructionIRFunction(defInstr, defIRFuncText) and
|
||||
useIRFunc != defIRFunc and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' is used on instruction '" + useInstr.toString() +
|
||||
"' in function '$@', but is defined on instruction '" + defInstr.toString() +
|
||||
"' in function '$@'."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is not in exactly one block.
|
||||
*/
|
||||
query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) {
|
||||
blockCount = count(instr.getBlock()) and
|
||||
blockCount != 1
|
||||
query predicate instructionWithoutUniqueBlock(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int blockCount |
|
||||
blockCount = count(instr.getBlock()) and
|
||||
blockCount != 1 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is a member of " + blockCount.toString() +
|
||||
" blocks in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate forwardEdge(IRBlock b1, IRBlock b2) {
|
||||
@@ -192,10 +322,11 @@ module InstructionConsistency {
|
||||
*
|
||||
* This check ensures we don't have too _few_ back edges.
|
||||
*/
|
||||
query predicate containsLoopOfForwardEdges(IRFunction f) {
|
||||
query predicate containsLoopOfForwardEdges(IRFunction f, string message) {
|
||||
exists(IRBlock block |
|
||||
forwardEdge+(block, block) and
|
||||
block.getEnclosingIRFunction() = f
|
||||
block.getEnclosingIRFunction() = f and
|
||||
message = "Function contains a loop consisting of only forward edges."
|
||||
)
|
||||
}
|
||||
|
||||
@@ -207,12 +338,19 @@ module InstructionConsistency {
|
||||
*
|
||||
* This check ensures we don't have too _many_ back edges.
|
||||
*/
|
||||
query predicate lostReachability(IRBlock block) {
|
||||
query predicate lostReachability(
|
||||
IRBlock block, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRFunction f, IRBlock entry |
|
||||
entry = f.getEntryBlock() and
|
||||
entry.getASuccessor+() = block and
|
||||
not forwardEdge+(entry, block) and
|
||||
not Language::hasGoto(f.getFunction())
|
||||
not Language::hasGoto(f.getFunction()) and
|
||||
message =
|
||||
"Block '" + block.toString() +
|
||||
"' is not reachable by traversing only forward edges in function '$@'." and
|
||||
irFunc = TPresentIRFunction(f) and
|
||||
irFuncText = irFunc.toString()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -220,16 +358,22 @@ module InstructionConsistency {
|
||||
* Holds if the number of back edges differs between the `Instruction` graph
|
||||
* and the `IRBlock` graph.
|
||||
*/
|
||||
query predicate backEdgeCountMismatch(Language::Function f, int fromInstr, int fromBlock) {
|
||||
fromInstr =
|
||||
count(Instruction i1, Instruction i2 |
|
||||
i1.getEnclosingFunction() = f and i1.getBackEdgeSuccessor(_) = i2
|
||||
) and
|
||||
fromBlock =
|
||||
count(IRBlock b1, IRBlock b2 |
|
||||
b1.getEnclosingFunction() = f and b1.getBackEdgeSuccessor(_) = b2
|
||||
) and
|
||||
fromInstr != fromBlock
|
||||
query predicate backEdgeCountMismatch(OptionalIRFunction irFunc, string message) {
|
||||
exists(int fromInstr, int fromBlock |
|
||||
fromInstr =
|
||||
count(Instruction i1, Instruction i2 |
|
||||
getInstructionIRFunction(i1) = irFunc and i1.getBackEdgeSuccessor(_) = i2
|
||||
) and
|
||||
fromBlock =
|
||||
count(IRBlock b1, IRBlock b2 |
|
||||
getBlockIRFunction(b1) = irFunc and b1.getBackEdgeSuccessor(_) = b2
|
||||
) and
|
||||
fromInstr != fromBlock and
|
||||
message =
|
||||
"The instruction graph for function '" + irFunc.toString() + "' contains " +
|
||||
fromInstr.toString() + " back edges, but the block graph contains " + fromBlock.toString()
|
||||
+ " back edges."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,7 +395,7 @@ module InstructionConsistency {
|
||||
* Holds if `useOperand` has a definition that does not dominate the use.
|
||||
*/
|
||||
query predicate useNotDominatedByDefinition(
|
||||
Operand useOperand, string message, IRFunction func, string funcText
|
||||
Operand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
|
||||
pointOfEvaluation(useOperand, useBlock, useIndex) and
|
||||
@@ -272,19 +416,17 @@ module InstructionConsistency {
|
||||
message =
|
||||
"Operand '" + useOperand.toString() +
|
||||
"' is not dominated by its definition in function '$@'." and
|
||||
func = useOperand.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
irFunc = getOperandIRFunction(useOperand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate switchInstructionWithoutDefaultEdge(
|
||||
SwitchInstruction switchInstr, string message, IRFunction func, string funcText
|
||||
SwitchInstruction switchInstr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(switchInstr.getDefaultSuccessor()) and
|
||||
message =
|
||||
"SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and
|
||||
func = switchInstr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
irFunc = getInstructionIRFunction(switchInstr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -305,18 +447,30 @@ module InstructionConsistency {
|
||||
instr.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
query predicate notMarkedAsConflated(Instruction instr) {
|
||||
query predicate notMarkedAsConflated(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
shouldBeConflated(instr) and
|
||||
not instr.isResultConflated()
|
||||
not instr.isResultConflated() and
|
||||
message =
|
||||
"Instruction '" + instr.toString() +
|
||||
"' should be marked as having a conflated result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
query predicate wronglyMarkedAsConflated(Instruction instr) {
|
||||
query predicate wronglyMarkedAsConflated(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
instr.isResultConflated() and
|
||||
not shouldBeConflated(instr)
|
||||
not shouldBeConflated(instr) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() +
|
||||
"' should not be marked as having a conflated result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
query predicate invalidOverlap(
|
||||
MemoryOperand useOperand, string message, IRFunction func, string funcText
|
||||
MemoryOperand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Overlap overlap |
|
||||
overlap = useOperand.getDefinitionOverlap() and
|
||||
@@ -324,8 +478,20 @@ module InstructionConsistency {
|
||||
message =
|
||||
"MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
|
||||
overlap.toString() + "'." and
|
||||
func = useOperand.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
irFunc = getOperandIRFunction(useOperand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate nonUniqueEnclosingIRFunction(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int irFuncCount |
|
||||
irFuncCount = count(instr.getEnclosingIRFunction()) and
|
||||
irFuncCount != 1 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' has " + irFuncCount.toString() +
|
||||
" results for `getEnclosingIRFunction()` in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,12 @@
|
||||
private import internal.IRInternal
|
||||
private import internal.IRFunctionImports as Imports
|
||||
import Imports::IRFunctionBase
|
||||
import Instruction
|
||||
|
||||
private newtype TIRFunction =
|
||||
MkIRFunction(Language::Function func) { Construction::functionHasIR(func) }
|
||||
|
||||
/**
|
||||
* Represents the IR for a function.
|
||||
* The IR for a function.
|
||||
*/
|
||||
class IRFunction extends TIRFunction {
|
||||
Language::Function func;
|
||||
|
||||
IRFunction() { this = MkIRFunction(func) }
|
||||
|
||||
final string toString() { result = "IR: " + func.toString() }
|
||||
|
||||
/**
|
||||
* Gets the function whose IR is represented.
|
||||
*/
|
||||
final Language::Function getFunction() { result = func }
|
||||
|
||||
/**
|
||||
* Gets the location of the function.
|
||||
*/
|
||||
final Language::Location getLocation() { result = func.getLocation() }
|
||||
|
||||
class IRFunction extends IRFunctionBase {
|
||||
/**
|
||||
* Gets the entry point for this function.
|
||||
*/
|
||||
|
||||
@@ -217,19 +217,23 @@ class IRThrowVariable extends IRTempVariable {
|
||||
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
|
||||
* function that accepts a variable number of arguments.
|
||||
*/
|
||||
class IREllipsisVariable extends IRTempVariable {
|
||||
class IREllipsisVariable extends IRTempVariable, IRParameter {
|
||||
IREllipsisVariable() { tag = EllipsisTempVar() }
|
||||
|
||||
final override string toString() { result = "#ellipsis" }
|
||||
|
||||
final override int getIndex() { result = func.getNumberOfParameters() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A temporary variable generated to hold the `this` pointer.
|
||||
*/
|
||||
class IRThisVariable extends IRTempVariable {
|
||||
class IRThisVariable extends IRTempVariable, IRParameter {
|
||||
IRThisVariable() { tag = ThisTempVar() }
|
||||
|
||||
final override string toString() { result = "#this" }
|
||||
|
||||
final override int getIndex() { result = -1 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -274,3 +278,29 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial
|
||||
|
||||
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR variable which acts like a function parameter, including positional parameters and the
|
||||
* temporary variables generated for `this` and ellipsis parameters.
|
||||
*/
|
||||
class IRParameter extends IRAutomaticVariable {
|
||||
IRParameter() {
|
||||
this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter
|
||||
or
|
||||
this = TIRTempVariable(_, _, ThisTempVar(), _)
|
||||
or
|
||||
this = TIRTempVariable(_, _, EllipsisTempVar(), _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the zero-based index of this parameter. The `this` parameter has index -1.
|
||||
*/
|
||||
int getIndex() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR variable representing a positional parameter.
|
||||
*/
|
||||
class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable {
|
||||
final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() }
|
||||
}
|
||||
|
||||
@@ -29,7 +29,13 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil
|
||||
/**
|
||||
* Represents a single operation in the IR.
|
||||
*/
|
||||
class Instruction extends Construction::TInstruction {
|
||||
class Instruction extends Construction::TStageInstruction {
|
||||
Instruction() {
|
||||
// The base `TStageInstruction` type is a superset of the actual instructions appearing in this
|
||||
// stage. This call lets the stage filter out the ones that are not reused from raw IR.
|
||||
Construction::hasInstruction(this)
|
||||
}
|
||||
|
||||
final string toString() { result = getOpcode().toString() + ": " + getAST().toString() }
|
||||
|
||||
/**
|
||||
@@ -194,14 +200,14 @@ class Instruction extends Construction::TInstruction {
|
||||
* conversion.
|
||||
*/
|
||||
final Language::Expr getConvertedResultExpression() {
|
||||
result = Construction::getInstructionConvertedResultExpression(this)
|
||||
result = Raw::getInstructionConvertedResultExpression(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any.
|
||||
*/
|
||||
final Language::Expr getUnconvertedResultExpression() {
|
||||
result = Construction::getInstructionUnconvertedResultExpression(this)
|
||||
result = Raw::getInstructionUnconvertedResultExpression(this)
|
||||
}
|
||||
|
||||
final Language::LanguageType getResultLanguageType() {
|
||||
@@ -212,6 +218,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* Gets the type of the result produced by this instruction. If the instruction does not produce
|
||||
* a result, its result type will be `IRVoidType`.
|
||||
*/
|
||||
cached
|
||||
final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
@@ -250,7 +257,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* result of the `Load` instruction is a prvalue of type `int`, representing
|
||||
* the integer value loaded from variable `x`.
|
||||
*/
|
||||
final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
|
||||
final predicate isGLValue() { getResultLanguageType().hasType(_, true) }
|
||||
|
||||
/**
|
||||
* Gets the size of the result produced by this instruction, in bytes. If the
|
||||
@@ -259,7 +266,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* If `this.isGLValue()` holds for this instruction, the value of
|
||||
* `getResultSize()` will always be the size of a pointer.
|
||||
*/
|
||||
final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
|
||||
final int getResultSize() { result = getResultLanguageType().getByteSize() }
|
||||
|
||||
/**
|
||||
* Gets the opcode that specifies the operation performed by this instruction.
|
||||
@@ -395,7 +402,7 @@ class Instruction extends Construction::TInstruction {
|
||||
class VariableInstruction extends Instruction {
|
||||
IRVariable var;
|
||||
|
||||
VariableInstruction() { var = Construction::getInstructionVariable(this) }
|
||||
VariableInstruction() { var = Raw::getInstructionVariable(this) }
|
||||
|
||||
override string getImmediateString() { result = var.toString() }
|
||||
|
||||
@@ -410,7 +417,7 @@ class VariableInstruction extends Instruction {
|
||||
class FieldInstruction extends Instruction {
|
||||
Language::Field field;
|
||||
|
||||
FieldInstruction() { field = Construction::getInstructionField(this) }
|
||||
FieldInstruction() { field = Raw::getInstructionField(this) }
|
||||
|
||||
final override string getImmediateString() { result = field.toString() }
|
||||
|
||||
@@ -420,7 +427,7 @@ class FieldInstruction extends Instruction {
|
||||
class FunctionInstruction extends Instruction {
|
||||
Language::Function funcSymbol;
|
||||
|
||||
FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) }
|
||||
FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }
|
||||
|
||||
final override string getImmediateString() { result = funcSymbol.toString() }
|
||||
|
||||
@@ -430,7 +437,7 @@ class FunctionInstruction extends Instruction {
|
||||
class ConstantValueInstruction extends Instruction {
|
||||
string value;
|
||||
|
||||
ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) }
|
||||
ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) }
|
||||
|
||||
final override string getImmediateString() { result = value }
|
||||
|
||||
@@ -440,7 +447,7 @@ class ConstantValueInstruction extends Instruction {
|
||||
class IndexedInstruction extends Instruction {
|
||||
int index;
|
||||
|
||||
IndexedInstruction() { index = Construction::getInstructionIndex(this) }
|
||||
IndexedInstruction() { index = Raw::getInstructionIndex(this) }
|
||||
|
||||
final override string getImmediateString() { result = index.toString() }
|
||||
|
||||
@@ -541,6 +548,11 @@ class ReturnIndirectionInstruction extends VariableInstruction {
|
||||
* function.
|
||||
*/
|
||||
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
|
||||
|
||||
/**
|
||||
* Holds if this instruction is the return indirection for `this`.
|
||||
*/
|
||||
final predicate isThisIndirection() { var instanceof IRThisVariable }
|
||||
}
|
||||
|
||||
class CopyInstruction extends Instruction {
|
||||
@@ -598,11 +610,16 @@ class ConstantInstruction extends ConstantValueInstruction {
|
||||
}
|
||||
|
||||
class IntegerConstantInstruction extends ConstantInstruction {
|
||||
IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType }
|
||||
IntegerConstantInstruction() {
|
||||
exists(IRType resultType |
|
||||
resultType = getResultIRType() and
|
||||
(resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class FloatConstantInstruction extends ConstantInstruction {
|
||||
FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
|
||||
FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType }
|
||||
}
|
||||
|
||||
class StringConstantInstruction extends VariableInstruction {
|
||||
@@ -699,7 +716,7 @@ class PointerArithmeticInstruction extends BinaryInstruction {
|
||||
|
||||
PointerArithmeticInstruction() {
|
||||
getOpcode() instanceof PointerArithmeticOpcode and
|
||||
elementSize = Construction::getInstructionElementSize(this)
|
||||
elementSize = Raw::getInstructionElementSize(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = elementSize.toString() }
|
||||
@@ -748,7 +765,7 @@ class InheritanceConversionInstruction extends UnaryInstruction {
|
||||
Language::Class derivedClass;
|
||||
|
||||
InheritanceConversionInstruction() {
|
||||
Construction::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
Raw::getInstructionInheritance(this, baseClass, derivedClass)
|
||||
}
|
||||
|
||||
final override string getImmediateString() {
|
||||
@@ -1211,7 +1228,7 @@ class CatchByTypeInstruction extends CatchInstruction {
|
||||
|
||||
CatchByTypeInstruction() {
|
||||
getOpcode() instanceof Opcode::CatchByType and
|
||||
exceptionType = Construction::getInstructionExceptionType(this)
|
||||
exceptionType = Raw::getInstructionExceptionType(this)
|
||||
}
|
||||
|
||||
final override string getImmediateString() { result = exceptionType.toString() }
|
||||
@@ -1357,7 +1374,7 @@ class BuiltInOperationInstruction extends Instruction {
|
||||
|
||||
BuiltInOperationInstruction() {
|
||||
getOpcode() instanceof BuiltInOperationOpcode and
|
||||
operation = Construction::getInstructionBuiltInOperation(this)
|
||||
operation = Raw::getInstructionBuiltInOperation(this)
|
||||
}
|
||||
|
||||
final Language::BuiltInOperation getBuiltInOperation() { result = operation }
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase
|
||||
@@ -1,3 +1,4 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import SSAConstruction as Construction
|
||||
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Raw
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import SSAConstructionInternal
|
||||
private import SSAConstructionImports
|
||||
private import SSAConstructionImports as Imports
|
||||
private import Imports::Opcode
|
||||
private import Imports::OperandTag
|
||||
private import Imports::Overlap
|
||||
private import Imports::TInstruction
|
||||
private import Imports::RawIR as RawIR
|
||||
private import SSAInstructions
|
||||
private import NewIR
|
||||
|
||||
private class OldBlock = Reachability::ReachableBlock;
|
||||
@@ -10,54 +16,47 @@ import Cached
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
predicate hasPhiInstructionCached(
|
||||
OldInstruction blockStartInstr, Alias::MemoryLocation defLocation
|
||||
) {
|
||||
exists(OldBlock oldBlock |
|
||||
definitionHasPhiNode(defLocation, oldBlock) and
|
||||
blockStartInstr = oldBlock.getFirstInstruction()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasChiInstructionCached(OldInstruction primaryInstruction) {
|
||||
hasChiNode(_, primaryInstruction)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasUnreachedInstructionCached(IRFunction irFunc) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
irFunc = oldInstruction.getEnclosingIRFunction() and
|
||||
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
|
||||
)
|
||||
}
|
||||
|
||||
class TStageInstruction =
|
||||
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction;
|
||||
|
||||
cached
|
||||
predicate hasInstruction(TStageInstruction instr) {
|
||||
instr instanceof TRawInstruction and instr instanceof OldInstruction
|
||||
or
|
||||
instr instanceof TPhiInstruction
|
||||
or
|
||||
instr instanceof TChiInstruction
|
||||
or
|
||||
instr instanceof TUnreachedInstruction
|
||||
}
|
||||
|
||||
private IRBlock getNewBlock(OldBlock oldBlock) {
|
||||
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
}
|
||||
|
||||
cached
|
||||
predicate functionHasIR(Language::Function func) {
|
||||
exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func)
|
||||
}
|
||||
|
||||
cached
|
||||
OldInstruction getOldInstruction(Instruction instr) { instr = WrappedInstruction(result) }
|
||||
|
||||
private IRVariable getNewIRVariable(OldIR::IRVariable var) {
|
||||
// This is just a type cast. Both classes derive from the same newtype.
|
||||
result = var
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TInstruction =
|
||||
WrappedInstruction(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction
|
||||
} or
|
||||
Phi(OldBlock block, Alias::MemoryLocation defLocation) {
|
||||
definitionHasPhiNode(defLocation, block)
|
||||
} or
|
||||
Chi(OldInstruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction and
|
||||
hasChiNode(_, oldInstruction)
|
||||
} or
|
||||
Unreached(Language::Function function) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
function = oldInstruction.getEnclosingFunction() and
|
||||
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasTempVariable(
|
||||
Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type
|
||||
) {
|
||||
exists(OldIR::IRTempVariable var |
|
||||
var.getEnclosingFunction() = func and
|
||||
var.getAST() = ast and
|
||||
var.getTag() = tag and
|
||||
var.getLanguageType() = type
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasModeledMemoryResult(Instruction instruction) {
|
||||
exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or
|
||||
@@ -73,7 +72,7 @@ private module Cached {
|
||||
or
|
||||
// Chi instructions track virtual variables, and therefore a chi instruction is
|
||||
// conflated if it's associated with the aliased virtual variable.
|
||||
exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) |
|
||||
exists(OldInstruction oldInstruction | instruction = getChi(oldInstruction) |
|
||||
Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof
|
||||
Alias::AliasedVirtualVariable
|
||||
)
|
||||
@@ -81,7 +80,7 @@ private module Cached {
|
||||
// Phi instructions track locations, and therefore a phi instruction is
|
||||
// conflated if it's associated with a conflated location.
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = Phi(_, location) and
|
||||
instruction = getPhi(_, location) and
|
||||
not exists(location.getAllocation())
|
||||
)
|
||||
}
|
||||
@@ -128,7 +127,7 @@ private module Cached {
|
||||
hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result)
|
||||
)
|
||||
or
|
||||
instruction = Chi(getOldInstruction(result)) and
|
||||
instruction = getChi(getOldInstruction(result)) and
|
||||
tag instanceof ChiPartialOperandTag and
|
||||
overlap instanceof MustExactlyOverlap
|
||||
or
|
||||
@@ -172,13 +171,15 @@ private module Cached {
|
||||
|
||||
pragma[noopt]
|
||||
cached
|
||||
Instruction getPhiOperandDefinition(Phi instr, IRBlock newPredecessorBlock, Overlap overlap) {
|
||||
Instruction getPhiOperandDefinition(
|
||||
PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap
|
||||
) {
|
||||
exists(
|
||||
Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock,
|
||||
OldBlock predBlock, OldBlock defBlock, int defOffset, Alias::MemoryLocation actualDefLocation
|
||||
|
|
||||
hasPhiOperandDefinition(defLocation, useLocation, phiBlock, predBlock, defBlock, defOffset) and
|
||||
instr = Phi(phiBlock, useLocation) and
|
||||
instr = getPhi(phiBlock, useLocation) and
|
||||
newPredecessorBlock = getNewBlock(predBlock) and
|
||||
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
|
||||
overlap = Alias::getOverlap(actualDefLocation, useLocation)
|
||||
@@ -191,7 +192,7 @@ private module Cached {
|
||||
Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation,
|
||||
OldBlock defBlock, int defRank, int defOffset, OldBlock useBlock, int useRank
|
||||
|
|
||||
chiInstr = Chi(oldInstr) and
|
||||
chiInstr = getChi(oldInstr) and
|
||||
vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and
|
||||
hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, oldInstr) and
|
||||
@@ -203,21 +204,11 @@ private module Cached {
|
||||
cached
|
||||
Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
|
||||
exists(OldBlock oldBlock |
|
||||
instr = Phi(oldBlock, _) and
|
||||
instr = getPhi(oldBlock, _) and
|
||||
result = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getConvertedResultExpression()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/*
|
||||
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
|
||||
* that node is its successor in the new successor relation, and the Chi node's successors are
|
||||
@@ -228,20 +219,20 @@ private module Cached {
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
if hasChiNode(_, getOldInstruction(instruction))
|
||||
then
|
||||
result = Chi(getOldInstruction(instruction)) and
|
||||
result = getChi(getOldInstruction(instruction)) and
|
||||
kind instanceof GotoEdge
|
||||
else (
|
||||
exists(OldInstruction oldInstruction |
|
||||
oldInstruction = getOldInstruction(instruction) and
|
||||
(
|
||||
if Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind)
|
||||
then result = Unreached(instruction.getEnclosingFunction())
|
||||
then result = unreachedInstruction(instruction.getEnclosingIRFunction())
|
||||
else result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = Chi(oldInstruction) and
|
||||
instruction = getChi(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction.getSuccessor(kind))
|
||||
)
|
||||
)
|
||||
@@ -260,137 +251,73 @@ private module Cached {
|
||||
// `oldInstruction`, in which case the back edge should come out of the
|
||||
// chi node instead.
|
||||
if hasChiNode(_, oldInstruction)
|
||||
then instruction = Chi(oldInstruction)
|
||||
then instruction = getChi(oldInstruction)
|
||||
else instruction = getNewInstruction(oldInstruction)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::AST getInstructionAST(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction)
|
||||
or
|
||||
instruction = Chi(oldInstruction)
|
||||
|
|
||||
result = oldInstruction.getAST()
|
||||
Language::AST getInstructionAST(Instruction instr) {
|
||||
result = getOldInstruction(instr).getAST()
|
||||
or
|
||||
exists(RawIR::Instruction blockStartInstr |
|
||||
instr = phiInstruction(blockStartInstr, _) and
|
||||
result = blockStartInstr.getAST()
|
||||
)
|
||||
or
|
||||
exists(OldBlock block |
|
||||
instruction = Phi(block, _) and
|
||||
result = block.getFirstInstruction().getAST()
|
||||
exists(RawIR::Instruction primaryInstr |
|
||||
instr = chiInstruction(primaryInstr) and
|
||||
result = primaryInstr.getAST()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(result)
|
||||
exists(IRFunctionBase irFunc |
|
||||
instr = unreachedInstruction(irFunc) and result = irFunc.getFunction()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::LanguageType getInstructionResultType(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction) and
|
||||
result = oldInstruction.getResultLanguageType()
|
||||
Language::LanguageType getInstructionResultType(Instruction instr) {
|
||||
result = instr.(RawIR::Instruction).getResultLanguageType()
|
||||
or
|
||||
exists(Alias::MemoryLocation defLocation |
|
||||
instr = phiInstruction(_, defLocation) and
|
||||
result = defLocation.getType()
|
||||
)
|
||||
or
|
||||
exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar |
|
||||
instruction = Chi(oldInstruction) and
|
||||
hasChiNode(vvar, oldInstruction) and
|
||||
exists(Instruction primaryInstr, Alias::VirtualVariable vvar |
|
||||
instr = chiInstruction(primaryInstr) and
|
||||
hasChiNode(vvar, primaryInstr) and
|
||||
result = vvar.getType()
|
||||
)
|
||||
or
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = Phi(_, location) and
|
||||
result = location.getType()
|
||||
instr = unreachedInstruction(_) and result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instr) {
|
||||
result = getOldInstruction(instr).getOpcode()
|
||||
or
|
||||
instr = phiInstruction(_, _) and result instanceof Opcode::Phi
|
||||
or
|
||||
instr = chiInstruction(_) and result instanceof Opcode::Chi
|
||||
or
|
||||
instr = unreachedInstruction(_) and result instanceof Opcode::Unreached
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunctionBase getInstructionEnclosingIRFunction(Instruction instr) {
|
||||
result = getOldInstruction(instr).getEnclosingIRFunction()
|
||||
or
|
||||
exists(OldInstruction blockStartInstr |
|
||||
instr = phiInstruction(blockStartInstr, _) and
|
||||
result = blockStartInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(_) and
|
||||
result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction) and
|
||||
result = oldInstruction.getOpcode()
|
||||
exists(OldInstruction primaryInstr |
|
||||
instr = chiInstruction(primaryInstr) and result = primaryInstr.getEnclosingIRFunction()
|
||||
)
|
||||
or
|
||||
instruction instanceof Chi and
|
||||
result instanceof Opcode::Chi
|
||||
or
|
||||
instruction instanceof Phi and
|
||||
result instanceof Opcode::Phi
|
||||
or
|
||||
instruction instanceof Unreached and
|
||||
result instanceof Opcode::Unreached
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction)
|
||||
or
|
||||
instruction = Chi(oldInstruction)
|
||||
|
|
||||
result.getFunction() = oldInstruction.getEnclosingFunction()
|
||||
)
|
||||
or
|
||||
exists(OldBlock block |
|
||||
instruction = Phi(block, _) and
|
||||
result.getFunction() = block.getEnclosingFunction()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(result.getFunction())
|
||||
}
|
||||
|
||||
cached
|
||||
IRVariable getInstructionVariable(Instruction instruction) {
|
||||
result =
|
||||
getNewIRVariable(getOldInstruction(instruction).(OldIR::VariableInstruction).getIRVariable())
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Field getInstructionField(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionIndex(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Function getInstructionFunction(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
|
||||
}
|
||||
|
||||
cached
|
||||
string getInstructionConstantValue(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::ConstantValueInstruction).getValue()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
|
||||
result =
|
||||
getOldInstruction(instruction).(OldIR::BuiltInOperationInstruction).getBuiltInOperation()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::LanguageType getInstructionExceptionType(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType()
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionElementSize(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::PointerArithmeticInstruction).getElementSize()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate getInstructionInheritance(
|
||||
Instruction instruction, Language::Class baseClass, Language::Class derivedClass
|
||||
) {
|
||||
exists(OldIR::InheritanceConversionInstruction oldInstr |
|
||||
oldInstr = getOldInstruction(instruction) and
|
||||
baseClass = oldInstr.getBaseClass() and
|
||||
derivedClass = oldInstr.getDerivedClass()
|
||||
)
|
||||
instr = unreachedInstruction(result)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -401,7 +328,7 @@ private module Cached {
|
||||
)
|
||||
or
|
||||
exists(OldIR::Instruction oldInstruction |
|
||||
instruction = Chi(oldInstruction) and
|
||||
instruction = getChi(oldInstruction) and
|
||||
result = getNewInstruction(oldInstruction)
|
||||
)
|
||||
}
|
||||
@@ -409,6 +336,14 @@ private module Cached {
|
||||
|
||||
private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr }
|
||||
|
||||
private OldInstruction getOldInstruction(Instruction instr) { instr = result }
|
||||
|
||||
private ChiInstruction getChi(OldInstruction primaryInstr) { result = chiInstruction(primaryInstr) }
|
||||
|
||||
private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) {
|
||||
result = phiInstruction(defBlock.getFirstInstruction(), defLocation)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `def` needs to have a `Chi` instruction inserted after it, to account for a partial definition
|
||||
* of a virtual variable. The `Chi` instruction provides a definition of the entire virtual variable of which the
|
||||
@@ -588,7 +523,7 @@ module DefUse {
|
||||
|
|
||||
// An odd offset corresponds to the `Chi` instruction.
|
||||
defOffset = oldOffset * 2 + 1 and
|
||||
result = Chi(oldInstr) and
|
||||
result = getChi(oldInstr) and
|
||||
(
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr) or
|
||||
defLocation = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable()
|
||||
@@ -607,7 +542,7 @@ module DefUse {
|
||||
or
|
||||
defOffset = -1 and
|
||||
hasDefinition(_, defLocation, defBlock, defOffset) and
|
||||
result = Phi(defBlock, defLocation) and
|
||||
result = getPhi(defBlock, defLocation) and
|
||||
actualDefLocation = defLocation
|
||||
}
|
||||
|
||||
@@ -891,7 +826,7 @@ private module CachedForDebugging {
|
||||
)
|
||||
or
|
||||
exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity |
|
||||
instr = Phi(phiBlock, location) and
|
||||
instr = getPhi(phiBlock, location) and
|
||||
result =
|
||||
"Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and
|
||||
if location instanceof Alias::VirtualVariable
|
||||
@@ -901,7 +836,7 @@ private module CachedForDebugging {
|
||||
else specificity = "s"
|
||||
)
|
||||
or
|
||||
instr = Unreached(_) and
|
||||
instr = unreachedInstruction(_) and
|
||||
result = "Unreached"
|
||||
}
|
||||
|
||||
@@ -961,3 +896,19 @@ module SSAConsistency {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the portion of the parameterized IR interface that is used to construct the SSA stages
|
||||
* of the IR. The raw stage of the IR does not expose these predicates.
|
||||
* These predicates are all just aliases for predicates defined in the `Cached` module. This ensures
|
||||
* that all of SSA construction will be evaluated in the same stage.
|
||||
*/
|
||||
module SSA {
|
||||
class MemoryLocation = Alias::MemoryLocation;
|
||||
|
||||
predicate hasPhiInstruction = Cached::hasPhiInstructionCached/2;
|
||||
|
||||
predicate hasChiInstruction = Cached::hasChiInstructionCached/1;
|
||||
|
||||
predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import semmle.code.cpp.ir.implementation.Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
import semmle.code.cpp.ir.implementation.Opcode as Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
import semmle.code.cpp.ir.internal.Overlap as Overlap
|
||||
import semmle.code.cpp.ir.implementation.internal.TInstruction as TInstruction
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as RawIR
|
||||
|
||||
@@ -2,5 +2,7 @@ import semmle.code.cpp.ir.implementation.raw.IR as OldIR
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.reachability.ReachableBlock as Reachability
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.reachability.Dominance as Dominance
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as NewIR
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as RawStage
|
||||
import semmle.code.cpp.ir.implementation.internal.TInstruction::UnaliasedSSAInstructions as SSAInstructions
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import SimpleSSA as Alias
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.Print
|
||||
private import semmle.code.cpp.ir.implementation.IRType
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Raw
|
||||
|
||||
private int getPointerSize() { result = max(any(NullPointerType t).getSize()) }
|
||||
|
||||
@@ -143,7 +143,7 @@ private predicate isOpaqueType(Type type) {
|
||||
predicate hasOpaqueType(Type tag, int byteSize) {
|
||||
isOpaqueType(tag) and byteSize = getTypeSize(tag)
|
||||
or
|
||||
tag instanceof UnknownType and IRConstruction::needsUnknownOpaqueType(byteSize)
|
||||
tag instanceof UnknownType and Raw::needsUnknownOpaqueType(byteSize)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -191,7 +191,7 @@ private newtype TCppType =
|
||||
TPRValueType(Type type) { exists(getIRTypeForPRValue(type)) } or
|
||||
TFunctionGLValueType() or
|
||||
TGLValueAddressType(Type type) or
|
||||
TUnknownOpaqueType(int byteSize) { IRConstruction::needsUnknownOpaqueType(byteSize) } or
|
||||
TUnknownOpaqueType(int byteSize) { Raw::needsUnknownOpaqueType(byteSize) } or
|
||||
TUnknownType()
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Provides a class for modeling `printf`-style formatting functions. To use
|
||||
* this QL library, create a QL class extending `DataFlowFunction` with a
|
||||
* this QL library, create a QL class extending `FormattingFunction` with a
|
||||
* characteristic predicate that selects the function or set of functions you
|
||||
* are modeling. Within that class, override the predicates provided by
|
||||
* `FormattingFunction` to match the flow within that function.
|
||||
|
||||
@@ -1455,7 +1455,7 @@ class SwitchStmt extends ConditionalStmt, @stmt_switch {
|
||||
/**
|
||||
* Gets the body statement of this 'switch' statement.
|
||||
*
|
||||
* In almost all cases the result will be a `BlockStmt`, but there are
|
||||
* In almost all cases the result will be a `Block`, but there are
|
||||
* other syntactically valid constructions.
|
||||
*
|
||||
* For example, for
|
||||
|
||||
159
cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/stl.cpp
Normal file
159
cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/stl.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
|
||||
#include "shared.h"
|
||||
|
||||
typedef unsigned long size_t;
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<class charT> struct char_traits;
|
||||
|
||||
typedef size_t streamsize;
|
||||
|
||||
template <class T> class allocator {
|
||||
public:
|
||||
allocator() throw();
|
||||
};
|
||||
|
||||
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
|
||||
class basic_string {
|
||||
public:
|
||||
explicit basic_string(const Allocator& a = Allocator());
|
||||
basic_string(const charT* s, const Allocator& a = Allocator());
|
||||
|
||||
const charT* c_str() const;
|
||||
};
|
||||
|
||||
typedef basic_string<char> string;
|
||||
|
||||
template <class charT, class traits = char_traits<charT> >
|
||||
class basic_istream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
|
||||
public:
|
||||
basic_istream<charT,traits>& operator>>(int& n);
|
||||
};
|
||||
|
||||
template <class charT, class traits = char_traits<charT> >
|
||||
class basic_ostream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
|
||||
public:
|
||||
typedef charT char_type;
|
||||
basic_ostream<charT,traits>& write(const char_type* s, streamsize n);
|
||||
|
||||
basic_ostream<charT, traits>& operator<<(int n);
|
||||
};
|
||||
|
||||
template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);
|
||||
template<class charT, class traits, class Allocator> basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>& os, const basic_string<charT, traits, Allocator>& str);
|
||||
|
||||
template<class charT, class traits = char_traits<charT>>
|
||||
class basic_iostream : public basic_istream<charT, traits>, public basic_ostream<charT, traits> {
|
||||
public:
|
||||
};
|
||||
|
||||
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>>
|
||||
class basic_stringstream : public basic_iostream<charT, traits> {
|
||||
public:
|
||||
explicit basic_stringstream(/*ios_base::openmode which = ios_base::out|ios_base::in - not needed for this test*/);
|
||||
|
||||
basic_string<charT, traits, Allocator> str() const;
|
||||
};
|
||||
|
||||
using stringstream = basic_stringstream<char>;
|
||||
}
|
||||
|
||||
char *source() { return getenv("USERDATA"); }
|
||||
void sink(const std::string &s) {};
|
||||
void sink(const std::stringstream &s) {};
|
||||
|
||||
void test_string()
|
||||
{
|
||||
char *a = source();
|
||||
std::string b("123");
|
||||
std::string c(source());
|
||||
|
||||
sink(a); // tainted
|
||||
sink(b);
|
||||
sink(c); // tainted [NOT DETECTED]
|
||||
sink(b.c_str());
|
||||
sink(c.c_str()); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test_stringstream()
|
||||
{
|
||||
std::stringstream ss1, ss2, ss3, ss4, ss5;
|
||||
std::string t(source());
|
||||
|
||||
ss1 << "1234";
|
||||
ss2 << source();
|
||||
ss3 << "123" << source();
|
||||
ss4 << source() << "456";
|
||||
ss5 << t;
|
||||
|
||||
sink(ss1);
|
||||
sink(ss2); // tainted [NOT DETECTED]
|
||||
sink(ss3); // tainted [NOT DETECTED]
|
||||
sink(ss4); // tainted [NOT DETECTED]
|
||||
sink(ss5); // tainted [NOT DETECTED]
|
||||
sink(ss1.str());
|
||||
sink(ss2.str()); // tainted [NOT DETECTED]
|
||||
sink(ss3.str()); // tainted [NOT DETECTED]
|
||||
sink(ss4.str()); // tainted [NOT DETECTED]
|
||||
sink(ss5.str()); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test_stringstream_int(int source)
|
||||
{
|
||||
std::stringstream ss1, ss2;
|
||||
|
||||
ss1 << 1234;
|
||||
ss2 << source;
|
||||
|
||||
sink(ss1);
|
||||
sink(ss2); // tainted [NOT DETECTED]
|
||||
sink(ss1.str());
|
||||
sink(ss2.str()); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
char *user_input() {
|
||||
return source();
|
||||
}
|
||||
|
||||
void sink(const char *filename, const char *mode);
|
||||
|
||||
void test_strings2()
|
||||
{
|
||||
string path1 = user_input();
|
||||
sink(path1.c_str(), "r"); // tainted [NOT DETECTED]
|
||||
|
||||
string path2;
|
||||
path2 = user_input();
|
||||
sink(path2.c_str(), "r"); // tainted
|
||||
|
||||
string path3(user_input());
|
||||
sink(path3.c_str(), "r"); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test_string3()
|
||||
{
|
||||
const char *cs = source();
|
||||
|
||||
// convert char * -> std::string
|
||||
std::string ss(cs);
|
||||
|
||||
sink(cs); // tainted
|
||||
sink(ss); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test_string4()
|
||||
{
|
||||
const char *cs = source();
|
||||
|
||||
// convert char * -> std::string
|
||||
std::string ss(cs);
|
||||
|
||||
// convert back std::string -> char *
|
||||
cs = ss.c_str();
|
||||
|
||||
sink(cs); // tainted [NOT DETECTED]
|
||||
sink(ss); // tainted [NOT DETECTED]
|
||||
}
|
||||
@@ -153,6 +153,50 @@
|
||||
| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:13:15:13:20 | call to getenv |
|
||||
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:16:15:16:21 | global2 |
|
||||
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:23:15:23:20 | call to getenv |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | shared.h:5:23:5:31 | sinkparam |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:21:29:21:29 | s |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:43:114:43:118 | p#1 |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:62:25:62:30 | call to getenv |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:68:8:68:8 | a |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:68:12:68:17 | call to source |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:21 | call to source |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:23 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:24 | call to basic_string |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:72:7:72:7 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:72:7:72:7 | a |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:21 | call to source |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:23 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:24 | call to basic_string |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:9:85:14 | call to source |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:9:85:16 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:18:86:23 | call to source |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:18:86:25 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:9:87:14 | call to source |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:9:87:16 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:118:10:118:15 | call to source |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:16:125:28 | call to basic_string |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:17:125:26 | call to user_input |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:17:125:28 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:128:9:128:13 | path2 |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:19 | call to user_input |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | call to basic_string |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | (const basic_string<char, char_traits<char>, allocator<char>>)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | path2 |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:24 | call to user_input |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:26 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:27 | call to basic_string |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:138:14:138:15 | cs |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:138:19:138:24 | call to source |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:138:19:138:26 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:141:17:141:18 | cs |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:141:17:141:19 | call to basic_string |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:143:7:143:8 | cs |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:149:14:149:15 | cs |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:149:19:149:24 | call to source |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:149:19:149:26 | (const char *)... |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:152:17:152:18 | cs |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:152:17:152:19 | call to basic_string |
|
||||
| test_diff.cpp:92:10:92:13 | argv | shared.h:5:23:5:31 | sinkparam |
|
||||
| test_diff.cpp:92:10:92:13 | argv | test_diff.cpp:92:10:92:13 | argv |
|
||||
| test_diff.cpp:92:10:92:13 | argv | test_diff.cpp:92:10:92:16 | (const char *)... |
|
||||
|
||||
@@ -31,6 +31,20 @@
|
||||
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:150:13:150:14 | & ... | IR only |
|
||||
| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:13:5:13:11 | global1 | AST only |
|
||||
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:23:5:23:11 | global2 | AST only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:62:7:62:12 | source | AST only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:24 | call to basic_string | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:24 | call to basic_string | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:9:87:16 | (const char *)... | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:117:7:117:16 | user_input | AST only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:16:125:28 | call to basic_string | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:128:9:128:13 | path2 | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | call to basic_string | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | (const basic_string<char, char_traits<char>, allocator<char>>)... | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | path2 | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:27 | call to basic_string | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:141:17:141:19 | call to basic_string | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:152:17:152:19 | call to basic_string | IR only |
|
||||
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:157:7:157:8 | cs | AST only |
|
||||
| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:11:104:20 | (...) | IR only |
|
||||
| test_diff.cpp:108:10:108:13 | argv | test_diff.cpp:36:24:36:24 | p | AST only |
|
||||
| test_diff.cpp:111:10:111:13 | argv | shared.h:5:23:5:31 | sinkparam | AST only |
|
||||
|
||||
@@ -53,8 +53,8 @@ public:
|
||||
{
|
||||
B *b = new B();
|
||||
b->set(new C1());
|
||||
sink(b->get()); // $ast $f-:ir
|
||||
sink((new B(new C()))->get()); // $ast $f-:ir
|
||||
sink(b->get()); // $ast $ir=55:12
|
||||
sink((new B(new C()))->get()); // $ast $ir
|
||||
}
|
||||
|
||||
void f3()
|
||||
@@ -129,7 +129,7 @@ public:
|
||||
{
|
||||
B *b = new B();
|
||||
f7(b);
|
||||
sink(b->c); // $ast $f-:ir
|
||||
sink(b->c); // $ast,ir
|
||||
}
|
||||
|
||||
class D
|
||||
@@ -149,7 +149,7 @@ public:
|
||||
{
|
||||
B *b = new B();
|
||||
D *d = new D(b, r());
|
||||
sink(d->b); // $ast=143:25 $ast=150:12 $f-:ir
|
||||
sink(d->b); // $ast,ir=143:25 $ast,ir=150:12
|
||||
sink(d->b->c); // $ast $f-:ir
|
||||
sink(b->c); // $ast,ir
|
||||
}
|
||||
|
||||
@@ -26,9 +26,9 @@ public:
|
||||
|
||||
void func()
|
||||
{
|
||||
sink(s1); // $ast $f-:ir
|
||||
sink(s1); // $ast $ir
|
||||
sink(s2); // $f-:ast $f-:ir
|
||||
sink(s3); // $ast $f-:ir
|
||||
sink(s3); // $ast $ir
|
||||
sink(s4); // $f-:ast $f-:ir
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class Conf extends Configuration {
|
||||
override predicate isSink(Node sink) {
|
||||
exists(Call c |
|
||||
c.getTarget().hasName("sink") and
|
||||
c.getAnArgument() = sink.asExpr()
|
||||
c.getAnArgument() = sink.asConvertedExpr()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
41
cpp/ql/test/library-tests/dataflow/fields/Nodes.qll
Normal file
41
cpp/ql/test/library-tests/dataflow/fields/Nodes.qll
Normal file
@@ -0,0 +1,41 @@
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow as IR
|
||||
private import semmle.code.cpp.dataflow.DataFlow as AST
|
||||
private import cpp
|
||||
|
||||
private newtype TNode =
|
||||
TASTNode(AST::DataFlow::Node n) or
|
||||
TIRNode(IR::DataFlow::Node n)
|
||||
|
||||
class Node extends TNode {
|
||||
string toString() { none() }
|
||||
|
||||
IR::DataFlow::Node asIR() { none() }
|
||||
|
||||
AST::DataFlow::Node asAST() { none() }
|
||||
|
||||
Location getLocation() { none() }
|
||||
}
|
||||
|
||||
class ASTNode extends Node, TASTNode {
|
||||
AST::DataFlow::Node n;
|
||||
|
||||
ASTNode() { this = TASTNode(n) }
|
||||
|
||||
override string toString() { result = n.toString() }
|
||||
|
||||
override AST::DataFlow::Node asAST() { result = n }
|
||||
|
||||
override Location getLocation() { result = n.getLocation() }
|
||||
}
|
||||
|
||||
class IRNode extends Node, TIRNode {
|
||||
IR::DataFlow::Node n;
|
||||
|
||||
IRNode() { this = TIRNode(n) }
|
||||
|
||||
override string toString() { result = n.toString() }
|
||||
|
||||
override IR::DataFlow::Node asIR() { result = n }
|
||||
|
||||
override Location getLocation() { result = n.getLocation() }
|
||||
}
|
||||
@@ -48,19 +48,19 @@ struct S {
|
||||
void test_setDirectly() {
|
||||
S s;
|
||||
s.setDirectly(user_input());
|
||||
sink(s.getDirectly()); // $ast $f-:ir
|
||||
sink(s.getDirectly()); // $ast $ir
|
||||
}
|
||||
|
||||
void test_setIndirectly() {
|
||||
S s;
|
||||
s.setIndirectly(user_input());
|
||||
sink(s.getIndirectly()); // $ast $f-:ir
|
||||
sink(s.getIndirectly()); // $ast $ir
|
||||
}
|
||||
|
||||
void test_setThroughNonMember() {
|
||||
S s;
|
||||
s.setThroughNonMember(user_input());
|
||||
sink(s.getThroughNonMember()); // $ast $f-:ir
|
||||
sink(s.getThroughNonMember()); // $ast $ir
|
||||
}
|
||||
|
||||
void test_nonMemberSetA() {
|
||||
|
||||
@@ -48,8 +48,8 @@ void bar(Outer &b)
|
||||
// in _some_ access path somewhere in the search. That makes the library conclude
|
||||
// that there could be flow to `b.inner.f.a_` even when the flow was actually to
|
||||
// `b.inner.f.b_`.
|
||||
sink(b.inner.f.a()); // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $f-:ir
|
||||
sink(b.inner.f.b()); // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f-:ir
|
||||
sink(b.inner.f.a()); // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19
|
||||
sink(b.inner.f.b()); // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19
|
||||
}
|
||||
|
||||
void foo()
|
||||
|
||||
@@ -25,8 +25,8 @@ public:
|
||||
|
||||
void bar(Foo &f)
|
||||
{
|
||||
sink(f.a()); //$ast=34:11 $ast=36:11 $f-:ir
|
||||
sink(f.b()); //$ast=35:14 $ast=36:25 $f-:ir
|
||||
sink(f.a()); //$ast=34:11 $ast=36:11 $ir=34:11 $ir=36:11
|
||||
sink(f.b()); //$ast=35:14 $ast=36:25 $ir=35:14 $ir=36:25
|
||||
}
|
||||
|
||||
void foo()
|
||||
|
||||
36
cpp/ql/test/library-tests/dataflow/fields/flow-diff.expected
Normal file
36
cpp/ql/test/library-tests/dataflow/fields/flow-diff.expected
Normal file
@@ -0,0 +1,36 @@
|
||||
| A.cpp:41:15:41:21 | new | A.cpp:43:10:43:12 | & ... | AST only |
|
||||
| A.cpp:47:12:47:18 | new | A.cpp:49:13:49:13 | c | AST only |
|
||||
| A.cpp:64:21:64:28 | new | A.cpp:66:14:66:14 | c | AST only |
|
||||
| A.cpp:73:25:73:32 | new | A.cpp:75:14:75:14 | c | AST only |
|
||||
| A.cpp:98:12:98:18 | new | A.cpp:120:16:120:16 | a | AST only |
|
||||
| A.cpp:142:14:142:20 | new | A.cpp:153:16:153:16 | c | AST only |
|
||||
| A.cpp:159:12:159:18 | new | A.cpp:165:26:165:29 | head | AST only |
|
||||
| A.cpp:159:12:159:18 | new | A.cpp:169:15:169:18 | head | AST only |
|
||||
| B.cpp:6:15:6:24 | new | B.cpp:9:20:9:24 | elem1 | AST only |
|
||||
| B.cpp:15:15:15:27 | new | B.cpp:19:20:19:24 | elem2 | AST only |
|
||||
| D.cpp:28:15:28:24 | new | D.cpp:22:25:22:31 | call to getElem | AST only |
|
||||
| D.cpp:35:15:35:24 | new | D.cpp:22:25:22:31 | call to getElem | AST only |
|
||||
| D.cpp:42:15:42:24 | new | D.cpp:22:25:22:31 | call to getElem | AST only |
|
||||
| D.cpp:49:15:49:24 | new | D.cpp:22:25:22:31 | call to getElem | AST only |
|
||||
| D.cpp:56:15:56:24 | new | D.cpp:64:25:64:28 | elem | AST only |
|
||||
| E.cpp:28:21:28:23 | ref arg raw | E.cpp:31:10:31:12 | raw | AST only |
|
||||
| E.cpp:29:24:29:29 | ref arg buffer | E.cpp:32:13:32:18 | buffer | AST only |
|
||||
| E.cpp:30:28:30:33 | ref arg buffer | E.cpp:21:18:21:23 | buffer | AST only |
|
||||
| aliasing.cpp:37:13:37:22 | call to user_input | aliasing.cpp:38:11:38:12 | m1 | IR only |
|
||||
| aliasing.cpp:42:11:42:20 | call to user_input | aliasing.cpp:43:13:43:14 | m1 | IR only |
|
||||
| aliasing.cpp:79:11:79:20 | call to user_input | aliasing.cpp:80:12:80:13 | m1 | IR only |
|
||||
| aliasing.cpp:86:10:86:19 | call to user_input | aliasing.cpp:87:12:87:13 | m1 | IR only |
|
||||
| by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:111:25:111:25 | a | AST only |
|
||||
| by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:115:27:115:27 | a | AST only |
|
||||
| by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:131:25:131:25 | a | AST only |
|
||||
| by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:135:27:135:27 | a | AST only |
|
||||
| by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:132:14:132:14 | a | AST only |
|
||||
| by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:136:16:136:16 | a | AST only |
|
||||
| qualifiers.cpp:22:27:22:36 | call to user_input | qualifiers.cpp:23:23:23:23 | a | AST only |
|
||||
| qualifiers.cpp:27:28:27:37 | call to user_input | qualifiers.cpp:28:23:28:23 | a | AST only |
|
||||
| qualifiers.cpp:32:35:32:44 | call to user_input | qualifiers.cpp:33:23:33:23 | a | AST only |
|
||||
| qualifiers.cpp:37:38:37:47 | call to user_input | qualifiers.cpp:38:23:38:23 | a | AST only |
|
||||
| qualifiers.cpp:42:29:42:38 | call to user_input | qualifiers.cpp:43:23:43:23 | a | AST only |
|
||||
| qualifiers.cpp:47:31:47:40 | call to user_input | qualifiers.cpp:48:23:48:23 | a | AST only |
|
||||
| struct_init.c:20:20:20:29 | call to user_input | struct_init.c:33:25:33:25 | a | AST only |
|
||||
| struct_init.c:40:20:40:29 | call to user_input | struct_init.c:15:12:15:12 | a | AST only |
|
||||
31
cpp/ql/test/library-tests/dataflow/fields/flow-diff.ql
Normal file
31
cpp/ql/test/library-tests/dataflow/fields/flow-diff.ql
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* @kind problem
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import Nodes
|
||||
import IRConfiguration as IRConf
|
||||
import ASTConfiguration as ASTConf
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow as IR
|
||||
private import semmle.code.cpp.dataflow.DataFlow as AST
|
||||
|
||||
from Node source, Node sink, IRConf::Conf irConf, ASTConf::Conf astConf, string msg
|
||||
where
|
||||
irConf.hasFlow(source.asIR(), sink.asIR()) and
|
||||
not exists(AST::DataFlow::Node astSource, AST::DataFlow::Node astSink |
|
||||
astSource.asExpr() = source.asIR().asExpr() and
|
||||
astSink.asExpr() = sink.asIR().asExpr()
|
||||
|
|
||||
astConf.hasFlow(astSource, astSink)
|
||||
) and
|
||||
msg = "IR only"
|
||||
or
|
||||
astConf.hasFlow(source.asAST(), sink.asAST()) and
|
||||
not exists(IR::DataFlow::Node irSource, IR::DataFlow::Node irSink |
|
||||
irSource.asExpr() = source.asAST().asExpr() and
|
||||
irSink.asExpr() = sink.asAST().asExpr()
|
||||
|
|
||||
irConf.hasFlow(irSource, irSink)
|
||||
) and
|
||||
msg = "AST only"
|
||||
select source, sink, msg
|
||||
@@ -1,19 +1,55 @@
|
||||
edges
|
||||
| A.cpp:55:5:55:5 | set output argument [c] | A.cpp:56:10:56:10 | Argument -1 indirection [c] |
|
||||
| A.cpp:55:12:55:19 | (C *)... | A.cpp:55:5:55:5 | set output argument [c] |
|
||||
| A.cpp:55:12:55:19 | new | A.cpp:55:12:55:19 | (C *)... |
|
||||
| A.cpp:56:10:56:10 | Argument -1 indirection [c] | A.cpp:56:13:56:15 | call to get |
|
||||
| A.cpp:57:10:57:25 | Argument -1 indirection [c] | A.cpp:57:28:57:30 | call to get |
|
||||
| A.cpp:57:11:57:24 | B output argument [c] | A.cpp:57:10:57:25 | Argument -1 indirection [c] |
|
||||
| A.cpp:57:17:57:23 | new | A.cpp:57:11:57:24 | B output argument [c] |
|
||||
| A.cpp:98:12:98:18 | new | A.cpp:100:5:100:13 | Store |
|
||||
| A.cpp:100:5:100:13 | Store | A.cpp:100:5:100:13 | a [a] |
|
||||
| A.cpp:100:5:100:13 | a [a] | A.cpp:101:8:101:9 | Argument 0 indirection [a] |
|
||||
| A.cpp:101:8:101:9 | Argument 0 indirection [a] | A.cpp:103:14:103:14 | *c [a] |
|
||||
| A.cpp:103:14:103:14 | *c [a] | A.cpp:107:16:107:16 | a |
|
||||
| A.cpp:107:16:107:16 | a | A.cpp:107:12:107:16 | (void *)... |
|
||||
| A.cpp:107:16:107:16 | a | A.cpp:107:16:107:16 | a |
|
||||
| A.cpp:126:5:126:5 | Chi [c] | A.cpp:131:8:131:8 | f7 output argument [c] |
|
||||
| A.cpp:126:5:126:5 | set output argument [c] | A.cpp:126:5:126:5 | Chi [c] |
|
||||
| A.cpp:126:12:126:18 | new | A.cpp:126:5:126:5 | set output argument [c] |
|
||||
| A.cpp:131:8:131:8 | Chi [c] | A.cpp:132:13:132:13 | c |
|
||||
| A.cpp:131:8:131:8 | f7 output argument [c] | A.cpp:131:8:131:8 | Chi [c] |
|
||||
| A.cpp:132:13:132:13 | c | A.cpp:132:13:132:13 | c |
|
||||
| A.cpp:142:7:142:20 | Chi [c] | A.cpp:151:18:151:18 | D output argument [c] |
|
||||
| A.cpp:142:7:142:20 | Store | A.cpp:142:7:142:20 | c [c] |
|
||||
| A.cpp:142:7:142:20 | c [c] | A.cpp:142:7:142:20 | Chi [c] |
|
||||
| A.cpp:142:14:142:20 | new | A.cpp:142:7:142:20 | Store |
|
||||
| A.cpp:143:7:143:31 | Chi [b] | A.cpp:151:12:151:24 | D output argument [b] |
|
||||
| A.cpp:143:7:143:31 | Store | A.cpp:143:7:143:31 | b [b] |
|
||||
| A.cpp:143:7:143:31 | b [b] | A.cpp:143:7:143:31 | Chi [b] |
|
||||
| A.cpp:143:25:143:31 | new | A.cpp:143:7:143:31 | Store |
|
||||
| A.cpp:150:12:150:18 | new | A.cpp:151:18:151:18 | b |
|
||||
| A.cpp:151:12:151:24 | Chi [b] | A.cpp:152:13:152:13 | b |
|
||||
| A.cpp:151:12:151:24 | D output argument [b] | A.cpp:151:12:151:24 | Chi [b] |
|
||||
| A.cpp:151:18:151:18 | Chi [c] | A.cpp:154:13:154:13 | c |
|
||||
| A.cpp:151:18:151:18 | D output argument [c] | A.cpp:151:18:151:18 | Chi [c] |
|
||||
| A.cpp:154:13:154:13 | c | A.cpp:154:10:154:13 | (void *)... |
|
||||
| A.cpp:151:18:151:18 | b | A.cpp:151:12:151:24 | D output argument [b] |
|
||||
| A.cpp:152:13:152:13 | b | A.cpp:152:13:152:13 | b |
|
||||
| A.cpp:154:13:154:13 | c | A.cpp:154:13:154:13 | c |
|
||||
| C.cpp:18:12:18:18 | C output argument [s1] | C.cpp:19:5:19:5 | Argument -1 indirection [s1] |
|
||||
| C.cpp:18:12:18:18 | C output argument [s3] | C.cpp:19:5:19:5 | Argument -1 indirection [s3] |
|
||||
| C.cpp:19:5:19:5 | Argument -1 indirection [s1] | C.cpp:27:8:27:11 | *#this [s1] |
|
||||
| C.cpp:19:5:19:5 | Argument -1 indirection [s3] | C.cpp:27:8:27:11 | *#this [s3] |
|
||||
| C.cpp:22:12:22:21 | Store | C.cpp:22:12:22:21 | s1 [s1] |
|
||||
| C.cpp:22:12:22:21 | new | C.cpp:22:12:22:21 | Store |
|
||||
| C.cpp:22:12:22:21 | s1 [s1] | C.cpp:24:5:24:25 | Chi [s1] |
|
||||
| C.cpp:24:5:24:25 | Chi [s1] | C.cpp:18:12:18:18 | C output argument [s1] |
|
||||
| C.cpp:24:5:24:25 | Chi [s3] | C.cpp:18:12:18:18 | C output argument [s3] |
|
||||
| C.cpp:24:5:24:25 | Store | C.cpp:24:5:24:25 | s3 [s3] |
|
||||
| C.cpp:24:5:24:25 | s3 [s3] | C.cpp:24:5:24:25 | Chi [s3] |
|
||||
| C.cpp:24:16:24:25 | new | C.cpp:24:5:24:25 | Store |
|
||||
| C.cpp:27:8:27:11 | *#this [s1] | C.cpp:29:10:29:11 | s1 |
|
||||
| C.cpp:27:8:27:11 | *#this [s3] | C.cpp:31:10:31:11 | s3 |
|
||||
| C.cpp:29:10:29:11 | s1 | C.cpp:29:10:29:11 | s1 |
|
||||
| C.cpp:31:10:31:11 | s3 | C.cpp:31:10:31:11 | s3 |
|
||||
| aliasing.cpp:9:3:9:22 | Chi [m1] | aliasing.cpp:25:17:25:19 | pointerSetter output argument [m1] |
|
||||
| aliasing.cpp:9:3:9:22 | Store | aliasing.cpp:9:3:9:22 | m1 [m1] |
|
||||
| aliasing.cpp:9:3:9:22 | m1 [m1] | aliasing.cpp:9:3:9:22 | Chi [m1] |
|
||||
@@ -38,6 +74,15 @@ edges
|
||||
| aliasing.cpp:79:11:79:20 | call to user_input | aliasing.cpp:80:12:80:13 | m1 |
|
||||
| aliasing.cpp:86:10:86:19 | call to user_input | aliasing.cpp:87:12:87:13 | m1 |
|
||||
| aliasing.cpp:92:12:92:21 | call to user_input | aliasing.cpp:93:12:93:13 | m1 |
|
||||
| by_reference.cpp:50:3:50:3 | setDirectly output argument [a] | by_reference.cpp:51:8:51:8 | Argument -1 indirection [a] |
|
||||
| by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:50:3:50:3 | setDirectly output argument [a] |
|
||||
| by_reference.cpp:51:8:51:8 | Argument -1 indirection [a] | by_reference.cpp:51:10:51:20 | call to getDirectly |
|
||||
| by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] | by_reference.cpp:57:8:57:8 | Argument -1 indirection [a] |
|
||||
| by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] |
|
||||
| by_reference.cpp:57:8:57:8 | Argument -1 indirection [a] | by_reference.cpp:57:10:57:22 | call to getIndirectly |
|
||||
| by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] | by_reference.cpp:63:8:63:8 | Argument -1 indirection [a] |
|
||||
| by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] |
|
||||
| by_reference.cpp:63:8:63:8 | Argument -1 indirection [a] | by_reference.cpp:63:10:63:28 | call to getThroughNonMember |
|
||||
| by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] | by_reference.cpp:69:22:69:23 | Argument 0 indirection [a] |
|
||||
| by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] |
|
||||
| by_reference.cpp:69:22:69:23 | Argument 0 indirection [a] | by_reference.cpp:69:8:69:20 | call to nonMemberGetA |
|
||||
@@ -71,6 +116,130 @@ edges
|
||||
| by_reference.cpp:130:27:130:27 | inner_nested.a [a] | by_reference.cpp:130:27:130:27 | a |
|
||||
| by_reference.cpp:134:29:134:29 | a | by_reference.cpp:134:29:134:29 | a |
|
||||
| by_reference.cpp:134:29:134:29 | inner_nested.a [a] | by_reference.cpp:134:29:134:29 | a |
|
||||
| complex.cpp:40:17:40:17 | *b [a_] | complex.cpp:51:16:51:16 | Argument -1 indirection [a_] |
|
||||
| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:51:16:51:16 | Argument -1 indirection [b_] |
|
||||
| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:52:16:52:16 | Argument -1 indirection [b_] |
|
||||
| complex.cpp:40:17:40:17 | *b [inner, b_] | complex.cpp:51:16:51:16 | inner.f [b_] |
|
||||
| complex.cpp:40:17:40:17 | *b [inner, f, ... (3)] | complex.cpp:51:16:51:16 | Argument -1 indirection [inner, f, ... (3)] |
|
||||
| complex.cpp:40:17:40:17 | *b [inner, f, ... (3)] | complex.cpp:51:16:51:16 | Chi [inner, f, ... (3)] |
|
||||
| complex.cpp:40:17:40:17 | *b [inner, f, ... (3)] | complex.cpp:51:16:51:16 | inner.f [f, a_] |
|
||||
| complex.cpp:40:17:40:17 | *b [inner, f, ... (3)] | complex.cpp:51:16:51:16 | inner.f [f, b_] |
|
||||
| complex.cpp:40:17:40:17 | *b [inner, f, ... (5)] | complex.cpp:51:16:51:16 | inner.f [f, inner, ... (4)] |
|
||||
| complex.cpp:51:16:51:16 | Argument -1 indirection [a_] | complex.cpp:51:16:51:16 | a output argument [a_] |
|
||||
| complex.cpp:51:16:51:16 | Argument -1 indirection [a_] | complex.cpp:51:18:51:18 | call to a |
|
||||
| complex.cpp:51:16:51:16 | Argument -1 indirection [b_] | complex.cpp:51:16:51:16 | a output argument [b_] |
|
||||
| complex.cpp:51:16:51:16 | Argument -1 indirection [inner, f, ... (3)] | complex.cpp:51:16:51:16 | a output argument [inner, f, ... (3)] |
|
||||
| complex.cpp:51:16:51:16 | Chi [inner, f, ... (3)] | complex.cpp:52:16:52:16 | inner.f [f, b_] |
|
||||
| complex.cpp:51:16:51:16 | a output argument [a_] | complex.cpp:51:16:51:16 | f [f, a_] |
|
||||
| complex.cpp:51:16:51:16 | a output argument [b_] | complex.cpp:51:16:51:16 | f [f, b_] |
|
||||
| complex.cpp:51:16:51:16 | a output argument [b_] | complex.cpp:52:16:52:16 | Argument -1 indirection [b_] |
|
||||
| complex.cpp:51:16:51:16 | a output argument [inner, f, ... (3)] | complex.cpp:51:16:51:16 | Chi [inner, f, ... (3)] |
|
||||
| complex.cpp:51:16:51:16 | f [a_] | complex.cpp:51:16:51:16 | Argument -1 indirection [a_] |
|
||||
| complex.cpp:51:16:51:16 | f [b_] | complex.cpp:51:16:51:16 | Argument -1 indirection [b_] |
|
||||
| complex.cpp:51:16:51:16 | f [f, a_] | complex.cpp:51:16:51:16 | inner.f [inner, f, ... (3)] |
|
||||
| complex.cpp:51:16:51:16 | f [f, b_] | complex.cpp:51:16:51:16 | inner.f [inner, f, ... (3)] |
|
||||
| complex.cpp:51:16:51:16 | f [inner, f, ... (3)] | complex.cpp:51:16:51:16 | Argument -1 indirection [inner, f, ... (3)] |
|
||||
| complex.cpp:51:16:51:16 | inner.f [b_] | complex.cpp:52:16:52:16 | Argument -1 indirection [b_] |
|
||||
| complex.cpp:51:16:51:16 | inner.f [f, a_] | complex.cpp:51:16:51:16 | f [a_] |
|
||||
| complex.cpp:51:16:51:16 | inner.f [f, b_] | complex.cpp:51:16:51:16 | f [b_] |
|
||||
| complex.cpp:51:16:51:16 | inner.f [f, inner, ... (4)] | complex.cpp:51:16:51:16 | f [inner, f, ... (3)] |
|
||||
| complex.cpp:51:16:51:16 | inner.f [inner, f, ... (3)] | complex.cpp:51:16:51:16 | Chi [inner, f, ... (3)] |
|
||||
| complex.cpp:52:16:52:16 | Argument -1 indirection [b_] | complex.cpp:52:18:52:18 | call to b |
|
||||
| complex.cpp:52:16:52:16 | f [b_] | complex.cpp:52:16:52:16 | Argument -1 indirection [b_] |
|
||||
| complex.cpp:52:16:52:16 | inner.f [f, b_] | complex.cpp:52:16:52:16 | f [b_] |
|
||||
| complex.cpp:62:12:62:12 | f [f, a_] | complex.cpp:62:12:62:12 | inner.f [inner, f, ... (3)] |
|
||||
| complex.cpp:62:12:62:12 | inner.f [inner, f, ... (3)] | complex.cpp:68:7:68:8 | Argument 0 indirection [inner, f, ... (3)] |
|
||||
| complex.cpp:62:12:62:12 | setA output argument [a_] | complex.cpp:62:12:62:12 | f [f, a_] |
|
||||
| complex.cpp:62:12:62:12 | setA output argument [a_] | complex.cpp:68:7:68:8 | Argument 0 indirection [a_] |
|
||||
| complex.cpp:62:19:62:28 | call to user_input | complex.cpp:62:12:62:12 | setA output argument [a_] |
|
||||
| complex.cpp:63:12:63:12 | f [f, b_] | complex.cpp:63:12:63:12 | inner.f [inner, f, ... (3)] |
|
||||
| complex.cpp:63:12:63:12 | inner.f [inner, f, ... (3)] | complex.cpp:71:7:71:8 | Argument 0 indirection [inner, f, ... (3)] |
|
||||
| complex.cpp:63:12:63:12 | setB output argument [b_] | complex.cpp:63:12:63:12 | f [f, b_] |
|
||||
| complex.cpp:63:12:63:12 | setB output argument [b_] | complex.cpp:71:7:71:8 | Argument 0 indirection [b_] |
|
||||
| complex.cpp:63:19:63:28 | call to user_input | complex.cpp:63:12:63:12 | setB output argument [b_] |
|
||||
| complex.cpp:64:12:64:12 | Chi [inner, f, ... (3)] | complex.cpp:65:12:65:12 | inner.f [f, a_] |
|
||||
| complex.cpp:64:12:64:12 | Chi [inner, f, ... (3)] | complex.cpp:65:12:65:12 | inner.f [f, b_] |
|
||||
| complex.cpp:64:12:64:12 | Chi [inner, f, ... (3)] | complex.cpp:65:12:65:12 | inner.f [f, b_] |
|
||||
| complex.cpp:64:12:64:12 | f [f, a_] | complex.cpp:64:12:64:12 | inner.f [inner, f, ... (3)] |
|
||||
| complex.cpp:64:12:64:12 | inner.f [inner, f, ... (3)] | complex.cpp:64:12:64:12 | Chi [inner, f, ... (3)] |
|
||||
| complex.cpp:64:12:64:12 | inner.f [inner, f, ... (3)] | complex.cpp:65:12:65:12 | Argument -1 indirection [inner, f, ... (3)] |
|
||||
| complex.cpp:64:12:64:12 | inner.f [inner, f, ... (3)] | complex.cpp:74:7:74:8 | Argument 0 indirection [inner, f, ... (3)] |
|
||||
| complex.cpp:64:12:64:12 | setA output argument [a_] | complex.cpp:64:12:64:12 | f [f, a_] |
|
||||
| complex.cpp:64:12:64:12 | setA output argument [a_] | complex.cpp:65:12:65:12 | Argument -1 indirection [a_] |
|
||||
| complex.cpp:64:12:64:12 | setA output argument [a_] | complex.cpp:74:7:74:8 | Argument 0 indirection [a_] |
|
||||
| complex.cpp:64:19:64:28 | call to user_input | complex.cpp:64:12:64:12 | setA output argument [a_] |
|
||||
| complex.cpp:65:12:65:12 | Argument -1 indirection [a_] | complex.cpp:65:12:65:12 | setB output argument [a_] |
|
||||
| complex.cpp:65:12:65:12 | Argument -1 indirection [b_] | complex.cpp:65:12:65:12 | setB output argument [b_] |
|
||||
| complex.cpp:65:12:65:12 | Argument -1 indirection [inner, f, ... (3)] | complex.cpp:65:12:65:12 | setB output argument [inner, f, ... (3)] |
|
||||
| complex.cpp:65:12:65:12 | f [a_] | complex.cpp:65:12:65:12 | Argument -1 indirection [a_] |
|
||||
| complex.cpp:65:12:65:12 | f [b_] | complex.cpp:65:12:65:12 | Argument -1 indirection [b_] |
|
||||
| complex.cpp:65:12:65:12 | f [b_] | complex.cpp:65:12:65:12 | inner.f [inner, b_] |
|
||||
| complex.cpp:65:12:65:12 | f [f, a_] | complex.cpp:65:12:65:12 | inner.f [inner, f, ... (3)] |
|
||||
| complex.cpp:65:12:65:12 | f [f, b_] | complex.cpp:65:12:65:12 | inner.f [inner, f, ... (3)] |
|
||||
| complex.cpp:65:12:65:12 | f [f, inner, ... (4)] | complex.cpp:65:12:65:12 | inner.f [inner, f, ... (5)] |
|
||||
| complex.cpp:65:12:65:12 | inner.f [f, a_] | complex.cpp:65:12:65:12 | f [a_] |
|
||||
| complex.cpp:65:12:65:12 | inner.f [f, b_] | complex.cpp:65:12:65:12 | f [b_] |
|
||||
| complex.cpp:65:12:65:12 | inner.f [f, b_] | complex.cpp:65:12:65:12 | f [b_] |
|
||||
| complex.cpp:65:12:65:12 | inner.f [inner, b_] | complex.cpp:74:7:74:8 | Argument 0 indirection [inner, b_] |
|
||||
| complex.cpp:65:12:65:12 | inner.f [inner, f, ... (3)] | complex.cpp:74:7:74:8 | Argument 0 indirection [inner, f, ... (3)] |
|
||||
| complex.cpp:65:12:65:12 | inner.f [inner, f, ... (5)] | complex.cpp:74:7:74:8 | Argument 0 indirection [inner, f, ... (5)] |
|
||||
| complex.cpp:65:12:65:12 | setB output argument [a_] | complex.cpp:65:12:65:12 | f [f, a_] |
|
||||
| complex.cpp:65:12:65:12 | setB output argument [a_] | complex.cpp:74:7:74:8 | Argument 0 indirection [a_] |
|
||||
| complex.cpp:65:12:65:12 | setB output argument [b_] | complex.cpp:65:12:65:12 | f [f, b_] |
|
||||
| complex.cpp:65:12:65:12 | setB output argument [b_] | complex.cpp:74:7:74:8 | Argument 0 indirection [b_] |
|
||||
| complex.cpp:65:12:65:12 | setB output argument [inner, f, ... (3)] | complex.cpp:65:12:65:12 | f [f, inner, ... (4)] |
|
||||
| complex.cpp:65:12:65:12 | setB output argument [inner, f, ... (3)] | complex.cpp:74:7:74:8 | Argument 0 indirection [inner, f, ... (3)] |
|
||||
| complex.cpp:65:19:65:28 | call to user_input | complex.cpp:65:12:65:12 | setB output argument [b_] |
|
||||
| complex.cpp:68:7:68:8 | Argument 0 indirection [a_] | complex.cpp:40:17:40:17 | *b [a_] |
|
||||
| complex.cpp:68:7:68:8 | Argument 0 indirection [inner, f, ... (3)] | complex.cpp:40:17:40:17 | *b [inner, f, ... (3)] |
|
||||
| complex.cpp:71:7:71:8 | Argument 0 indirection [b_] | complex.cpp:40:17:40:17 | *b [b_] |
|
||||
| complex.cpp:71:7:71:8 | Argument 0 indirection [inner, f, ... (3)] | complex.cpp:40:17:40:17 | *b [inner, f, ... (3)] |
|
||||
| complex.cpp:74:7:74:8 | Argument 0 indirection [a_] | complex.cpp:40:17:40:17 | *b [a_] |
|
||||
| complex.cpp:74:7:74:8 | Argument 0 indirection [b_] | complex.cpp:40:17:40:17 | *b [b_] |
|
||||
| complex.cpp:74:7:74:8 | Argument 0 indirection [inner, b_] | complex.cpp:40:17:40:17 | *b [inner, b_] |
|
||||
| complex.cpp:74:7:74:8 | Argument 0 indirection [inner, f, ... (3)] | complex.cpp:40:17:40:17 | *b [inner, f, ... (3)] |
|
||||
| complex.cpp:74:7:74:8 | Argument 0 indirection [inner, f, ... (5)] | complex.cpp:40:17:40:17 | *b [inner, f, ... (5)] |
|
||||
| constructors.cpp:26:15:26:15 | *f [a_] | constructors.cpp:28:10:28:10 | Argument -1 indirection [a_] |
|
||||
| constructors.cpp:26:15:26:15 | *f [b_] | constructors.cpp:28:10:28:10 | Argument -1 indirection [b_] |
|
||||
| constructors.cpp:26:15:26:15 | *f [b_] | constructors.cpp:29:10:29:10 | Argument -1 indirection [b_] |
|
||||
| constructors.cpp:28:10:28:10 | Argument -1 indirection [a_] | constructors.cpp:28:12:28:12 | call to a |
|
||||
| constructors.cpp:28:10:28:10 | Argument -1 indirection [b_] | constructors.cpp:28:10:28:10 | a output argument [b_] |
|
||||
| constructors.cpp:28:10:28:10 | a output argument [b_] | constructors.cpp:29:10:29:10 | Argument -1 indirection [b_] |
|
||||
| constructors.cpp:29:10:29:10 | Argument -1 indirection [b_] | constructors.cpp:29:12:29:12 | call to b |
|
||||
| constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:34:11:34:26 | Foo output argument [a_] |
|
||||
| constructors.cpp:34:11:34:26 | Foo output argument [a_] | constructors.cpp:40:9:40:9 | Argument 0 indirection [a_] |
|
||||
| constructors.cpp:35:11:35:26 | Foo output argument [b_] | constructors.cpp:43:9:43:9 | Argument 0 indirection [b_] |
|
||||
| constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:35:11:35:26 | Foo output argument [b_] |
|
||||
| constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:36:11:36:37 | Foo output argument [a_] |
|
||||
| constructors.cpp:36:11:36:37 | Foo output argument [a_] | constructors.cpp:46:9:46:9 | Argument 0 indirection [a_] |
|
||||
| constructors.cpp:36:11:36:37 | Foo output argument [b_] | constructors.cpp:46:9:46:9 | Argument 0 indirection [b_] |
|
||||
| constructors.cpp:36:25:36:34 | call to user_input | constructors.cpp:36:11:36:37 | Foo output argument [b_] |
|
||||
| constructors.cpp:40:9:40:9 | Argument 0 indirection [a_] | constructors.cpp:26:15:26:15 | *f [a_] |
|
||||
| constructors.cpp:43:9:43:9 | Argument 0 indirection [b_] | constructors.cpp:26:15:26:15 | *f [b_] |
|
||||
| constructors.cpp:46:9:46:9 | Argument 0 indirection [a_] | constructors.cpp:26:15:26:15 | *f [a_] |
|
||||
| constructors.cpp:46:9:46:9 | Argument 0 indirection [b_] | constructors.cpp:26:15:26:15 | *f [b_] |
|
||||
| simple.cpp:26:15:26:15 | *f [a_] | simple.cpp:28:10:28:10 | Argument -1 indirection [a_] |
|
||||
| simple.cpp:26:15:26:15 | *f [b_] | simple.cpp:28:10:28:10 | Argument -1 indirection [b_] |
|
||||
| simple.cpp:26:15:26:15 | *f [b_] | simple.cpp:29:10:29:10 | Argument -1 indirection [b_] |
|
||||
| simple.cpp:28:10:28:10 | Argument -1 indirection [a_] | simple.cpp:28:12:28:12 | call to a |
|
||||
| simple.cpp:28:10:28:10 | Argument -1 indirection [b_] | simple.cpp:28:10:28:10 | a output argument [b_] |
|
||||
| simple.cpp:28:10:28:10 | a output argument [b_] | simple.cpp:29:10:29:10 | Argument -1 indirection [b_] |
|
||||
| simple.cpp:29:10:29:10 | Argument -1 indirection [b_] | simple.cpp:29:12:29:12 | call to b |
|
||||
| simple.cpp:39:5:39:5 | setA output argument [a_] | simple.cpp:45:9:45:9 | Argument 0 indirection [a_] |
|
||||
| simple.cpp:39:12:39:21 | call to user_input | simple.cpp:39:5:39:5 | setA output argument [a_] |
|
||||
| simple.cpp:40:5:40:5 | setB output argument [b_] | simple.cpp:48:9:48:9 | Argument 0 indirection [b_] |
|
||||
| simple.cpp:40:12:40:21 | call to user_input | simple.cpp:40:5:40:5 | setB output argument [b_] |
|
||||
| simple.cpp:41:5:41:5 | setA output argument [a_] | simple.cpp:42:5:42:5 | Argument -1 indirection [a_] |
|
||||
| simple.cpp:41:5:41:5 | setA output argument [a_] | simple.cpp:51:9:51:9 | Argument 0 indirection [a_] |
|
||||
| simple.cpp:41:12:41:21 | call to user_input | simple.cpp:41:5:41:5 | setA output argument [a_] |
|
||||
| simple.cpp:42:5:42:5 | Argument -1 indirection [a_] | simple.cpp:42:5:42:5 | setB output argument [a_] |
|
||||
| simple.cpp:42:5:42:5 | setB output argument [a_] | simple.cpp:51:9:51:9 | Argument 0 indirection [a_] |
|
||||
| simple.cpp:42:5:42:5 | setB output argument [b_] | simple.cpp:51:9:51:9 | Argument 0 indirection [b_] |
|
||||
| simple.cpp:42:12:42:21 | call to user_input | simple.cpp:42:5:42:5 | setB output argument [b_] |
|
||||
| simple.cpp:45:9:45:9 | Argument 0 indirection [a_] | simple.cpp:26:15:26:15 | *f [a_] |
|
||||
| simple.cpp:48:9:48:9 | Argument 0 indirection [b_] | simple.cpp:26:15:26:15 | *f [b_] |
|
||||
| simple.cpp:51:9:51:9 | Argument 0 indirection [a_] | simple.cpp:26:15:26:15 | *f [a_] |
|
||||
| simple.cpp:51:9:51:9 | Argument 0 indirection [b_] | simple.cpp:26:15:26:15 | *f [b_] |
|
||||
| simple.cpp:65:5:65:22 | i [i] | simple.cpp:66:12:66:12 | Store [i] |
|
||||
| simple.cpp:65:11:65:20 | call to user_input | simple.cpp:65:5:65:22 | i [i] |
|
||||
| simple.cpp:66:12:66:12 | Store [i] | simple.cpp:67:13:67:13 | i |
|
||||
@@ -147,23 +316,65 @@ edges
|
||||
| struct_init.c:36:10:36:24 | Argument 0 indirection [a] | struct_init.c:14:24:14:25 | *ab [a] |
|
||||
| struct_init.c:36:10:36:24 | nestedAB [a] | struct_init.c:36:10:36:24 | Argument 0 indirection [a] |
|
||||
nodes
|
||||
| A.cpp:55:5:55:5 | set output argument [c] | semmle.label | set output argument [c] |
|
||||
| A.cpp:55:12:55:19 | (C *)... | semmle.label | (C *)... |
|
||||
| A.cpp:55:12:55:19 | new | semmle.label | new |
|
||||
| A.cpp:56:10:56:10 | Argument -1 indirection [c] | semmle.label | Argument -1 indirection [c] |
|
||||
| A.cpp:56:13:56:15 | call to get | semmle.label | call to get |
|
||||
| A.cpp:57:10:57:25 | Argument -1 indirection [c] | semmle.label | Argument -1 indirection [c] |
|
||||
| A.cpp:57:11:57:24 | B output argument [c] | semmle.label | B output argument [c] |
|
||||
| A.cpp:57:17:57:23 | new | semmle.label | new |
|
||||
| A.cpp:57:28:57:30 | call to get | semmle.label | call to get |
|
||||
| A.cpp:98:12:98:18 | new | semmle.label | new |
|
||||
| A.cpp:100:5:100:13 | Store | semmle.label | Store |
|
||||
| A.cpp:100:5:100:13 | a [a] | semmle.label | a [a] |
|
||||
| A.cpp:101:8:101:9 | Argument 0 indirection [a] | semmle.label | Argument 0 indirection [a] |
|
||||
| A.cpp:103:14:103:14 | *c [a] | semmle.label | *c [a] |
|
||||
| A.cpp:107:12:107:16 | (void *)... | semmle.label | (void *)... |
|
||||
| A.cpp:107:16:107:16 | a | semmle.label | a |
|
||||
| A.cpp:107:16:107:16 | a | semmle.label | a |
|
||||
| A.cpp:126:5:126:5 | Chi [c] | semmle.label | Chi [c] |
|
||||
| A.cpp:126:5:126:5 | set output argument [c] | semmle.label | set output argument [c] |
|
||||
| A.cpp:126:12:126:18 | new | semmle.label | new |
|
||||
| A.cpp:131:8:131:8 | Chi [c] | semmle.label | Chi [c] |
|
||||
| A.cpp:131:8:131:8 | f7 output argument [c] | semmle.label | f7 output argument [c] |
|
||||
| A.cpp:132:13:132:13 | c | semmle.label | c |
|
||||
| A.cpp:132:13:132:13 | c | semmle.label | c |
|
||||
| A.cpp:142:7:142:20 | Chi [c] | semmle.label | Chi [c] |
|
||||
| A.cpp:142:7:142:20 | Store | semmle.label | Store |
|
||||
| A.cpp:142:7:142:20 | c [c] | semmle.label | c [c] |
|
||||
| A.cpp:142:14:142:20 | new | semmle.label | new |
|
||||
| A.cpp:143:7:143:31 | Chi [b] | semmle.label | Chi [b] |
|
||||
| A.cpp:143:7:143:31 | Store | semmle.label | Store |
|
||||
| A.cpp:143:7:143:31 | b [b] | semmle.label | b [b] |
|
||||
| A.cpp:143:25:143:31 | new | semmle.label | new |
|
||||
| A.cpp:150:12:150:18 | new | semmle.label | new |
|
||||
| A.cpp:151:12:151:24 | Chi [b] | semmle.label | Chi [b] |
|
||||
| A.cpp:151:12:151:24 | D output argument [b] | semmle.label | D output argument [b] |
|
||||
| A.cpp:151:18:151:18 | Chi [c] | semmle.label | Chi [c] |
|
||||
| A.cpp:151:18:151:18 | D output argument [c] | semmle.label | D output argument [c] |
|
||||
| A.cpp:154:10:154:13 | (void *)... | semmle.label | (void *)... |
|
||||
| A.cpp:151:18:151:18 | b | semmle.label | b |
|
||||
| A.cpp:152:13:152:13 | b | semmle.label | b |
|
||||
| A.cpp:152:13:152:13 | b | semmle.label | b |
|
||||
| A.cpp:154:13:154:13 | c | semmle.label | c |
|
||||
| A.cpp:154:13:154:13 | c | semmle.label | c |
|
||||
| C.cpp:18:12:18:18 | C output argument [s1] | semmle.label | C output argument [s1] |
|
||||
| C.cpp:18:12:18:18 | C output argument [s3] | semmle.label | C output argument [s3] |
|
||||
| C.cpp:19:5:19:5 | Argument -1 indirection [s1] | semmle.label | Argument -1 indirection [s1] |
|
||||
| C.cpp:19:5:19:5 | Argument -1 indirection [s3] | semmle.label | Argument -1 indirection [s3] |
|
||||
| C.cpp:22:12:22:21 | Store | semmle.label | Store |
|
||||
| C.cpp:22:12:22:21 | new | semmle.label | new |
|
||||
| C.cpp:22:12:22:21 | s1 [s1] | semmle.label | s1 [s1] |
|
||||
| C.cpp:24:5:24:25 | Chi [s1] | semmle.label | Chi [s1] |
|
||||
| C.cpp:24:5:24:25 | Chi [s3] | semmle.label | Chi [s3] |
|
||||
| C.cpp:24:5:24:25 | Store | semmle.label | Store |
|
||||
| C.cpp:24:5:24:25 | s3 [s3] | semmle.label | s3 [s3] |
|
||||
| C.cpp:24:16:24:25 | new | semmle.label | new |
|
||||
| C.cpp:27:8:27:11 | *#this [s1] | semmle.label | *#this [s1] |
|
||||
| C.cpp:27:8:27:11 | *#this [s3] | semmle.label | *#this [s3] |
|
||||
| C.cpp:29:10:29:11 | s1 | semmle.label | s1 |
|
||||
| C.cpp:29:10:29:11 | s1 | semmle.label | s1 |
|
||||
| C.cpp:31:10:31:11 | s3 | semmle.label | s3 |
|
||||
| C.cpp:31:10:31:11 | s3 | semmle.label | s3 |
|
||||
| aliasing.cpp:9:3:9:22 | Chi [m1] | semmle.label | Chi [m1] |
|
||||
| aliasing.cpp:9:3:9:22 | Store | semmle.label | Store |
|
||||
| aliasing.cpp:9:3:9:22 | m1 [m1] | semmle.label | m1 [m1] |
|
||||
@@ -196,6 +407,18 @@ nodes
|
||||
| aliasing.cpp:87:12:87:13 | m1 | semmle.label | m1 |
|
||||
| aliasing.cpp:92:12:92:21 | call to user_input | semmle.label | call to user_input |
|
||||
| aliasing.cpp:93:12:93:13 | m1 | semmle.label | m1 |
|
||||
| by_reference.cpp:50:3:50:3 | setDirectly output argument [a] | semmle.label | setDirectly output argument [a] |
|
||||
| by_reference.cpp:50:17:50:26 | call to user_input | semmle.label | call to user_input |
|
||||
| by_reference.cpp:51:8:51:8 | Argument -1 indirection [a] | semmle.label | Argument -1 indirection [a] |
|
||||
| by_reference.cpp:51:10:51:20 | call to getDirectly | semmle.label | call to getDirectly |
|
||||
| by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] | semmle.label | setIndirectly output argument [a] |
|
||||
| by_reference.cpp:56:19:56:28 | call to user_input | semmle.label | call to user_input |
|
||||
| by_reference.cpp:57:8:57:8 | Argument -1 indirection [a] | semmle.label | Argument -1 indirection [a] |
|
||||
| by_reference.cpp:57:10:57:22 | call to getIndirectly | semmle.label | call to getIndirectly |
|
||||
| by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] | semmle.label | setThroughNonMember output argument [a] |
|
||||
| by_reference.cpp:62:25:62:34 | call to user_input | semmle.label | call to user_input |
|
||||
| by_reference.cpp:63:8:63:8 | Argument -1 indirection [a] | semmle.label | Argument -1 indirection [a] |
|
||||
| by_reference.cpp:63:10:63:28 | call to getThroughNonMember | semmle.label | call to getThroughNonMember |
|
||||
| by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] | semmle.label | nonMemberSetA output argument [a] |
|
||||
| by_reference.cpp:68:21:68:30 | call to user_input | semmle.label | call to user_input |
|
||||
| by_reference.cpp:69:8:69:20 | call to nonMemberGetA | semmle.label | call to nonMemberGetA |
|
||||
@@ -232,6 +455,116 @@ nodes
|
||||
| by_reference.cpp:134:29:134:29 | a | semmle.label | a |
|
||||
| by_reference.cpp:134:29:134:29 | a | semmle.label | a |
|
||||
| by_reference.cpp:134:29:134:29 | inner_nested.a [a] | semmle.label | inner_nested.a [a] |
|
||||
| complex.cpp:40:17:40:17 | *b [a_] | semmle.label | *b [a_] |
|
||||
| complex.cpp:40:17:40:17 | *b [b_] | semmle.label | *b [b_] |
|
||||
| complex.cpp:40:17:40:17 | *b [inner, b_] | semmle.label | *b [inner, b_] |
|
||||
| complex.cpp:40:17:40:17 | *b [inner, f, ... (3)] | semmle.label | *b [inner, f, ... (3)] |
|
||||
| complex.cpp:40:17:40:17 | *b [inner, f, ... (5)] | semmle.label | *b [inner, f, ... (5)] |
|
||||
| complex.cpp:51:16:51:16 | Argument -1 indirection [a_] | semmle.label | Argument -1 indirection [a_] |
|
||||
| complex.cpp:51:16:51:16 | Argument -1 indirection [b_] | semmle.label | Argument -1 indirection [b_] |
|
||||
| complex.cpp:51:16:51:16 | Argument -1 indirection [inner, f, ... (3)] | semmle.label | Argument -1 indirection [inner, f, ... (3)] |
|
||||
| complex.cpp:51:16:51:16 | Chi [inner, f, ... (3)] | semmle.label | Chi [inner, f, ... (3)] |
|
||||
| complex.cpp:51:16:51:16 | a output argument [a_] | semmle.label | a output argument [a_] |
|
||||
| complex.cpp:51:16:51:16 | a output argument [b_] | semmle.label | a output argument [b_] |
|
||||
| complex.cpp:51:16:51:16 | a output argument [inner, f, ... (3)] | semmle.label | a output argument [inner, f, ... (3)] |
|
||||
| complex.cpp:51:16:51:16 | f [a_] | semmle.label | f [a_] |
|
||||
| complex.cpp:51:16:51:16 | f [b_] | semmle.label | f [b_] |
|
||||
| complex.cpp:51:16:51:16 | f [f, a_] | semmle.label | f [f, a_] |
|
||||
| complex.cpp:51:16:51:16 | f [f, b_] | semmle.label | f [f, b_] |
|
||||
| complex.cpp:51:16:51:16 | f [inner, f, ... (3)] | semmle.label | f [inner, f, ... (3)] |
|
||||
| complex.cpp:51:16:51:16 | inner.f [b_] | semmle.label | inner.f [b_] |
|
||||
| complex.cpp:51:16:51:16 | inner.f [f, a_] | semmle.label | inner.f [f, a_] |
|
||||
| complex.cpp:51:16:51:16 | inner.f [f, b_] | semmle.label | inner.f [f, b_] |
|
||||
| complex.cpp:51:16:51:16 | inner.f [f, inner, ... (4)] | semmle.label | inner.f [f, inner, ... (4)] |
|
||||
| complex.cpp:51:16:51:16 | inner.f [inner, f, ... (3)] | semmle.label | inner.f [inner, f, ... (3)] |
|
||||
| complex.cpp:51:18:51:18 | call to a | semmle.label | call to a |
|
||||
| complex.cpp:52:16:52:16 | Argument -1 indirection [b_] | semmle.label | Argument -1 indirection [b_] |
|
||||
| complex.cpp:52:16:52:16 | f [b_] | semmle.label | f [b_] |
|
||||
| complex.cpp:52:16:52:16 | inner.f [f, b_] | semmle.label | inner.f [f, b_] |
|
||||
| complex.cpp:52:18:52:18 | call to b | semmle.label | call to b |
|
||||
| complex.cpp:62:12:62:12 | f [f, a_] | semmle.label | f [f, a_] |
|
||||
| complex.cpp:62:12:62:12 | inner.f [inner, f, ... (3)] | semmle.label | inner.f [inner, f, ... (3)] |
|
||||
| complex.cpp:62:12:62:12 | setA output argument [a_] | semmle.label | setA output argument [a_] |
|
||||
| complex.cpp:62:19:62:28 | call to user_input | semmle.label | call to user_input |
|
||||
| complex.cpp:63:12:63:12 | f [f, b_] | semmle.label | f [f, b_] |
|
||||
| complex.cpp:63:12:63:12 | inner.f [inner, f, ... (3)] | semmle.label | inner.f [inner, f, ... (3)] |
|
||||
| complex.cpp:63:12:63:12 | setB output argument [b_] | semmle.label | setB output argument [b_] |
|
||||
| complex.cpp:63:19:63:28 | call to user_input | semmle.label | call to user_input |
|
||||
| complex.cpp:64:12:64:12 | Chi [inner, f, ... (3)] | semmle.label | Chi [inner, f, ... (3)] |
|
||||
| complex.cpp:64:12:64:12 | f [f, a_] | semmle.label | f [f, a_] |
|
||||
| complex.cpp:64:12:64:12 | inner.f [inner, f, ... (3)] | semmle.label | inner.f [inner, f, ... (3)] |
|
||||
| complex.cpp:64:12:64:12 | setA output argument [a_] | semmle.label | setA output argument [a_] |
|
||||
| complex.cpp:64:19:64:28 | call to user_input | semmle.label | call to user_input |
|
||||
| complex.cpp:65:12:65:12 | Argument -1 indirection [a_] | semmle.label | Argument -1 indirection [a_] |
|
||||
| complex.cpp:65:12:65:12 | Argument -1 indirection [b_] | semmle.label | Argument -1 indirection [b_] |
|
||||
| complex.cpp:65:12:65:12 | Argument -1 indirection [inner, f, ... (3)] | semmle.label | Argument -1 indirection [inner, f, ... (3)] |
|
||||
| complex.cpp:65:12:65:12 | f [a_] | semmle.label | f [a_] |
|
||||
| complex.cpp:65:12:65:12 | f [b_] | semmle.label | f [b_] |
|
||||
| complex.cpp:65:12:65:12 | f [b_] | semmle.label | f [b_] |
|
||||
| complex.cpp:65:12:65:12 | f [f, a_] | semmle.label | f [f, a_] |
|
||||
| complex.cpp:65:12:65:12 | f [f, b_] | semmle.label | f [f, b_] |
|
||||
| complex.cpp:65:12:65:12 | f [f, inner, ... (4)] | semmle.label | f [f, inner, ... (4)] |
|
||||
| complex.cpp:65:12:65:12 | inner.f [f, a_] | semmle.label | inner.f [f, a_] |
|
||||
| complex.cpp:65:12:65:12 | inner.f [f, b_] | semmle.label | inner.f [f, b_] |
|
||||
| complex.cpp:65:12:65:12 | inner.f [f, b_] | semmle.label | inner.f [f, b_] |
|
||||
| complex.cpp:65:12:65:12 | inner.f [inner, b_] | semmle.label | inner.f [inner, b_] |
|
||||
| complex.cpp:65:12:65:12 | inner.f [inner, f, ... (3)] | semmle.label | inner.f [inner, f, ... (3)] |
|
||||
| complex.cpp:65:12:65:12 | inner.f [inner, f, ... (5)] | semmle.label | inner.f [inner, f, ... (5)] |
|
||||
| complex.cpp:65:12:65:12 | setB output argument [a_] | semmle.label | setB output argument [a_] |
|
||||
| complex.cpp:65:12:65:12 | setB output argument [b_] | semmle.label | setB output argument [b_] |
|
||||
| complex.cpp:65:12:65:12 | setB output argument [inner, f, ... (3)] | semmle.label | setB output argument [inner, f, ... (3)] |
|
||||
| complex.cpp:65:19:65:28 | call to user_input | semmle.label | call to user_input |
|
||||
| complex.cpp:68:7:68:8 | Argument 0 indirection [a_] | semmle.label | Argument 0 indirection [a_] |
|
||||
| complex.cpp:68:7:68:8 | Argument 0 indirection [inner, f, ... (3)] | semmle.label | Argument 0 indirection [inner, f, ... (3)] |
|
||||
| complex.cpp:71:7:71:8 | Argument 0 indirection [b_] | semmle.label | Argument 0 indirection [b_] |
|
||||
| complex.cpp:71:7:71:8 | Argument 0 indirection [inner, f, ... (3)] | semmle.label | Argument 0 indirection [inner, f, ... (3)] |
|
||||
| complex.cpp:74:7:74:8 | Argument 0 indirection [a_] | semmle.label | Argument 0 indirection [a_] |
|
||||
| complex.cpp:74:7:74:8 | Argument 0 indirection [b_] | semmle.label | Argument 0 indirection [b_] |
|
||||
| complex.cpp:74:7:74:8 | Argument 0 indirection [inner, b_] | semmle.label | Argument 0 indirection [inner, b_] |
|
||||
| complex.cpp:74:7:74:8 | Argument 0 indirection [inner, f, ... (3)] | semmle.label | Argument 0 indirection [inner, f, ... (3)] |
|
||||
| complex.cpp:74:7:74:8 | Argument 0 indirection [inner, f, ... (5)] | semmle.label | Argument 0 indirection [inner, f, ... (5)] |
|
||||
| constructors.cpp:26:15:26:15 | *f [a_] | semmle.label | *f [a_] |
|
||||
| constructors.cpp:26:15:26:15 | *f [b_] | semmle.label | *f [b_] |
|
||||
| constructors.cpp:28:10:28:10 | Argument -1 indirection [a_] | semmle.label | Argument -1 indirection [a_] |
|
||||
| constructors.cpp:28:10:28:10 | Argument -1 indirection [b_] | semmle.label | Argument -1 indirection [b_] |
|
||||
| constructors.cpp:28:10:28:10 | a output argument [b_] | semmle.label | a output argument [b_] |
|
||||
| constructors.cpp:28:12:28:12 | call to a | semmle.label | call to a |
|
||||
| constructors.cpp:29:10:29:10 | Argument -1 indirection [b_] | semmle.label | Argument -1 indirection [b_] |
|
||||
| constructors.cpp:29:12:29:12 | call to b | semmle.label | call to b |
|
||||
| constructors.cpp:34:11:34:20 | call to user_input | semmle.label | call to user_input |
|
||||
| constructors.cpp:34:11:34:26 | Foo output argument [a_] | semmle.label | Foo output argument [a_] |
|
||||
| constructors.cpp:35:11:35:26 | Foo output argument [b_] | semmle.label | Foo output argument [b_] |
|
||||
| constructors.cpp:35:14:35:23 | call to user_input | semmle.label | call to user_input |
|
||||
| constructors.cpp:36:11:36:20 | call to user_input | semmle.label | call to user_input |
|
||||
| constructors.cpp:36:11:36:37 | Foo output argument [a_] | semmle.label | Foo output argument [a_] |
|
||||
| constructors.cpp:36:11:36:37 | Foo output argument [b_] | semmle.label | Foo output argument [b_] |
|
||||
| constructors.cpp:36:25:36:34 | call to user_input | semmle.label | call to user_input |
|
||||
| constructors.cpp:40:9:40:9 | Argument 0 indirection [a_] | semmle.label | Argument 0 indirection [a_] |
|
||||
| constructors.cpp:43:9:43:9 | Argument 0 indirection [b_] | semmle.label | Argument 0 indirection [b_] |
|
||||
| constructors.cpp:46:9:46:9 | Argument 0 indirection [a_] | semmle.label | Argument 0 indirection [a_] |
|
||||
| constructors.cpp:46:9:46:9 | Argument 0 indirection [b_] | semmle.label | Argument 0 indirection [b_] |
|
||||
| simple.cpp:26:15:26:15 | *f [a_] | semmle.label | *f [a_] |
|
||||
| simple.cpp:26:15:26:15 | *f [b_] | semmle.label | *f [b_] |
|
||||
| simple.cpp:28:10:28:10 | Argument -1 indirection [a_] | semmle.label | Argument -1 indirection [a_] |
|
||||
| simple.cpp:28:10:28:10 | Argument -1 indirection [b_] | semmle.label | Argument -1 indirection [b_] |
|
||||
| simple.cpp:28:10:28:10 | a output argument [b_] | semmle.label | a output argument [b_] |
|
||||
| simple.cpp:28:12:28:12 | call to a | semmle.label | call to a |
|
||||
| simple.cpp:29:10:29:10 | Argument -1 indirection [b_] | semmle.label | Argument -1 indirection [b_] |
|
||||
| simple.cpp:29:12:29:12 | call to b | semmle.label | call to b |
|
||||
| simple.cpp:39:5:39:5 | setA output argument [a_] | semmle.label | setA output argument [a_] |
|
||||
| simple.cpp:39:12:39:21 | call to user_input | semmle.label | call to user_input |
|
||||
| simple.cpp:40:5:40:5 | setB output argument [b_] | semmle.label | setB output argument [b_] |
|
||||
| simple.cpp:40:12:40:21 | call to user_input | semmle.label | call to user_input |
|
||||
| simple.cpp:41:5:41:5 | setA output argument [a_] | semmle.label | setA output argument [a_] |
|
||||
| simple.cpp:41:12:41:21 | call to user_input | semmle.label | call to user_input |
|
||||
| simple.cpp:42:5:42:5 | Argument -1 indirection [a_] | semmle.label | Argument -1 indirection [a_] |
|
||||
| simple.cpp:42:5:42:5 | setB output argument [a_] | semmle.label | setB output argument [a_] |
|
||||
| simple.cpp:42:5:42:5 | setB output argument [b_] | semmle.label | setB output argument [b_] |
|
||||
| simple.cpp:42:12:42:21 | call to user_input | semmle.label | call to user_input |
|
||||
| simple.cpp:45:9:45:9 | Argument 0 indirection [a_] | semmle.label | Argument 0 indirection [a_] |
|
||||
| simple.cpp:48:9:48:9 | Argument 0 indirection [b_] | semmle.label | Argument 0 indirection [b_] |
|
||||
| simple.cpp:51:9:51:9 | Argument 0 indirection [a_] | semmle.label | Argument 0 indirection [a_] |
|
||||
| simple.cpp:51:9:51:9 | Argument 0 indirection [b_] | semmle.label | Argument 0 indirection [b_] |
|
||||
| simple.cpp:65:5:65:22 | i [i] | semmle.label | i [i] |
|
||||
| simple.cpp:65:11:65:20 | call to user_input | semmle.label | call to user_input |
|
||||
| simple.cpp:66:12:66:12 | Store [i] | semmle.label | Store [i] |
|
||||
@@ -309,10 +642,16 @@ nodes
|
||||
| struct_init.c:36:10:36:24 | Argument 0 indirection [a] | semmle.label | Argument 0 indirection [a] |
|
||||
| struct_init.c:36:10:36:24 | nestedAB [a] | semmle.label | nestedAB [a] |
|
||||
#select
|
||||
| A.cpp:107:12:107:16 | (void *)... | A.cpp:98:12:98:18 | new | A.cpp:107:12:107:16 | (void *)... | (void *)... flows from $@ | A.cpp:98:12:98:18 | new | new |
|
||||
| A.cpp:56:13:56:15 | call to get | A.cpp:55:12:55:19 | (C *)... | A.cpp:56:13:56:15 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | (C *)... | (C *)... |
|
||||
| A.cpp:56:13:56:15 | call to get | A.cpp:55:12:55:19 | new | A.cpp:56:13:56:15 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | new | new |
|
||||
| A.cpp:57:28:57:30 | call to get | A.cpp:57:17:57:23 | new | A.cpp:57:28:57:30 | call to get | call to get flows from $@ | A.cpp:57:17:57:23 | new | new |
|
||||
| A.cpp:107:16:107:16 | a | A.cpp:98:12:98:18 | new | A.cpp:107:16:107:16 | a | a flows from $@ | A.cpp:98:12:98:18 | new | new |
|
||||
| A.cpp:154:10:154:13 | (void *)... | A.cpp:142:14:142:20 | new | A.cpp:154:10:154:13 | (void *)... | (void *)... flows from $@ | A.cpp:142:14:142:20 | new | new |
|
||||
| A.cpp:132:13:132:13 | c | A.cpp:126:12:126:18 | new | A.cpp:132:13:132:13 | c | c flows from $@ | A.cpp:126:12:126:18 | new | new |
|
||||
| A.cpp:152:13:152:13 | b | A.cpp:143:25:143:31 | new | A.cpp:152:13:152:13 | b | b flows from $@ | A.cpp:143:25:143:31 | new | new |
|
||||
| A.cpp:152:13:152:13 | b | A.cpp:150:12:150:18 | new | A.cpp:152:13:152:13 | b | b flows from $@ | A.cpp:150:12:150:18 | new | new |
|
||||
| A.cpp:154:13:154:13 | c | A.cpp:142:14:142:20 | new | A.cpp:154:13:154:13 | c | c flows from $@ | A.cpp:142:14:142:20 | new | new |
|
||||
| C.cpp:29:10:29:11 | s1 | C.cpp:22:12:22:21 | new | C.cpp:29:10:29:11 | s1 | s1 flows from $@ | C.cpp:22:12:22:21 | new | new |
|
||||
| C.cpp:31:10:31:11 | s3 | C.cpp:24:16:24:25 | new | C.cpp:31:10:31:11 | s3 | s3 flows from $@ | C.cpp:24:16:24:25 | new | new |
|
||||
| aliasing.cpp:29:11:29:12 | m1 | aliasing.cpp:9:11:9:20 | call to user_input | aliasing.cpp:29:11:29:12 | m1 | m1 flows from $@ | aliasing.cpp:9:11:9:20 | call to user_input | call to user_input |
|
||||
| aliasing.cpp:30:11:30:12 | m1 | aliasing.cpp:13:10:13:19 | call to user_input | aliasing.cpp:30:11:30:12 | m1 | m1 flows from $@ | aliasing.cpp:13:10:13:19 | call to user_input | call to user_input |
|
||||
| aliasing.cpp:38:11:38:12 | m1 | aliasing.cpp:37:13:37:22 | call to user_input | aliasing.cpp:38:11:38:12 | m1 | m1 flows from $@ | aliasing.cpp:37:13:37:22 | call to user_input | call to user_input |
|
||||
@@ -321,11 +660,30 @@ nodes
|
||||
| aliasing.cpp:80:12:80:13 | m1 | aliasing.cpp:79:11:79:20 | call to user_input | aliasing.cpp:80:12:80:13 | m1 | m1 flows from $@ | aliasing.cpp:79:11:79:20 | call to user_input | call to user_input |
|
||||
| aliasing.cpp:87:12:87:13 | m1 | aliasing.cpp:86:10:86:19 | call to user_input | aliasing.cpp:87:12:87:13 | m1 | m1 flows from $@ | aliasing.cpp:86:10:86:19 | call to user_input | call to user_input |
|
||||
| aliasing.cpp:93:12:93:13 | m1 | aliasing.cpp:92:12:92:21 | call to user_input | aliasing.cpp:93:12:93:13 | m1 | m1 flows from $@ | aliasing.cpp:92:12:92:21 | call to user_input | call to user_input |
|
||||
| by_reference.cpp:51:10:51:20 | call to getDirectly | by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:51:10:51:20 | call to getDirectly | call to getDirectly flows from $@ | by_reference.cpp:50:17:50:26 | call to user_input | call to user_input |
|
||||
| by_reference.cpp:57:10:57:22 | call to getIndirectly | by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:57:10:57:22 | call to getIndirectly | call to getIndirectly flows from $@ | by_reference.cpp:56:19:56:28 | call to user_input | call to user_input |
|
||||
| by_reference.cpp:63:10:63:28 | call to getThroughNonMember | by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:63:10:63:28 | call to getThroughNonMember | call to getThroughNonMember flows from $@ | by_reference.cpp:62:25:62:34 | call to user_input | call to user_input |
|
||||
| by_reference.cpp:69:8:69:20 | call to nonMemberGetA | by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:69:8:69:20 | call to nonMemberGetA | call to nonMemberGetA flows from $@ | by_reference.cpp:68:21:68:30 | call to user_input | call to user_input |
|
||||
| by_reference.cpp:110:27:110:27 | a | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:110:27:110:27 | a | a flows from $@ | by_reference.cpp:84:14:84:23 | call to user_input | call to user_input |
|
||||
| by_reference.cpp:114:29:114:29 | a | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:114:29:114:29 | a | a flows from $@ | by_reference.cpp:84:14:84:23 | call to user_input | call to user_input |
|
||||
| by_reference.cpp:130:27:130:27 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:130:27:130:27 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input |
|
||||
| by_reference.cpp:134:29:134:29 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:134:29:134:29 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input |
|
||||
| complex.cpp:51:18:51:18 | call to a | complex.cpp:62:19:62:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:62:19:62:28 | call to user_input | call to user_input |
|
||||
| complex.cpp:51:18:51:18 | call to a | complex.cpp:63:19:63:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:63:19:63:28 | call to user_input | call to user_input |
|
||||
| complex.cpp:51:18:51:18 | call to a | complex.cpp:64:19:64:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:64:19:64:28 | call to user_input | call to user_input |
|
||||
| complex.cpp:51:18:51:18 | call to a | complex.cpp:65:19:65:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:65:19:65:28 | call to user_input | call to user_input |
|
||||
| complex.cpp:52:18:52:18 | call to b | complex.cpp:62:19:62:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:62:19:62:28 | call to user_input | call to user_input |
|
||||
| complex.cpp:52:18:52:18 | call to b | complex.cpp:63:19:63:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:63:19:63:28 | call to user_input | call to user_input |
|
||||
| complex.cpp:52:18:52:18 | call to b | complex.cpp:64:19:64:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:64:19:64:28 | call to user_input | call to user_input |
|
||||
| complex.cpp:52:18:52:18 | call to b | complex.cpp:65:19:65:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:65:19:65:28 | call to user_input | call to user_input |
|
||||
| constructors.cpp:28:12:28:12 | call to a | constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:34:11:34:20 | call to user_input | call to user_input |
|
||||
| constructors.cpp:28:12:28:12 | call to a | constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:36:11:36:20 | call to user_input | call to user_input |
|
||||
| constructors.cpp:29:12:29:12 | call to b | constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:29:12:29:12 | call to b | call to b flows from $@ | constructors.cpp:35:14:35:23 | call to user_input | call to user_input |
|
||||
| constructors.cpp:29:12:29:12 | call to b | constructors.cpp:36:25:36:34 | call to user_input | constructors.cpp:29:12:29:12 | call to b | call to b flows from $@ | constructors.cpp:36:25:36:34 | call to user_input | call to user_input |
|
||||
| simple.cpp:28:12:28:12 | call to a | simple.cpp:39:12:39:21 | call to user_input | simple.cpp:28:12:28:12 | call to a | call to a flows from $@ | simple.cpp:39:12:39:21 | call to user_input | call to user_input |
|
||||
| simple.cpp:28:12:28:12 | call to a | simple.cpp:41:12:41:21 | call to user_input | simple.cpp:28:12:28:12 | call to a | call to a flows from $@ | simple.cpp:41:12:41:21 | call to user_input | call to user_input |
|
||||
| simple.cpp:29:12:29:12 | call to b | simple.cpp:40:12:40:21 | call to user_input | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:40:12:40:21 | call to user_input | call to user_input |
|
||||
| simple.cpp:29:12:29:12 | call to b | simple.cpp:42:12:42:21 | call to user_input | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:42:12:42:21 | call to user_input | call to user_input |
|
||||
| simple.cpp:67:13:67:13 | i | simple.cpp:65:11:65:20 | call to user_input | simple.cpp:67:13:67:13 | i | i flows from $@ | simple.cpp:65:11:65:20 | call to user_input | call to user_input |
|
||||
| simple.cpp:84:14:84:20 | call to getf2f1 | simple.cpp:83:17:83:26 | call to user_input | simple.cpp:84:14:84:20 | call to getf2f1 | call to getf2f1 flows from $@ | simple.cpp:83:17:83:26 | call to user_input | call to user_input |
|
||||
| simple.cpp:111:18:111:18 | y | simple.cpp:136:31:136:40 | call to user_input | simple.cpp:111:18:111:18 | y | y flows from $@ | simple.cpp:136:31:136:40 | call to user_input | call to user_input |
|
||||
|
||||
@@ -5,43 +5,18 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow::DataFlow as IR
|
||||
import semmle.code.cpp.dataflow.DataFlow::DataFlow as AST
|
||||
import Nodes
|
||||
|
||||
newtype TNode =
|
||||
TASTNode(AST::Node n) or
|
||||
TIRNode(IR::Node n)
|
||||
|
||||
class Node extends TNode {
|
||||
string toString() { none() }
|
||||
|
||||
IR::Node asIR() { none() }
|
||||
|
||||
AST::Node asAST() { none() }
|
||||
|
||||
Location getLocation() { none() }
|
||||
}
|
||||
|
||||
class ASTNode extends Node, TASTNode {
|
||||
AST::Node n;
|
||||
|
||||
ASTNode() { this = TASTNode(n) }
|
||||
class ASTPartialDefNode extends ASTNode {
|
||||
ASTPartialDefNode() { exists(n.asPartialDefinition()) }
|
||||
|
||||
override string toString() { result = n.asPartialDefinition().toString() }
|
||||
|
||||
override AST::Node asAST() { result = n }
|
||||
|
||||
override Location getLocation() { result = n.getLocation() }
|
||||
}
|
||||
|
||||
class IRNode extends Node, TIRNode {
|
||||
IR::Node n;
|
||||
|
||||
IRNode() { this = TIRNode(n) }
|
||||
class IRPartialDefNode extends IRNode {
|
||||
IRPartialDefNode() { exists(n.asPartialDefinition()) }
|
||||
|
||||
override string toString() { result = n.asPartialDefinition().toString() }
|
||||
|
||||
override IR::Node asIR() { result = n }
|
||||
|
||||
override Location getLocation() { result = n.getLocation() }
|
||||
}
|
||||
|
||||
from Node node, AST::Node astNode, IR::Node irNode, string msg
|
||||
|
||||
@@ -25,8 +25,8 @@ public:
|
||||
|
||||
void bar(Foo &f)
|
||||
{
|
||||
sink(f.a()); //$ast=39:12 $ast=41:12 $f-:ir
|
||||
sink(f.b()); //$ast=40:12 $ast=42:12 $f-:ir
|
||||
sink(f.a()); //$ast=39:12 $ast=41:12 $ir=39:12 $ir=41:12
|
||||
sink(f.b()); //$ast=40:12 $ast=42:12 $ir=40:12 $ir=42:12
|
||||
}
|
||||
|
||||
void foo()
|
||||
|
||||
@@ -188,6 +188,228 @@
|
||||
| stl.cpp:131:15:131:24 | call to user_input | stl.cpp:131:15:131:27 | call to basic_string | TAINT |
|
||||
| stl.cpp:131:15:131:27 | call to basic_string | stl.cpp:132:7:132:11 | path3 | |
|
||||
| stl.cpp:132:7:132:11 | path3 | stl.cpp:132:13:132:17 | call to c_str | TAINT |
|
||||
| stl.cpp:137:19:137:24 | call to source | stl.cpp:140:17:140:18 | cs | |
|
||||
| stl.cpp:137:19:137:24 | call to source | stl.cpp:142:7:142:8 | cs | |
|
||||
| stl.cpp:140:17:140:18 | cs | stl.cpp:140:17:140:19 | call to basic_string | TAINT |
|
||||
| stl.cpp:140:17:140:19 | call to basic_string | stl.cpp:143:7:143:8 | ss | |
|
||||
| stl.cpp:148:19:148:24 | call to source | stl.cpp:151:17:151:18 | cs | |
|
||||
| stl.cpp:151:17:151:18 | cs | stl.cpp:151:17:151:19 | call to basic_string | TAINT |
|
||||
| stl.cpp:151:17:151:19 | call to basic_string | stl.cpp:154:7:154:8 | ss | |
|
||||
| stl.cpp:151:17:151:19 | call to basic_string | stl.cpp:157:7:157:8 | ss | |
|
||||
| stl.cpp:154:7:154:8 | ss | stl.cpp:154:10:154:14 | call to c_str | TAINT |
|
||||
| stl.cpp:154:10:154:14 | call to c_str | stl.cpp:154:2:154:16 | ... = ... | |
|
||||
| stl.cpp:154:10:154:14 | call to c_str | stl.cpp:156:7:156:8 | cs | |
|
||||
| swap1.cpp:14:17:14:17 | t | swap1.cpp:14:17:14:17 | t | |
|
||||
| swap1.cpp:14:17:14:17 | t | swap1.cpp:14:17:14:17 | t | |
|
||||
| swap1.cpp:14:17:14:17 | t | swap1.cpp:14:56:14:56 | t | |
|
||||
| swap1.cpp:14:17:14:17 | t | swap1.cpp:14:56:14:56 | t | |
|
||||
| swap1.cpp:24:9:24:13 | this | swap1.cpp:24:31:24:34 | this | |
|
||||
| swap1.cpp:24:23:24:26 | that | swap1.cpp:24:23:24:26 | that | |
|
||||
| swap1.cpp:24:23:24:26 | that | swap1.cpp:24:36:24:39 | that | |
|
||||
| swap1.cpp:24:36:24:39 | ref arg that | swap1.cpp:24:23:24:26 | that | |
|
||||
| swap1.cpp:25:9:25:13 | this | swap1.cpp:25:36:25:52 | constructor init of field data1 [pre-this] | |
|
||||
| swap1.cpp:25:28:25:31 | that | swap1.cpp:25:42:25:45 | that | |
|
||||
| swap1.cpp:25:47:25:51 | data1 | swap1.cpp:25:36:25:52 | constructor init of field data1 | TAINT |
|
||||
| swap1.cpp:25:47:25:51 | data1 | swap1.cpp:25:47:25:51 | data1 | |
|
||||
| swap1.cpp:27:16:27:24 | this | swap1.cpp:30:13:30:16 | this | |
|
||||
| swap1.cpp:27:39:27:42 | that | swap1.cpp:29:24:29:27 | that | |
|
||||
| swap1.cpp:29:23:29:27 | call to Class | swap1.cpp:30:18:30:20 | tmp | |
|
||||
| swap1.cpp:30:13:30:16 | ref arg this | swap1.cpp:31:21:31:24 | this | |
|
||||
| swap1.cpp:30:13:30:16 | this | swap1.cpp:31:21:31:24 | this | |
|
||||
| swap1.cpp:31:21:31:24 | this | swap1.cpp:31:20:31:24 | * ... | TAINT |
|
||||
| swap1.cpp:34:16:34:24 | this | swap1.cpp:36:13:36:16 | this | |
|
||||
| swap1.cpp:34:34:34:37 | that | swap1.cpp:34:34:34:37 | that | |
|
||||
| swap1.cpp:34:34:34:37 | that | swap1.cpp:36:18:36:21 | that | |
|
||||
| swap1.cpp:36:13:36:16 | ref arg this | swap1.cpp:37:21:37:24 | this | |
|
||||
| swap1.cpp:36:13:36:16 | this | swap1.cpp:37:21:37:24 | this | |
|
||||
| swap1.cpp:36:18:36:21 | ref arg that | swap1.cpp:34:34:34:37 | that | |
|
||||
| swap1.cpp:37:21:37:24 | this | swap1.cpp:37:20:37:24 | * ... | TAINT |
|
||||
| swap1.cpp:40:14:40:17 | this | swap1.cpp:43:18:43:22 | this | |
|
||||
| swap1.cpp:40:26:40:29 | that | swap1.cpp:40:26:40:29 | that | |
|
||||
| swap1.cpp:40:26:40:29 | that | swap1.cpp:43:25:43:28 | that | |
|
||||
| swap1.cpp:43:18:43:22 | data1 | swap1.cpp:43:30:43:34 | ref arg data1 | |
|
||||
| swap1.cpp:43:25:43:28 | that | swap1.cpp:43:18:43:22 | ref arg data1 | |
|
||||
| swap1.cpp:43:25:43:28 | that [post update] | swap1.cpp:40:26:40:29 | that | |
|
||||
| swap1.cpp:43:30:43:34 | data1 | swap1.cpp:43:18:43:22 | ref arg data1 | |
|
||||
| swap1.cpp:48:22:48:22 | x | swap1.cpp:48:22:48:22 | x | |
|
||||
| swap1.cpp:48:22:48:22 | x | swap1.cpp:50:9:50:9 | x | |
|
||||
| swap1.cpp:48:22:48:22 | x | swap2.cpp:48:22:48:22 | x | |
|
||||
| swap1.cpp:48:22:48:22 | x | swap2.cpp:50:9:50:9 | x | |
|
||||
| swap1.cpp:48:32:48:32 | y | swap1.cpp:48:32:48:32 | y | |
|
||||
| swap1.cpp:48:32:48:32 | y | swap1.cpp:50:16:50:16 | y | |
|
||||
| swap1.cpp:48:32:48:32 | y | swap2.cpp:48:32:48:32 | y | |
|
||||
| swap1.cpp:48:32:48:32 | y | swap2.cpp:50:16:50:16 | y | |
|
||||
| swap1.cpp:50:9:50:9 | ref arg x | swap1.cpp:48:22:48:22 | x | |
|
||||
| swap1.cpp:50:9:50:9 | ref arg x | swap2.cpp:48:22:48:22 | x | |
|
||||
| swap1.cpp:50:16:50:16 | ref arg y | swap1.cpp:48:32:48:32 | y | |
|
||||
| swap1.cpp:50:16:50:16 | ref arg y | swap2.cpp:48:32:48:32 | y | |
|
||||
| swap1.cpp:56:23:56:23 | x | swap1.cpp:58:5:58:5 | x | |
|
||||
| swap1.cpp:56:23:56:23 | x | swap1.cpp:60:10:60:10 | x | |
|
||||
| swap1.cpp:56:23:56:23 | x | swap1.cpp:63:9:63:9 | x | |
|
||||
| swap1.cpp:56:23:56:23 | x | swap1.cpp:66:10:66:10 | x | |
|
||||
| swap1.cpp:57:23:57:23 | y | swap1.cpp:61:10:61:10 | y | |
|
||||
| swap1.cpp:57:23:57:23 | y | swap1.cpp:63:5:63:5 | y | |
|
||||
| swap1.cpp:57:23:57:23 | y | swap1.cpp:65:10:65:10 | y | |
|
||||
| swap1.cpp:58:5:58:5 | x [post update] | swap1.cpp:60:10:60:10 | x | |
|
||||
| swap1.cpp:58:5:58:5 | x [post update] | swap1.cpp:63:9:63:9 | x | |
|
||||
| swap1.cpp:58:5:58:5 | x [post update] | swap1.cpp:66:10:66:10 | x | |
|
||||
| swap1.cpp:58:5:58:22 | ... = ... | swap1.cpp:60:12:60:16 | data1 | |
|
||||
| swap1.cpp:58:5:58:22 | ... = ... | swap1.cpp:66:12:66:16 | data1 | |
|
||||
| swap1.cpp:58:15:58:20 | call to source | swap1.cpp:58:5:58:22 | ... = ... | |
|
||||
| swap1.cpp:63:5:63:5 | ref arg y | swap1.cpp:65:10:65:10 | y | |
|
||||
| swap1.cpp:68:23:68:24 | z1 | swap1.cpp:69:5:69:6 | z1 | |
|
||||
| swap1.cpp:68:23:68:24 | z1 | swap1.cpp:70:10:70:11 | z1 | |
|
||||
| swap1.cpp:68:23:68:24 | z1 | swap1.cpp:72:10:72:11 | z1 | |
|
||||
| swap1.cpp:68:23:68:24 | z1 | swap1.cpp:75:10:75:11 | z1 | |
|
||||
| swap1.cpp:68:27:68:28 | z2 | swap1.cpp:72:14:72:15 | z2 | |
|
||||
| swap1.cpp:68:27:68:28 | z2 | swap1.cpp:74:10:74:11 | z2 | |
|
||||
| swap1.cpp:69:5:69:6 | z1 [post update] | swap1.cpp:70:10:70:11 | z1 | |
|
||||
| swap1.cpp:69:5:69:6 | z1 [post update] | swap1.cpp:72:10:72:11 | z1 | |
|
||||
| swap1.cpp:69:5:69:6 | z1 [post update] | swap1.cpp:75:10:75:11 | z1 | |
|
||||
| swap1.cpp:69:5:69:23 | ... = ... | swap1.cpp:70:13:70:17 | data1 | |
|
||||
| swap1.cpp:69:5:69:23 | ... = ... | swap1.cpp:75:13:75:17 | data1 | |
|
||||
| swap1.cpp:69:16:69:21 | call to source | swap1.cpp:69:5:69:23 | ... = ... | |
|
||||
| swap1.cpp:72:10:72:11 | ref arg z1 | swap1.cpp:75:10:75:11 | z1 | |
|
||||
| swap1.cpp:72:14:72:15 | ref arg z2 | swap1.cpp:74:10:74:11 | z2 | |
|
||||
| swap1.cpp:80:23:80:23 | x | swap1.cpp:82:5:82:5 | x | |
|
||||
| swap1.cpp:80:23:80:23 | x | swap1.cpp:84:10:84:10 | x | |
|
||||
| swap1.cpp:80:23:80:23 | x | swap1.cpp:87:19:87:19 | x | |
|
||||
| swap1.cpp:80:23:80:23 | x | swap1.cpp:90:10:90:10 | x | |
|
||||
| swap1.cpp:81:23:81:23 | y | swap1.cpp:85:10:85:10 | y | |
|
||||
| swap1.cpp:81:23:81:23 | y | swap1.cpp:87:5:87:5 | y | |
|
||||
| swap1.cpp:81:23:81:23 | y | swap1.cpp:89:10:89:10 | y | |
|
||||
| swap1.cpp:82:5:82:5 | x [post update] | swap1.cpp:84:10:84:10 | x | |
|
||||
| swap1.cpp:82:5:82:5 | x [post update] | swap1.cpp:87:19:87:19 | x | |
|
||||
| swap1.cpp:82:5:82:5 | x [post update] | swap1.cpp:90:10:90:10 | x | |
|
||||
| swap1.cpp:82:5:82:22 | ... = ... | swap1.cpp:84:12:84:16 | data1 | |
|
||||
| swap1.cpp:82:5:82:22 | ... = ... | swap1.cpp:90:12:90:16 | data1 | |
|
||||
| swap1.cpp:82:15:82:20 | call to source | swap1.cpp:82:5:82:22 | ... = ... | |
|
||||
| swap1.cpp:87:5:87:5 | ref arg y | swap1.cpp:89:10:89:10 | y | |
|
||||
| swap1.cpp:87:9:87:17 | ref arg call to move | swap1.cpp:87:19:87:19 | x [inner post update] | |
|
||||
| swap1.cpp:87:9:87:17 | ref arg call to move | swap1.cpp:90:10:90:10 | x | |
|
||||
| swap1.cpp:87:19:87:19 | x | swap1.cpp:87:9:87:17 | call to move | |
|
||||
| swap1.cpp:95:23:95:31 | move_from | swap1.cpp:96:5:96:13 | move_from | |
|
||||
| swap1.cpp:95:23:95:31 | move_from | swap1.cpp:98:10:98:18 | move_from | |
|
||||
| swap1.cpp:95:23:95:31 | move_from | swap1.cpp:100:41:100:49 | move_from | |
|
||||
| swap1.cpp:96:5:96:13 | move_from [post update] | swap1.cpp:98:10:98:18 | move_from | |
|
||||
| swap1.cpp:96:5:96:13 | move_from [post update] | swap1.cpp:100:41:100:49 | move_from | |
|
||||
| swap1.cpp:96:5:96:30 | ... = ... | swap1.cpp:98:20:98:24 | data1 | |
|
||||
| swap1.cpp:96:23:96:28 | call to source | swap1.cpp:96:5:96:30 | ... = ... | |
|
||||
| swap1.cpp:100:31:100:39 | ref arg call to move | swap1.cpp:100:41:100:49 | move_from [inner post update] | |
|
||||
| swap1.cpp:100:31:100:51 | call to Class | swap1.cpp:102:10:102:16 | move_to | |
|
||||
| swap1.cpp:100:41:100:49 | move_from | swap1.cpp:100:31:100:39 | call to move | |
|
||||
| swap2.cpp:14:17:14:17 | t | swap2.cpp:14:17:14:17 | t | |
|
||||
| swap2.cpp:14:17:14:17 | t | swap2.cpp:14:17:14:17 | t | |
|
||||
| swap2.cpp:14:17:14:17 | t | swap2.cpp:14:56:14:56 | t | |
|
||||
| swap2.cpp:14:17:14:17 | t | swap2.cpp:14:56:14:56 | t | |
|
||||
| swap2.cpp:24:9:24:13 | this | swap2.cpp:24:31:24:34 | this | |
|
||||
| swap2.cpp:24:23:24:26 | that | swap2.cpp:24:23:24:26 | that | |
|
||||
| swap2.cpp:24:23:24:26 | that | swap2.cpp:24:36:24:39 | that | |
|
||||
| swap2.cpp:24:36:24:39 | ref arg that | swap2.cpp:24:23:24:26 | that | |
|
||||
| swap2.cpp:25:9:25:13 | this | swap2.cpp:25:36:25:52 | constructor init of field data1 [pre-this] | |
|
||||
| swap2.cpp:25:28:25:31 | that | swap2.cpp:25:42:25:45 | that | |
|
||||
| swap2.cpp:25:28:25:31 | that | swap2.cpp:25:61:25:64 | that | |
|
||||
| swap2.cpp:25:36:25:52 | constructor init of field data1 [post-this] | swap2.cpp:25:55:25:71 | constructor init of field data2 [pre-this] | |
|
||||
| swap2.cpp:25:36:25:52 | constructor init of field data1 [pre-this] | swap2.cpp:25:55:25:71 | constructor init of field data2 [pre-this] | |
|
||||
| swap2.cpp:25:47:25:51 | data1 | swap2.cpp:25:36:25:52 | constructor init of field data1 | TAINT |
|
||||
| swap2.cpp:25:47:25:51 | data1 | swap2.cpp:25:47:25:51 | data1 | |
|
||||
| swap2.cpp:25:66:25:70 | data2 | swap2.cpp:25:55:25:71 | constructor init of field data2 | TAINT |
|
||||
| swap2.cpp:25:66:25:70 | data2 | swap2.cpp:25:66:25:70 | data2 | |
|
||||
| swap2.cpp:27:16:27:24 | this | swap2.cpp:30:13:30:16 | this | |
|
||||
| swap2.cpp:27:39:27:42 | that | swap2.cpp:29:24:29:27 | that | |
|
||||
| swap2.cpp:29:23:29:27 | call to Class | swap2.cpp:30:18:30:20 | tmp | |
|
||||
| swap2.cpp:30:13:30:16 | ref arg this | swap2.cpp:31:21:31:24 | this | |
|
||||
| swap2.cpp:30:13:30:16 | this | swap2.cpp:31:21:31:24 | this | |
|
||||
| swap2.cpp:31:21:31:24 | this | swap2.cpp:31:20:31:24 | * ... | TAINT |
|
||||
| swap2.cpp:34:16:34:24 | this | swap2.cpp:36:13:36:16 | this | |
|
||||
| swap2.cpp:34:34:34:37 | that | swap2.cpp:34:34:34:37 | that | |
|
||||
| swap2.cpp:34:34:34:37 | that | swap2.cpp:36:18:36:21 | that | |
|
||||
| swap2.cpp:36:13:36:16 | ref arg this | swap2.cpp:37:21:37:24 | this | |
|
||||
| swap2.cpp:36:13:36:16 | this | swap2.cpp:37:21:37:24 | this | |
|
||||
| swap2.cpp:36:18:36:21 | ref arg that | swap2.cpp:34:34:34:37 | that | |
|
||||
| swap2.cpp:37:21:37:24 | this | swap2.cpp:37:20:37:24 | * ... | TAINT |
|
||||
| swap2.cpp:40:14:40:17 | this | swap2.cpp:43:18:43:22 | this | |
|
||||
| swap2.cpp:40:26:40:29 | that | swap2.cpp:40:26:40:29 | that | |
|
||||
| swap2.cpp:40:26:40:29 | that | swap2.cpp:43:25:43:28 | that | |
|
||||
| swap2.cpp:40:26:40:29 | that | swap2.cpp:43:50:43:53 | that | |
|
||||
| swap2.cpp:43:18:43:22 | data1 | swap2.cpp:43:30:43:34 | ref arg data1 | |
|
||||
| swap2.cpp:43:18:43:22 | this | swap2.cpp:43:43:43:47 | this | |
|
||||
| swap2.cpp:43:18:43:22 | this [post update] | swap2.cpp:43:43:43:47 | this | |
|
||||
| swap2.cpp:43:25:43:28 | that | swap2.cpp:43:18:43:22 | ref arg data1 | |
|
||||
| swap2.cpp:43:25:43:28 | that [post update] | swap2.cpp:40:26:40:29 | that | |
|
||||
| swap2.cpp:43:25:43:28 | that [post update] | swap2.cpp:43:50:43:53 | that | |
|
||||
| swap2.cpp:43:30:43:34 | data1 | swap2.cpp:43:18:43:22 | ref arg data1 | |
|
||||
| swap2.cpp:43:43:43:47 | data2 | swap2.cpp:43:55:43:59 | ref arg data2 | |
|
||||
| swap2.cpp:43:50:43:53 | that | swap2.cpp:43:43:43:47 | ref arg data2 | |
|
||||
| swap2.cpp:43:50:43:53 | that [post update] | swap2.cpp:40:26:40:29 | that | |
|
||||
| swap2.cpp:43:55:43:59 | data2 | swap2.cpp:43:43:43:47 | ref arg data2 | |
|
||||
| swap2.cpp:48:22:48:22 | x | swap1.cpp:48:22:48:22 | x | |
|
||||
| swap2.cpp:48:22:48:22 | x | swap1.cpp:50:9:50:9 | x | |
|
||||
| swap2.cpp:48:22:48:22 | x | swap2.cpp:48:22:48:22 | x | |
|
||||
| swap2.cpp:48:22:48:22 | x | swap2.cpp:50:9:50:9 | x | |
|
||||
| swap2.cpp:48:32:48:32 | y | swap1.cpp:48:32:48:32 | y | |
|
||||
| swap2.cpp:48:32:48:32 | y | swap1.cpp:50:16:50:16 | y | |
|
||||
| swap2.cpp:48:32:48:32 | y | swap2.cpp:48:32:48:32 | y | |
|
||||
| swap2.cpp:48:32:48:32 | y | swap2.cpp:50:16:50:16 | y | |
|
||||
| swap2.cpp:50:9:50:9 | ref arg x | swap1.cpp:48:22:48:22 | x | |
|
||||
| swap2.cpp:50:9:50:9 | ref arg x | swap2.cpp:48:22:48:22 | x | |
|
||||
| swap2.cpp:50:16:50:16 | ref arg y | swap1.cpp:48:32:48:32 | y | |
|
||||
| swap2.cpp:50:16:50:16 | ref arg y | swap2.cpp:48:32:48:32 | y | |
|
||||
| swap2.cpp:56:23:56:23 | x | swap2.cpp:58:5:58:5 | x | |
|
||||
| swap2.cpp:56:23:56:23 | x | swap2.cpp:60:10:60:10 | x | |
|
||||
| swap2.cpp:56:23:56:23 | x | swap2.cpp:63:9:63:9 | x | |
|
||||
| swap2.cpp:56:23:56:23 | x | swap2.cpp:66:10:66:10 | x | |
|
||||
| swap2.cpp:57:23:57:23 | y | swap2.cpp:61:10:61:10 | y | |
|
||||
| swap2.cpp:57:23:57:23 | y | swap2.cpp:63:5:63:5 | y | |
|
||||
| swap2.cpp:57:23:57:23 | y | swap2.cpp:65:10:65:10 | y | |
|
||||
| swap2.cpp:58:5:58:5 | x [post update] | swap2.cpp:60:10:60:10 | x | |
|
||||
| swap2.cpp:58:5:58:5 | x [post update] | swap2.cpp:63:9:63:9 | x | |
|
||||
| swap2.cpp:58:5:58:5 | x [post update] | swap2.cpp:66:10:66:10 | x | |
|
||||
| swap2.cpp:58:5:58:22 | ... = ... | swap2.cpp:60:12:60:16 | data1 | |
|
||||
| swap2.cpp:58:5:58:22 | ... = ... | swap2.cpp:66:12:66:16 | data1 | |
|
||||
| swap2.cpp:58:15:58:20 | call to source | swap2.cpp:58:5:58:22 | ... = ... | |
|
||||
| swap2.cpp:63:5:63:5 | ref arg y | swap2.cpp:65:10:65:10 | y | |
|
||||
| swap2.cpp:68:23:68:24 | z1 | swap2.cpp:69:5:69:6 | z1 | |
|
||||
| swap2.cpp:68:23:68:24 | z1 | swap2.cpp:70:10:70:11 | z1 | |
|
||||
| swap2.cpp:68:23:68:24 | z1 | swap2.cpp:72:10:72:11 | z1 | |
|
||||
| swap2.cpp:68:23:68:24 | z1 | swap2.cpp:75:10:75:11 | z1 | |
|
||||
| swap2.cpp:68:27:68:28 | z2 | swap2.cpp:72:14:72:15 | z2 | |
|
||||
| swap2.cpp:68:27:68:28 | z2 | swap2.cpp:74:10:74:11 | z2 | |
|
||||
| swap2.cpp:69:5:69:6 | z1 [post update] | swap2.cpp:70:10:70:11 | z1 | |
|
||||
| swap2.cpp:69:5:69:6 | z1 [post update] | swap2.cpp:72:10:72:11 | z1 | |
|
||||
| swap2.cpp:69:5:69:6 | z1 [post update] | swap2.cpp:75:10:75:11 | z1 | |
|
||||
| swap2.cpp:69:5:69:23 | ... = ... | swap2.cpp:70:13:70:17 | data1 | |
|
||||
| swap2.cpp:69:5:69:23 | ... = ... | swap2.cpp:75:13:75:17 | data1 | |
|
||||
| swap2.cpp:69:16:69:21 | call to source | swap2.cpp:69:5:69:23 | ... = ... | |
|
||||
| swap2.cpp:72:10:72:11 | ref arg z1 | swap2.cpp:75:10:75:11 | z1 | |
|
||||
| swap2.cpp:72:14:72:15 | ref arg z2 | swap2.cpp:74:10:74:11 | z2 | |
|
||||
| swap2.cpp:80:23:80:23 | x | swap2.cpp:82:5:82:5 | x | |
|
||||
| swap2.cpp:80:23:80:23 | x | swap2.cpp:84:10:84:10 | x | |
|
||||
| swap2.cpp:80:23:80:23 | x | swap2.cpp:87:19:87:19 | x | |
|
||||
| swap2.cpp:80:23:80:23 | x | swap2.cpp:90:10:90:10 | x | |
|
||||
| swap2.cpp:81:23:81:23 | y | swap2.cpp:85:10:85:10 | y | |
|
||||
| swap2.cpp:81:23:81:23 | y | swap2.cpp:87:5:87:5 | y | |
|
||||
| swap2.cpp:81:23:81:23 | y | swap2.cpp:89:10:89:10 | y | |
|
||||
| swap2.cpp:82:5:82:5 | x [post update] | swap2.cpp:84:10:84:10 | x | |
|
||||
| swap2.cpp:82:5:82:5 | x [post update] | swap2.cpp:87:19:87:19 | x | |
|
||||
| swap2.cpp:82:5:82:5 | x [post update] | swap2.cpp:90:10:90:10 | x | |
|
||||
| swap2.cpp:82:5:82:22 | ... = ... | swap2.cpp:84:12:84:16 | data1 | |
|
||||
| swap2.cpp:82:5:82:22 | ... = ... | swap2.cpp:90:12:90:16 | data1 | |
|
||||
| swap2.cpp:82:15:82:20 | call to source | swap2.cpp:82:5:82:22 | ... = ... | |
|
||||
| swap2.cpp:87:5:87:5 | ref arg y | swap2.cpp:89:10:89:10 | y | |
|
||||
| swap2.cpp:87:9:87:17 | ref arg call to move | swap2.cpp:87:19:87:19 | x [inner post update] | |
|
||||
| swap2.cpp:87:9:87:17 | ref arg call to move | swap2.cpp:90:10:90:10 | x | |
|
||||
| swap2.cpp:87:19:87:19 | x | swap2.cpp:87:9:87:17 | call to move | |
|
||||
| swap2.cpp:95:23:95:31 | move_from | swap2.cpp:96:5:96:13 | move_from | |
|
||||
| swap2.cpp:95:23:95:31 | move_from | swap2.cpp:98:10:98:18 | move_from | |
|
||||
| swap2.cpp:95:23:95:31 | move_from | swap2.cpp:100:41:100:49 | move_from | |
|
||||
| swap2.cpp:96:5:96:13 | move_from [post update] | swap2.cpp:98:10:98:18 | move_from | |
|
||||
| swap2.cpp:96:5:96:13 | move_from [post update] | swap2.cpp:100:41:100:49 | move_from | |
|
||||
| swap2.cpp:96:5:96:30 | ... = ... | swap2.cpp:98:20:98:24 | data1 | |
|
||||
| swap2.cpp:96:23:96:28 | call to source | swap2.cpp:96:5:96:30 | ... = ... | |
|
||||
| swap2.cpp:100:31:100:39 | ref arg call to move | swap2.cpp:100:41:100:49 | move_from [inner post update] | |
|
||||
| swap2.cpp:100:31:100:51 | call to Class | swap2.cpp:102:10:102:16 | move_to | |
|
||||
| swap2.cpp:100:41:100:49 | move_from | swap2.cpp:100:31:100:39 | call to move | |
|
||||
| taint.cpp:4:27:4:33 | source1 | taint.cpp:6:13:6:19 | source1 | |
|
||||
| taint.cpp:4:40:4:45 | clean1 | taint.cpp:5:8:5:13 | clean1 | |
|
||||
| taint.cpp:4:40:4:45 | clean1 | taint.cpp:6:3:6:8 | clean1 | |
|
||||
|
||||
@@ -131,3 +131,28 @@ void test_strings2()
|
||||
string path3(user_input());
|
||||
sink(path3.c_str(), "r"); // tainted
|
||||
}
|
||||
|
||||
void test_string3()
|
||||
{
|
||||
const char *cs = source();
|
||||
|
||||
// convert char * -> std::string
|
||||
std::string ss(cs);
|
||||
|
||||
sink(cs); // tainted
|
||||
sink(ss); // tainted
|
||||
}
|
||||
|
||||
void test_string4()
|
||||
{
|
||||
const char *cs = source();
|
||||
|
||||
// convert char * -> std::string
|
||||
std::string ss(cs);
|
||||
|
||||
// convert back std::string -> char *
|
||||
cs = ss.c_str();
|
||||
|
||||
sink(cs); // tainted
|
||||
sink(ss); // tainted
|
||||
}
|
||||
|
||||
5
cpp/ql/test/library-tests/dataflow/taint-tests/swap.h
Normal file
5
cpp/ql/test/library-tests/dataflow/taint-tests/swap.h
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace std
|
||||
{
|
||||
template <class T>
|
||||
constexpr void swap(T &a, T &b);
|
||||
}
|
||||
103
cpp/ql/test/library-tests/dataflow/taint-tests/swap1.cpp
Normal file
103
cpp/ql/test/library-tests/dataflow/taint-tests/swap1.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#include "swap.h"
|
||||
/*
|
||||
* Note: This file exists in two versions (swap1.cpp and swap2.cpp).
|
||||
* The only difference is that `IntWrapper` in swap1.cpp contains a single data member, and swap2.cpp
|
||||
* contains two data members.
|
||||
*/
|
||||
|
||||
int source();
|
||||
void sink(...);
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <class T>
|
||||
T &&move(T &t) noexcept { return static_cast<T &&>(t); } // simplified signature (and implementation)
|
||||
} // namespace std
|
||||
|
||||
namespace IntWrapper
|
||||
{
|
||||
struct Class
|
||||
{
|
||||
int data1;
|
||||
|
||||
Class() = default;
|
||||
Class(Class &&that) { swap(that); }
|
||||
Class(const Class &that) : data1(that.data1) {}
|
||||
|
||||
Class &operator=(const Class &that)
|
||||
{
|
||||
auto tmp = that;
|
||||
swap(tmp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Class &operator=(Class &&that)
|
||||
{
|
||||
swap(that);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(Class &that) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
swap(data1, that.data1);
|
||||
}
|
||||
};
|
||||
|
||||
// For ADL
|
||||
void swap(Class &x, Class &y)
|
||||
{
|
||||
x.swap(y);
|
||||
}
|
||||
} // namespace IntWrapper
|
||||
|
||||
void test_copy_assignment_operator()
|
||||
{
|
||||
IntWrapper::Class x;
|
||||
IntWrapper::Class y;
|
||||
x.data1 = source();
|
||||
|
||||
sink(x.data1); // tainted
|
||||
sink(y.data1); // clean
|
||||
|
||||
y = x;
|
||||
|
||||
sink(y.data1); // tainted
|
||||
sink(x.data1); // tainted
|
||||
|
||||
IntWrapper::Class z1, z2;
|
||||
z1.data1 = source();
|
||||
sink(z1.data1); // tainted
|
||||
|
||||
swap(z1, z2);
|
||||
|
||||
sink(z2.data1); // tainted [FALSE NEGATIVE in IR]
|
||||
sink(z1.data1); // clean [FALSE POSITIVE]
|
||||
}
|
||||
|
||||
void test_move_assignment_operator()
|
||||
{
|
||||
IntWrapper::Class x;
|
||||
IntWrapper::Class y;
|
||||
x.data1 = source();
|
||||
|
||||
sink(x.data1); // tainted
|
||||
sink(y.data1); // clean
|
||||
|
||||
y = std::move(x);
|
||||
|
||||
sink(y.data1); // tainted
|
||||
sink(x.data1); // tainted
|
||||
}
|
||||
|
||||
void test_move_constructor()
|
||||
{
|
||||
IntWrapper::Class move_from;
|
||||
move_from.data1 = source();
|
||||
|
||||
sink(move_from.data1); // tainted
|
||||
|
||||
IntWrapper::Class move_to(std::move(move_from));
|
||||
|
||||
sink(move_to.data1); // tainted
|
||||
}
|
||||
103
cpp/ql/test/library-tests/dataflow/taint-tests/swap2.cpp
Normal file
103
cpp/ql/test/library-tests/dataflow/taint-tests/swap2.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#include "swap.h"
|
||||
/*
|
||||
* Note: This file exists in two versions (swap1.cpp and swap2.cpp).
|
||||
* The only difference is that `IntWrapper` in swap1.cpp contains a single data member, and swap2.cpp
|
||||
* contains two data members.
|
||||
*/
|
||||
|
||||
int source();
|
||||
void sink(...);
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <class T>
|
||||
T &&move(T &t) noexcept { return static_cast<T &&>(t); } // simplified signature (and implementation)
|
||||
} // namespace std
|
||||
|
||||
namespace IntWrapper
|
||||
{
|
||||
struct Class
|
||||
{
|
||||
int data1; int data2;
|
||||
|
||||
Class() = default;
|
||||
Class(Class &&that) { swap(that); }
|
||||
Class(const Class &that) : data1(that.data1), data2(that.data2) {}
|
||||
|
||||
Class &operator=(const Class &that)
|
||||
{
|
||||
auto tmp = that;
|
||||
swap(tmp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Class &operator=(Class &&that)
|
||||
{
|
||||
swap(that);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(Class &that) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
swap(data1, that.data1); swap(data2, that.data2);
|
||||
}
|
||||
};
|
||||
|
||||
// For ADL
|
||||
void swap(Class &x, Class &y)
|
||||
{
|
||||
x.swap(y);
|
||||
}
|
||||
} // namespace IntWrapper
|
||||
|
||||
void test_copy_assignment_operator()
|
||||
{
|
||||
IntWrapper::Class x;
|
||||
IntWrapper::Class y;
|
||||
x.data1 = source();
|
||||
|
||||
sink(x.data1); // tainted
|
||||
sink(y.data1); // clean
|
||||
|
||||
y = x;
|
||||
|
||||
sink(y.data1); // tainted
|
||||
sink(x.data1); // tainted
|
||||
|
||||
IntWrapper::Class z1, z2;
|
||||
z1.data1 = source();
|
||||
sink(z1.data1); // tainted
|
||||
|
||||
swap(z1, z2);
|
||||
|
||||
sink(z2.data1); // tainted [FALSE NEGATIVE in IR]
|
||||
sink(z1.data1); // clean [FALSE POSITIVE]
|
||||
}
|
||||
|
||||
void test_move_assignment_operator()
|
||||
{
|
||||
IntWrapper::Class x;
|
||||
IntWrapper::Class y;
|
||||
x.data1 = source();
|
||||
|
||||
sink(x.data1); // tainted
|
||||
sink(y.data1); // clean
|
||||
|
||||
y = std::move(x);
|
||||
|
||||
sink(y.data1); // tainted
|
||||
sink(x.data1); // tainted
|
||||
}
|
||||
|
||||
void test_move_constructor()
|
||||
{
|
||||
IntWrapper::Class move_from;
|
||||
move_from.data1 = source();
|
||||
|
||||
sink(move_from.data1); // tainted
|
||||
|
||||
IntWrapper::Class move_to(std::move(move_from));
|
||||
|
||||
sink(move_to.data1); // tainted
|
||||
}
|
||||
@@ -86,12 +86,12 @@ void class_field_test() {
|
||||
mc1.myMethod();
|
||||
|
||||
sink(mc1.a);
|
||||
sink(mc1.b); // tainted [NOT DETECTED with IR]
|
||||
sink(mc1.c); // tainted [NOT DETECTED with IR]
|
||||
sink(mc1.d); // tainted [NOT DETECTED with IR]
|
||||
sink(mc1.b); // tainted
|
||||
sink(mc1.c); // tainted
|
||||
sink(mc1.d); // tainted
|
||||
sink(mc2.a);
|
||||
sink(mc2.b); // tainted [NOT DETECTED with IR]
|
||||
sink(mc2.c); // tainted [NOT DETECTED with IR]
|
||||
sink(mc2.b); // tainted
|
||||
sink(mc2.c); // tainted
|
||||
sink(mc2.d);
|
||||
}
|
||||
|
||||
@@ -197,9 +197,9 @@ void test_memcpy(int *source) {
|
||||
|
||||
// --- std::swap ---
|
||||
|
||||
namespace std {
|
||||
template<class T> constexpr void swap(T& a, T& b);
|
||||
}
|
||||
#include "swap.h"
|
||||
|
||||
|
||||
|
||||
void test_swap() {
|
||||
int x, y;
|
||||
@@ -483,4 +483,4 @@ void test_getdelim(FILE* source1) {
|
||||
getdelim(&line, &n, '\n', source1);
|
||||
|
||||
sink(line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,38 @@
|
||||
| stl.cpp:125:13:125:17 | call to c_str | stl.cpp:117:10:117:15 | call to source |
|
||||
| stl.cpp:129:13:129:17 | call to c_str | stl.cpp:117:10:117:15 | call to source |
|
||||
| stl.cpp:132:13:132:17 | call to c_str | stl.cpp:117:10:117:15 | call to source |
|
||||
| stl.cpp:142:7:142:8 | cs | stl.cpp:137:19:137:24 | call to source |
|
||||
| stl.cpp:143:7:143:8 | ss | stl.cpp:137:19:137:24 | call to source |
|
||||
| stl.cpp:156:7:156:8 | cs | stl.cpp:148:19:148:24 | call to source |
|
||||
| stl.cpp:157:7:157:8 | ss | stl.cpp:148:19:148:24 | call to source |
|
||||
| swap1.cpp:60:12:60:16 | data1 | swap1.cpp:58:15:58:20 | call to source |
|
||||
| swap1.cpp:65:12:65:16 | data1 | swap1.cpp:58:15:58:20 | call to source |
|
||||
| swap1.cpp:66:12:66:16 | data1 | swap1.cpp:58:15:58:20 | call to source |
|
||||
| swap1.cpp:70:13:70:17 | data1 | swap1.cpp:69:16:69:21 | call to source |
|
||||
| swap1.cpp:74:13:74:17 | data1 | swap1.cpp:69:16:69:21 | call to source |
|
||||
| swap1.cpp:75:13:75:17 | data1 | swap1.cpp:68:27:68:28 | z2 |
|
||||
| swap1.cpp:75:13:75:17 | data1 | swap1.cpp:69:16:69:21 | call to source |
|
||||
| swap1.cpp:84:12:84:16 | data1 | swap1.cpp:82:15:82:20 | call to source |
|
||||
| swap1.cpp:89:12:89:16 | data1 | swap1.cpp:80:23:80:23 | x |
|
||||
| swap1.cpp:89:12:89:16 | data1 | swap1.cpp:82:15:82:20 | call to source |
|
||||
| swap1.cpp:90:12:90:16 | data1 | swap1.cpp:82:15:82:20 | call to source |
|
||||
| swap1.cpp:98:20:98:24 | data1 | swap1.cpp:96:23:96:28 | call to source |
|
||||
| swap1.cpp:102:18:102:22 | data1 | swap1.cpp:95:23:95:31 | move_from |
|
||||
| swap1.cpp:102:18:102:22 | data1 | swap1.cpp:96:23:96:28 | call to source |
|
||||
| swap2.cpp:60:12:60:16 | data1 | swap2.cpp:58:15:58:20 | call to source |
|
||||
| swap2.cpp:65:12:65:16 | data1 | swap2.cpp:58:15:58:20 | call to source |
|
||||
| swap2.cpp:66:12:66:16 | data1 | swap2.cpp:58:15:58:20 | call to source |
|
||||
| swap2.cpp:70:13:70:17 | data1 | swap2.cpp:69:16:69:21 | call to source |
|
||||
| swap2.cpp:74:13:74:17 | data1 | swap2.cpp:69:16:69:21 | call to source |
|
||||
| swap2.cpp:75:13:75:17 | data1 | swap2.cpp:68:27:68:28 | z2 |
|
||||
| swap2.cpp:75:13:75:17 | data1 | swap2.cpp:69:16:69:21 | call to source |
|
||||
| swap2.cpp:84:12:84:16 | data1 | swap2.cpp:82:15:82:20 | call to source |
|
||||
| swap2.cpp:89:12:89:16 | data1 | swap2.cpp:80:23:80:23 | x |
|
||||
| swap2.cpp:89:12:89:16 | data1 | swap2.cpp:82:15:82:20 | call to source |
|
||||
| swap2.cpp:90:12:90:16 | data1 | swap2.cpp:82:15:82:20 | call to source |
|
||||
| swap2.cpp:98:20:98:24 | data1 | swap2.cpp:96:23:96:28 | call to source |
|
||||
| swap2.cpp:102:18:102:22 | data1 | swap2.cpp:95:23:95:31 | move_from |
|
||||
| swap2.cpp:102:18:102:22 | data1 | swap2.cpp:96:23:96:28 | call to source |
|
||||
| taint.cpp:8:8:8:13 | clean1 | taint.cpp:4:27:4:33 | source1 |
|
||||
| taint.cpp:16:8:16:14 | source1 | taint.cpp:12:22:12:27 | call to source |
|
||||
| taint.cpp:17:8:17:16 | ++ ... | taint.cpp:12:22:12:27 | call to source |
|
||||
|
||||
@@ -13,14 +13,21 @@
|
||||
| stl.cpp:125:13:125:17 | stl.cpp:117:10:117:15 | AST only |
|
||||
| stl.cpp:129:13:129:17 | stl.cpp:117:10:117:15 | AST only |
|
||||
| stl.cpp:132:13:132:17 | stl.cpp:117:10:117:15 | AST only |
|
||||
| stl.cpp:142:7:142:8 | stl.cpp:137:19:137:26 | IR only |
|
||||
| stl.cpp:143:7:143:8 | stl.cpp:137:19:137:24 | AST only |
|
||||
| stl.cpp:156:7:156:8 | stl.cpp:148:19:148:24 | AST only |
|
||||
| stl.cpp:157:7:157:8 | stl.cpp:148:19:148:24 | AST only |
|
||||
| swap1.cpp:74:13:74:17 | swap1.cpp:69:16:69:21 | AST only |
|
||||
| swap1.cpp:75:13:75:17 | swap1.cpp:68:27:68:28 | AST only |
|
||||
| swap1.cpp:89:12:89:16 | swap1.cpp:80:23:80:23 | AST only |
|
||||
| swap1.cpp:102:18:102:22 | swap1.cpp:95:23:95:31 | AST only |
|
||||
| swap2.cpp:74:13:74:17 | swap2.cpp:69:16:69:21 | AST only |
|
||||
| swap2.cpp:75:13:75:17 | swap2.cpp:68:27:68:28 | AST only |
|
||||
| swap2.cpp:89:12:89:16 | swap2.cpp:80:23:80:23 | AST only |
|
||||
| swap2.cpp:102:18:102:22 | swap2.cpp:95:23:95:31 | AST only |
|
||||
| taint.cpp:41:7:41:13 | taint.cpp:35:12:35:17 | AST only |
|
||||
| taint.cpp:42:7:42:13 | taint.cpp:35:12:35:17 | AST only |
|
||||
| taint.cpp:43:7:43:13 | taint.cpp:37:22:37:27 | AST only |
|
||||
| taint.cpp:89:11:89:11 | taint.cpp:71:22:71:27 | AST only |
|
||||
| taint.cpp:90:11:90:11 | taint.cpp:72:7:72:12 | AST only |
|
||||
| taint.cpp:91:11:91:11 | taint.cpp:77:7:77:12 | AST only |
|
||||
| taint.cpp:93:11:93:11 | taint.cpp:71:22:71:27 | AST only |
|
||||
| taint.cpp:94:11:94:11 | taint.cpp:72:7:72:12 | AST only |
|
||||
| taint.cpp:109:7:109:13 | taint.cpp:105:12:105:17 | IR only |
|
||||
| taint.cpp:110:7:110:13 | taint.cpp:105:12:105:17 | IR only |
|
||||
| taint.cpp:111:7:111:13 | taint.cpp:106:12:106:17 | IR only |
|
||||
|
||||
@@ -3,9 +3,36 @@
|
||||
| format.cpp:158:7:158:27 | ... + ... | format.cpp:148:16:148:30 | call to source |
|
||||
| stl.cpp:71:7:71:7 | (const char *)... | stl.cpp:67:12:67:17 | call to source |
|
||||
| stl.cpp:71:7:71:7 | a | stl.cpp:67:12:67:17 | call to source |
|
||||
| stl.cpp:142:7:142:8 | cs | stl.cpp:137:19:137:24 | call to source |
|
||||
| stl.cpp:142:7:142:8 | cs | stl.cpp:137:19:137:26 | (const char *)... |
|
||||
| swap1.cpp:60:12:60:16 | data1 | swap1.cpp:58:15:58:20 | call to source |
|
||||
| swap1.cpp:65:12:65:16 | data1 | swap1.cpp:58:15:58:20 | call to source |
|
||||
| swap1.cpp:66:12:66:16 | data1 | swap1.cpp:58:15:58:20 | call to source |
|
||||
| swap1.cpp:70:13:70:17 | data1 | swap1.cpp:69:16:69:21 | call to source |
|
||||
| swap1.cpp:75:13:75:17 | data1 | swap1.cpp:69:16:69:21 | call to source |
|
||||
| swap1.cpp:84:12:84:16 | data1 | swap1.cpp:82:15:82:20 | call to source |
|
||||
| swap1.cpp:89:12:89:16 | data1 | swap1.cpp:82:15:82:20 | call to source |
|
||||
| swap1.cpp:90:12:90:16 | data1 | swap1.cpp:82:15:82:20 | call to source |
|
||||
| swap1.cpp:98:20:98:24 | data1 | swap1.cpp:96:23:96:28 | call to source |
|
||||
| swap1.cpp:102:18:102:22 | data1 | swap1.cpp:96:23:96:28 | call to source |
|
||||
| swap2.cpp:60:12:60:16 | data1 | swap2.cpp:58:15:58:20 | call to source |
|
||||
| swap2.cpp:65:12:65:16 | data1 | swap2.cpp:58:15:58:20 | call to source |
|
||||
| swap2.cpp:66:12:66:16 | data1 | swap2.cpp:58:15:58:20 | call to source |
|
||||
| swap2.cpp:70:13:70:17 | data1 | swap2.cpp:69:16:69:21 | call to source |
|
||||
| swap2.cpp:75:13:75:17 | data1 | swap2.cpp:69:16:69:21 | call to source |
|
||||
| swap2.cpp:84:12:84:16 | data1 | swap2.cpp:82:15:82:20 | call to source |
|
||||
| swap2.cpp:89:12:89:16 | data1 | swap2.cpp:82:15:82:20 | call to source |
|
||||
| swap2.cpp:90:12:90:16 | data1 | swap2.cpp:82:15:82:20 | call to source |
|
||||
| swap2.cpp:98:20:98:24 | data1 | swap2.cpp:96:23:96:28 | call to source |
|
||||
| swap2.cpp:102:18:102:22 | data1 | swap2.cpp:96:23:96:28 | call to source |
|
||||
| taint.cpp:8:8:8:13 | clean1 | taint.cpp:4:27:4:33 | source1 |
|
||||
| taint.cpp:16:8:16:14 | source1 | taint.cpp:12:22:12:27 | call to source |
|
||||
| taint.cpp:17:8:17:16 | ++ ... | taint.cpp:12:22:12:27 | call to source |
|
||||
| taint.cpp:89:11:89:11 | b | taint.cpp:71:22:71:27 | call to source |
|
||||
| taint.cpp:90:11:90:11 | c | taint.cpp:72:7:72:12 | call to source |
|
||||
| taint.cpp:91:11:91:11 | d | taint.cpp:77:7:77:12 | call to source |
|
||||
| taint.cpp:93:11:93:11 | b | taint.cpp:71:22:71:27 | call to source |
|
||||
| taint.cpp:94:11:94:11 | c | taint.cpp:72:7:72:12 | call to source |
|
||||
| taint.cpp:109:7:109:13 | access to array | taint.cpp:105:12:105:17 | call to source |
|
||||
| taint.cpp:110:7:110:13 | access to array | taint.cpp:105:12:105:17 | call to source |
|
||||
| taint.cpp:111:7:111:13 | access to array | taint.cpp:106:12:106:17 | call to source |
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
missingOperand
|
||||
| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
|
||||
| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
|
||||
| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
|
||||
| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
|
||||
| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() |
|
||||
| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() |
|
||||
| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() |
|
||||
| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() |
|
||||
unexpectedOperand
|
||||
duplicateOperand
|
||||
missingPhiOperand
|
||||
@@ -24,6 +24,7 @@ switchInstructionWithoutDefaultEdge
|
||||
notMarkedAsConflated
|
||||
wronglyMarkedAsConflated
|
||||
invalidOverlap
|
||||
nonUniqueEnclosingIRFunction
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
missingOperand
|
||||
| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
|
||||
| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
|
||||
| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
|
||||
| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
|
||||
| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() |
|
||||
| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() |
|
||||
| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() |
|
||||
| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() |
|
||||
unexpectedOperand
|
||||
duplicateOperand
|
||||
missingPhiOperand
|
||||
@@ -24,6 +24,7 @@ switchInstructionWithoutDefaultEdge
|
||||
notMarkedAsConflated
|
||||
wronglyMarkedAsConflated
|
||||
invalidOverlap
|
||||
nonUniqueEnclosingIRFunction
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
missingOperand
|
||||
| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
|
||||
| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
|
||||
| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
|
||||
| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
|
||||
| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() |
|
||||
| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() |
|
||||
| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() |
|
||||
| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() |
|
||||
unexpectedOperand
|
||||
duplicateOperand
|
||||
missingPhiOperand
|
||||
@@ -24,6 +24,7 @@ switchInstructionWithoutDefaultEdge
|
||||
notMarkedAsConflated
|
||||
wronglyMarkedAsConflated
|
||||
invalidOverlap
|
||||
nonUniqueEnclosingIRFunction
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
|
||||
@@ -21,10 +21,11 @@ bad_asts.cpp:
|
||||
# 10| r10_9(int) = Load : &:r10_8, ~m?
|
||||
# 10| r10_10(int) = Add : r10_7, r10_9
|
||||
# 10| mu10_11(int) = Store : &:r10_1, r10_10
|
||||
# 9| r9_10(glval<int>) = VariableAddress[#return] :
|
||||
# 9| v9_11(void) = ReturnValue : &:r9_10, ~m?
|
||||
# 9| v9_12(void) = AliasedUse : ~m?
|
||||
# 9| v9_13(void) = ExitFunction :
|
||||
# 9| v9_10(void) = ReturnIndirection[#this] : &:r9_6, ~m?
|
||||
# 9| r9_11(glval<int>) = VariableAddress[#return] :
|
||||
# 9| v9_12(void) = ReturnValue : &:r9_11, ~m?
|
||||
# 9| v9_13(void) = AliasedUse : ~m?
|
||||
# 9| v9_14(void) = ExitFunction :
|
||||
|
||||
# 14| void Bad::CallBadMemberFunction()
|
||||
# 14| Block 0
|
||||
@@ -58,9 +59,10 @@ bad_asts.cpp:
|
||||
# 22| r22_6(glval<Point>) = Load : &:r22_4, ~m?
|
||||
# 22| mu22_7(Point) = InitializeIndirection[#this] : &:r22_6
|
||||
# 23| v23_1(void) = NoOp :
|
||||
# 22| v22_8(void) = ReturnVoid :
|
||||
# 22| v22_9(void) = AliasedUse : ~m?
|
||||
# 22| v22_10(void) = ExitFunction :
|
||||
# 22| v22_8(void) = ReturnIndirection[#this] : &:r22_6, ~m?
|
||||
# 22| v22_9(void) = ReturnVoid :
|
||||
# 22| v22_10(void) = AliasedUse : ~m?
|
||||
# 22| v22_11(void) = ExitFunction :
|
||||
|
||||
# 26| void Bad::CallCopyConstructor(Bad::Point const&)
|
||||
# 26| Block 0
|
||||
@@ -3448,9 +3450,10 @@ ir.cpp:
|
||||
# 628| r628_13(glval<unknown>) = FunctionAddress[~String] :
|
||||
# 628| v628_14(void) = Call : func:r628_13, this:r628_12
|
||||
# 628| mu628_15(unknown) = ^CallSideEffect : ~m?
|
||||
# 628| v628_16(void) = ReturnVoid :
|
||||
# 628| v628_17(void) = AliasedUse : ~m?
|
||||
# 628| v628_18(void) = ExitFunction :
|
||||
# 628| v628_16(void) = ReturnIndirection[#this] : &:r628_6, ~m?
|
||||
# 628| v628_17(void) = ReturnVoid :
|
||||
# 628| v628_18(void) = AliasedUse : ~m?
|
||||
# 628| v628_19(void) = ExitFunction :
|
||||
|
||||
# 630| int C::StaticMemberFunction(int)
|
||||
# 630| Block 0
|
||||
@@ -3483,10 +3486,11 @@ ir.cpp:
|
||||
# 635| r635_2(glval<int>) = VariableAddress[x] :
|
||||
# 635| r635_3(int) = Load : &:r635_2, ~m?
|
||||
# 635| mu635_4(int) = Store : &:r635_1, r635_3
|
||||
# 634| r634_10(glval<int>) = VariableAddress[#return] :
|
||||
# 634| v634_11(void) = ReturnValue : &:r634_10, ~m?
|
||||
# 634| v634_12(void) = AliasedUse : ~m?
|
||||
# 634| v634_13(void) = ExitFunction :
|
||||
# 634| v634_10(void) = ReturnIndirection[#this] : &:r634_6, ~m?
|
||||
# 634| r634_11(glval<int>) = VariableAddress[#return] :
|
||||
# 634| v634_12(void) = ReturnValue : &:r634_11, ~m?
|
||||
# 634| v634_13(void) = AliasedUse : ~m?
|
||||
# 634| v634_14(void) = ExitFunction :
|
||||
|
||||
# 638| int C::VirtualMemberFunction(int)
|
||||
# 638| Block 0
|
||||
@@ -3503,10 +3507,11 @@ ir.cpp:
|
||||
# 639| r639_2(glval<int>) = VariableAddress[x] :
|
||||
# 639| r639_3(int) = Load : &:r639_2, ~m?
|
||||
# 639| mu639_4(int) = Store : &:r639_1, r639_3
|
||||
# 638| r638_10(glval<int>) = VariableAddress[#return] :
|
||||
# 638| v638_11(void) = ReturnValue : &:r638_10, ~m?
|
||||
# 638| v638_12(void) = AliasedUse : ~m?
|
||||
# 638| v638_13(void) = ExitFunction :
|
||||
# 638| v638_10(void) = ReturnIndirection[#this] : &:r638_6, ~m?
|
||||
# 638| r638_11(glval<int>) = VariableAddress[#return] :
|
||||
# 638| v638_12(void) = ReturnValue : &:r638_11, ~m?
|
||||
# 638| v638_13(void) = AliasedUse : ~m?
|
||||
# 638| v638_14(void) = ExitFunction :
|
||||
|
||||
# 642| void C::FieldAccess()
|
||||
# 642| Block 0
|
||||
@@ -3555,9 +3560,10 @@ ir.cpp:
|
||||
# 649| r649_5(glval<int>) = VariableAddress[x] :
|
||||
# 649| mu649_6(int) = Store : &:r649_5, r649_4
|
||||
# 650| v650_1(void) = NoOp :
|
||||
# 642| v642_8(void) = ReturnVoid :
|
||||
# 642| v642_9(void) = AliasedUse : ~m?
|
||||
# 642| v642_10(void) = ExitFunction :
|
||||
# 642| v642_8(void) = ReturnIndirection[#this] : &:r642_6, ~m?
|
||||
# 642| v642_9(void) = ReturnVoid :
|
||||
# 642| v642_10(void) = AliasedUse : ~m?
|
||||
# 642| v642_11(void) = ExitFunction :
|
||||
|
||||
# 652| void C::MethodCalls()
|
||||
# 652| Block 0
|
||||
@@ -3594,9 +3600,10 @@ ir.cpp:
|
||||
# 655| v655_7(void) = ^BufferReadSideEffect[-1] : &:r655_2, ~m?
|
||||
# 655| mu655_8(C) = ^IndirectMayWriteSideEffect[-1] : &:r655_2
|
||||
# 656| v656_1(void) = NoOp :
|
||||
# 652| v652_8(void) = ReturnVoid :
|
||||
# 652| v652_9(void) = AliasedUse : ~m?
|
||||
# 652| v652_10(void) = ExitFunction :
|
||||
# 652| v652_8(void) = ReturnIndirection[#this] : &:r652_6, ~m?
|
||||
# 652| v652_9(void) = ReturnVoid :
|
||||
# 652| v652_10(void) = AliasedUse : ~m?
|
||||
# 652| v652_11(void) = ExitFunction :
|
||||
|
||||
# 658| void C::C()
|
||||
# 658| Block 0
|
||||
@@ -3631,9 +3638,10 @@ ir.cpp:
|
||||
# 662| v662_8(void) = ^BufferReadSideEffect[0] : &:r662_4, ~m?
|
||||
# 662| mu662_9(unknown) = ^BufferMayWriteSideEffect[0] : &:r662_4
|
||||
# 664| v664_1(void) = NoOp :
|
||||
# 658| v658_8(void) = ReturnVoid :
|
||||
# 658| v658_9(void) = AliasedUse : ~m?
|
||||
# 658| v658_10(void) = ExitFunction :
|
||||
# 658| v658_8(void) = ReturnIndirection[#this] : &:r658_6, ~m?
|
||||
# 658| v658_9(void) = ReturnVoid :
|
||||
# 658| v658_10(void) = AliasedUse : ~m?
|
||||
# 658| v658_11(void) = ExitFunction :
|
||||
|
||||
# 675| int DerefReference(int&)
|
||||
# 675| Block 0
|
||||
@@ -4014,11 +4022,12 @@ ir.cpp:
|
||||
#-----| r0_13(glval<Base>) = CopyValue : r0_12
|
||||
#-----| r0_14(Base &) = CopyValue : r0_13
|
||||
#-----| mu0_15(Base &) = Store : &:r0_10, r0_14
|
||||
# 745| v745_20(void) = ReturnIndirection[#this] : &:r745_6, ~m?
|
||||
#-----| v0_16(void) = ReturnIndirection[p#0] : &:r0_3, ~m?
|
||||
# 745| r745_20(glval<Base &>) = VariableAddress[#return] :
|
||||
# 745| v745_21(void) = ReturnValue : &:r745_20, ~m?
|
||||
# 745| v745_22(void) = AliasedUse : ~m?
|
||||
# 745| v745_23(void) = ExitFunction :
|
||||
# 745| r745_21(glval<Base &>) = VariableAddress[#return] :
|
||||
# 745| v745_22(void) = ReturnValue : &:r745_21, ~m?
|
||||
# 745| v745_23(void) = AliasedUse : ~m?
|
||||
# 745| v745_24(void) = ExitFunction :
|
||||
|
||||
# 745| void Base::Base(Base const&)
|
||||
# 745| Block 0
|
||||
@@ -4039,10 +4048,11 @@ ir.cpp:
|
||||
# 745| mu745_11(unknown) = ^CallSideEffect : ~m?
|
||||
# 745| mu745_12(String) = ^IndirectMayWriteSideEffect[-1] : &:r745_8
|
||||
# 745| v745_13(void) = NoOp :
|
||||
# 745| v745_14(void) = ReturnIndirection[#this] : &:r745_6, ~m?
|
||||
#-----| v0_5(void) = ReturnIndirection[p#0] : &:r0_3, ~m?
|
||||
# 745| v745_14(void) = ReturnVoid :
|
||||
# 745| v745_15(void) = AliasedUse : ~m?
|
||||
# 745| v745_16(void) = ExitFunction :
|
||||
# 745| v745_15(void) = ReturnVoid :
|
||||
# 745| v745_16(void) = AliasedUse : ~m?
|
||||
# 745| v745_17(void) = ExitFunction :
|
||||
|
||||
# 748| void Base::Base()
|
||||
# 748| Block 0
|
||||
@@ -4059,9 +4069,10 @@ ir.cpp:
|
||||
# 748| mu748_11(unknown) = ^CallSideEffect : ~m?
|
||||
# 748| mu748_12(String) = ^IndirectMayWriteSideEffect[-1] : &:r748_8
|
||||
# 749| v749_1(void) = NoOp :
|
||||
# 748| v748_13(void) = ReturnVoid :
|
||||
# 748| v748_14(void) = AliasedUse : ~m?
|
||||
# 748| v748_15(void) = ExitFunction :
|
||||
# 748| v748_13(void) = ReturnIndirection[#this] : &:r748_6, ~m?
|
||||
# 748| v748_14(void) = ReturnVoid :
|
||||
# 748| v748_15(void) = AliasedUse : ~m?
|
||||
# 748| v748_16(void) = ExitFunction :
|
||||
|
||||
# 750| void Base::~Base()
|
||||
# 750| Block 0
|
||||
@@ -4077,9 +4088,10 @@ ir.cpp:
|
||||
# 751| r751_3(glval<unknown>) = FunctionAddress[~String] :
|
||||
# 751| v751_4(void) = Call : func:r751_3, this:r751_2
|
||||
# 751| mu751_5(unknown) = ^CallSideEffect : ~m?
|
||||
# 750| v750_8(void) = ReturnVoid :
|
||||
# 750| v750_9(void) = AliasedUse : ~m?
|
||||
# 750| v750_10(void) = ExitFunction :
|
||||
# 750| v750_8(void) = ReturnIndirection[#this] : &:r750_6, ~m?
|
||||
# 750| v750_9(void) = ReturnVoid :
|
||||
# 750| v750_10(void) = AliasedUse : ~m?
|
||||
# 750| v750_11(void) = ExitFunction :
|
||||
|
||||
# 754| Middle& Middle::operator=(Middle const&)
|
||||
# 754| Block 0
|
||||
@@ -4135,11 +4147,12 @@ ir.cpp:
|
||||
#-----| r0_22(glval<Middle>) = CopyValue : r0_21
|
||||
#-----| r0_23(Middle &) = CopyValue : r0_22
|
||||
#-----| mu0_24(Middle &) = Store : &:r0_19, r0_23
|
||||
# 754| v754_29(void) = ReturnIndirection[#this] : &:r754_6, ~m?
|
||||
#-----| v0_25(void) = ReturnIndirection[p#0] : &:r0_3, ~m?
|
||||
# 754| r754_29(glval<Middle &>) = VariableAddress[#return] :
|
||||
# 754| v754_30(void) = ReturnValue : &:r754_29, ~m?
|
||||
# 754| v754_31(void) = AliasedUse : ~m?
|
||||
# 754| v754_32(void) = ExitFunction :
|
||||
# 754| r754_30(glval<Middle &>) = VariableAddress[#return] :
|
||||
# 754| v754_31(void) = ReturnValue : &:r754_30, ~m?
|
||||
# 754| v754_32(void) = AliasedUse : ~m?
|
||||
# 754| v754_33(void) = ExitFunction :
|
||||
|
||||
# 757| void Middle::Middle()
|
||||
# 757| Block 0
|
||||
@@ -4161,9 +4174,10 @@ ir.cpp:
|
||||
# 757| mu757_16(unknown) = ^CallSideEffect : ~m?
|
||||
# 757| mu757_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r757_13
|
||||
# 758| v758_1(void) = NoOp :
|
||||
# 757| v757_18(void) = ReturnVoid :
|
||||
# 757| v757_19(void) = AliasedUse : ~m?
|
||||
# 757| v757_20(void) = ExitFunction :
|
||||
# 757| v757_18(void) = ReturnIndirection[#this] : &:r757_6, ~m?
|
||||
# 757| v757_19(void) = ReturnVoid :
|
||||
# 757| v757_20(void) = AliasedUse : ~m?
|
||||
# 757| v757_21(void) = ExitFunction :
|
||||
|
||||
# 759| void Middle::~Middle()
|
||||
# 759| Block 0
|
||||
@@ -4183,9 +4197,10 @@ ir.cpp:
|
||||
# 760| r760_7(glval<unknown>) = FunctionAddress[~Base] :
|
||||
# 760| v760_8(void) = Call : func:r760_7, this:r760_6
|
||||
# 760| mu760_9(unknown) = ^CallSideEffect : ~m?
|
||||
# 759| v759_8(void) = ReturnVoid :
|
||||
# 759| v759_9(void) = AliasedUse : ~m?
|
||||
# 759| v759_10(void) = ExitFunction :
|
||||
# 759| v759_8(void) = ReturnIndirection[#this] : &:r759_6, ~m?
|
||||
# 759| v759_9(void) = ReturnVoid :
|
||||
# 759| v759_10(void) = AliasedUse : ~m?
|
||||
# 759| v759_11(void) = ExitFunction :
|
||||
|
||||
# 763| Derived& Derived::operator=(Derived const&)
|
||||
# 763| Block 0
|
||||
@@ -4241,11 +4256,12 @@ ir.cpp:
|
||||
#-----| r0_22(glval<Derived>) = CopyValue : r0_21
|
||||
#-----| r0_23(Derived &) = CopyValue : r0_22
|
||||
#-----| mu0_24(Derived &) = Store : &:r0_19, r0_23
|
||||
# 763| v763_29(void) = ReturnIndirection[#this] : &:r763_6, ~m?
|
||||
#-----| v0_25(void) = ReturnIndirection[p#0] : &:r0_3, ~m?
|
||||
# 763| r763_29(glval<Derived &>) = VariableAddress[#return] :
|
||||
# 763| v763_30(void) = ReturnValue : &:r763_29, ~m?
|
||||
# 763| v763_31(void) = AliasedUse : ~m?
|
||||
# 763| v763_32(void) = ExitFunction :
|
||||
# 763| r763_30(glval<Derived &>) = VariableAddress[#return] :
|
||||
# 763| v763_31(void) = ReturnValue : &:r763_30, ~m?
|
||||
# 763| v763_32(void) = AliasedUse : ~m?
|
||||
# 763| v763_33(void) = ExitFunction :
|
||||
|
||||
# 766| void Derived::Derived()
|
||||
# 766| Block 0
|
||||
@@ -4267,9 +4283,10 @@ ir.cpp:
|
||||
# 766| mu766_16(unknown) = ^CallSideEffect : ~m?
|
||||
# 766| mu766_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r766_13
|
||||
# 767| v767_1(void) = NoOp :
|
||||
# 766| v766_18(void) = ReturnVoid :
|
||||
# 766| v766_19(void) = AliasedUse : ~m?
|
||||
# 766| v766_20(void) = ExitFunction :
|
||||
# 766| v766_18(void) = ReturnIndirection[#this] : &:r766_6, ~m?
|
||||
# 766| v766_19(void) = ReturnVoid :
|
||||
# 766| v766_20(void) = AliasedUse : ~m?
|
||||
# 766| v766_21(void) = ExitFunction :
|
||||
|
||||
# 768| void Derived::~Derived()
|
||||
# 768| Block 0
|
||||
@@ -4289,9 +4306,10 @@ ir.cpp:
|
||||
# 769| r769_7(glval<unknown>) = FunctionAddress[~Middle] :
|
||||
# 769| v769_8(void) = Call : func:r769_7, this:r769_6
|
||||
# 769| mu769_9(unknown) = ^CallSideEffect : ~m?
|
||||
# 768| v768_8(void) = ReturnVoid :
|
||||
# 768| v768_9(void) = AliasedUse : ~m?
|
||||
# 768| v768_10(void) = ExitFunction :
|
||||
# 768| v768_8(void) = ReturnIndirection[#this] : &:r768_6, ~m?
|
||||
# 768| v768_9(void) = ReturnVoid :
|
||||
# 768| v768_10(void) = AliasedUse : ~m?
|
||||
# 768| v768_11(void) = ExitFunction :
|
||||
|
||||
# 775| void MiddleVB1::MiddleVB1()
|
||||
# 775| Block 0
|
||||
@@ -4313,9 +4331,10 @@ ir.cpp:
|
||||
# 775| mu775_16(unknown) = ^CallSideEffect : ~m?
|
||||
# 775| mu775_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r775_13
|
||||
# 776| v776_1(void) = NoOp :
|
||||
# 775| v775_18(void) = ReturnVoid :
|
||||
# 775| v775_19(void) = AliasedUse : ~m?
|
||||
# 775| v775_20(void) = ExitFunction :
|
||||
# 775| v775_18(void) = ReturnIndirection[#this] : &:r775_6, ~m?
|
||||
# 775| v775_19(void) = ReturnVoid :
|
||||
# 775| v775_20(void) = AliasedUse : ~m?
|
||||
# 775| v775_21(void) = ExitFunction :
|
||||
|
||||
# 777| void MiddleVB1::~MiddleVB1()
|
||||
# 777| Block 0
|
||||
@@ -4335,9 +4354,10 @@ ir.cpp:
|
||||
# 778| r778_7(glval<unknown>) = FunctionAddress[~Base] :
|
||||
# 778| v778_8(void) = Call : func:r778_7, this:r778_6
|
||||
# 778| mu778_9(unknown) = ^CallSideEffect : ~m?
|
||||
# 777| v777_8(void) = ReturnVoid :
|
||||
# 777| v777_9(void) = AliasedUse : ~m?
|
||||
# 777| v777_10(void) = ExitFunction :
|
||||
# 777| v777_8(void) = ReturnIndirection[#this] : &:r777_6, ~m?
|
||||
# 777| v777_9(void) = ReturnVoid :
|
||||
# 777| v777_10(void) = AliasedUse : ~m?
|
||||
# 777| v777_11(void) = ExitFunction :
|
||||
|
||||
# 784| void MiddleVB2::MiddleVB2()
|
||||
# 784| Block 0
|
||||
@@ -4359,9 +4379,10 @@ ir.cpp:
|
||||
# 784| mu784_16(unknown) = ^CallSideEffect : ~m?
|
||||
# 784| mu784_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r784_13
|
||||
# 785| v785_1(void) = NoOp :
|
||||
# 784| v784_18(void) = ReturnVoid :
|
||||
# 784| v784_19(void) = AliasedUse : ~m?
|
||||
# 784| v784_20(void) = ExitFunction :
|
||||
# 784| v784_18(void) = ReturnIndirection[#this] : &:r784_6, ~m?
|
||||
# 784| v784_19(void) = ReturnVoid :
|
||||
# 784| v784_20(void) = AliasedUse : ~m?
|
||||
# 784| v784_21(void) = ExitFunction :
|
||||
|
||||
# 786| void MiddleVB2::~MiddleVB2()
|
||||
# 786| Block 0
|
||||
@@ -4381,9 +4402,10 @@ ir.cpp:
|
||||
# 787| r787_7(glval<unknown>) = FunctionAddress[~Base] :
|
||||
# 787| v787_8(void) = Call : func:r787_7, this:r787_6
|
||||
# 787| mu787_9(unknown) = ^CallSideEffect : ~m?
|
||||
# 786| v786_8(void) = ReturnVoid :
|
||||
# 786| v786_9(void) = AliasedUse : ~m?
|
||||
# 786| v786_10(void) = ExitFunction :
|
||||
# 786| v786_8(void) = ReturnIndirection[#this] : &:r786_6, ~m?
|
||||
# 786| v786_9(void) = ReturnVoid :
|
||||
# 786| v786_10(void) = AliasedUse : ~m?
|
||||
# 786| v786_11(void) = ExitFunction :
|
||||
|
||||
# 793| void DerivedVB::DerivedVB()
|
||||
# 793| Block 0
|
||||
@@ -4415,9 +4437,10 @@ ir.cpp:
|
||||
# 793| mu793_26(unknown) = ^CallSideEffect : ~m?
|
||||
# 793| mu793_27(String) = ^IndirectMayWriteSideEffect[-1] : &:r793_23
|
||||
# 794| v794_1(void) = NoOp :
|
||||
# 793| v793_28(void) = ReturnVoid :
|
||||
# 793| v793_29(void) = AliasedUse : ~m?
|
||||
# 793| v793_30(void) = ExitFunction :
|
||||
# 793| v793_28(void) = ReturnIndirection[#this] : &:r793_6, ~m?
|
||||
# 793| v793_29(void) = ReturnVoid :
|
||||
# 793| v793_30(void) = AliasedUse : ~m?
|
||||
# 793| v793_31(void) = ExitFunction :
|
||||
|
||||
# 795| void DerivedVB::~DerivedVB()
|
||||
# 795| Block 0
|
||||
@@ -4445,9 +4468,10 @@ ir.cpp:
|
||||
# 796| r796_15(glval<unknown>) = FunctionAddress[~Base] :
|
||||
# 796| v796_16(void) = Call : func:r796_15, this:r796_14
|
||||
# 796| mu796_17(unknown) = ^CallSideEffect : ~m?
|
||||
# 795| v795_8(void) = ReturnVoid :
|
||||
# 795| v795_9(void) = AliasedUse : ~m?
|
||||
# 795| v795_10(void) = ExitFunction :
|
||||
# 795| v795_8(void) = ReturnIndirection[#this] : &:r795_6, ~m?
|
||||
# 795| v795_9(void) = ReturnVoid :
|
||||
# 795| v795_10(void) = AliasedUse : ~m?
|
||||
# 795| v795_11(void) = ExitFunction :
|
||||
|
||||
# 799| void HierarchyConversions()
|
||||
# 799| Block 0
|
||||
@@ -4751,9 +4775,10 @@ ir.cpp:
|
||||
# 842| r842_6(glval<PolymorphicBase>) = Load : &:r842_4, ~m?
|
||||
# 842| mu842_7(PolymorphicBase) = InitializeIndirection[#this] : &:r842_6
|
||||
# 842| v842_8(void) = NoOp :
|
||||
# 842| v842_9(void) = ReturnVoid :
|
||||
# 842| v842_10(void) = AliasedUse : ~m?
|
||||
# 842| v842_11(void) = ExitFunction :
|
||||
# 842| v842_9(void) = ReturnIndirection[#this] : &:r842_6, ~m?
|
||||
# 842| v842_10(void) = ReturnVoid :
|
||||
# 842| v842_11(void) = AliasedUse : ~m?
|
||||
# 842| v842_12(void) = ExitFunction :
|
||||
|
||||
# 846| void PolymorphicDerived::PolymorphicDerived()
|
||||
# 846| Block 0
|
||||
@@ -4770,9 +4795,10 @@ ir.cpp:
|
||||
# 846| mu846_11(unknown) = ^CallSideEffect : ~m?
|
||||
# 846| mu846_12(PolymorphicBase) = ^IndirectMayWriteSideEffect[-1] : &:r846_8
|
||||
# 846| v846_13(void) = NoOp :
|
||||
# 846| v846_14(void) = ReturnVoid :
|
||||
# 846| v846_15(void) = AliasedUse : ~m?
|
||||
# 846| v846_16(void) = ExitFunction :
|
||||
# 846| v846_14(void) = ReturnIndirection[#this] : &:r846_6, ~m?
|
||||
# 846| v846_15(void) = ReturnVoid :
|
||||
# 846| v846_16(void) = AliasedUse : ~m?
|
||||
# 846| v846_17(void) = ExitFunction :
|
||||
|
||||
# 846| void PolymorphicDerived::~PolymorphicDerived()
|
||||
# 846| Block 0
|
||||
@@ -4788,9 +4814,10 @@ ir.cpp:
|
||||
# 846| r846_9(glval<unknown>) = FunctionAddress[~PolymorphicBase] :
|
||||
# 846| v846_10(void) = Call : func:r846_9, this:r846_8
|
||||
# 846| mu846_11(unknown) = ^CallSideEffect : ~m?
|
||||
# 846| v846_12(void) = ReturnVoid :
|
||||
# 846| v846_13(void) = AliasedUse : ~m?
|
||||
# 846| v846_14(void) = ExitFunction :
|
||||
# 846| v846_12(void) = ReturnIndirection[#this] : &:r846_6, ~m?
|
||||
# 846| v846_13(void) = ReturnVoid :
|
||||
# 846| v846_14(void) = AliasedUse : ~m?
|
||||
# 846| v846_15(void) = ExitFunction :
|
||||
|
||||
# 849| void DynamicCast()
|
||||
# 849| Block 0
|
||||
@@ -4870,9 +4897,10 @@ ir.cpp:
|
||||
# 868| v868_7(void) = ^BufferReadSideEffect[0] : &:r868_3, ~m?
|
||||
# 868| mu868_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r868_3
|
||||
# 869| v869_1(void) = NoOp :
|
||||
# 867| v867_8(void) = ReturnVoid :
|
||||
# 867| v867_9(void) = AliasedUse : ~m?
|
||||
# 867| v867_10(void) = ExitFunction :
|
||||
# 867| v867_8(void) = ReturnIndirection[#this] : &:r867_6, ~m?
|
||||
# 867| v867_9(void) = ReturnVoid :
|
||||
# 867| v867_10(void) = AliasedUse : ~m?
|
||||
# 867| v867_11(void) = ExitFunction :
|
||||
|
||||
# 871| void ArrayConversions()
|
||||
# 871| Block 0
|
||||
@@ -5617,9 +5645,10 @@ ir.cpp:
|
||||
# 1038| r1038_6(glval<decltype([...](...){...})>) = Load : &:r1038_4, ~m?
|
||||
# 1038| mu1038_7(decltype([...](...){...})) = InitializeIndirection[#this] : &:r1038_6
|
||||
# 1038| v1038_8(void) = NoOp :
|
||||
# 1038| v1038_9(void) = ReturnVoid :
|
||||
# 1038| v1038_10(void) = AliasedUse : ~m?
|
||||
# 1038| v1038_11(void) = ExitFunction :
|
||||
# 1038| v1038_9(void) = ReturnIndirection[#this] : &:r1038_6, ~m?
|
||||
# 1038| v1038_10(void) = ReturnVoid :
|
||||
# 1038| v1038_11(void) = AliasedUse : ~m?
|
||||
# 1038| v1038_12(void) = ExitFunction :
|
||||
|
||||
# 1038| void(* (lambda [] type at line 1038, col. 12)::operator void (*)()() const)()
|
||||
# 1038| Block 0
|
||||
@@ -5633,10 +5662,11 @@ ir.cpp:
|
||||
# 1038| r1038_8(glval<..(*)(..)>) = VariableAddress[#return] :
|
||||
# 1038| r1038_9(..(*)(..)) = FunctionAddress[_FUN] :
|
||||
# 1038| mu1038_10(..(*)(..)) = Store : &:r1038_8, r1038_9
|
||||
# 1038| r1038_11(glval<..(*)(..)>) = VariableAddress[#return] :
|
||||
# 1038| v1038_12(void) = ReturnValue : &:r1038_11, ~m?
|
||||
# 1038| v1038_13(void) = AliasedUse : ~m?
|
||||
# 1038| v1038_14(void) = ExitFunction :
|
||||
# 1038| v1038_11(void) = ReturnIndirection[#this] : &:r1038_6, ~m?
|
||||
# 1038| r1038_12(glval<..(*)(..)>) = VariableAddress[#return] :
|
||||
# 1038| v1038_13(void) = ReturnValue : &:r1038_12, ~m?
|
||||
# 1038| v1038_14(void) = AliasedUse : ~m?
|
||||
# 1038| v1038_15(void) = ExitFunction :
|
||||
|
||||
# 1040| void Lambda(int, String const&)
|
||||
# 1040| Block 0
|
||||
@@ -5819,10 +5849,11 @@ ir.cpp:
|
||||
# 1041| r1041_10(glval<char>) = VariableAddress[#return] :
|
||||
# 1041| r1041_11(char) = Constant[65] :
|
||||
# 1041| mu1041_12(char) = Store : &:r1041_10, r1041_11
|
||||
# 1041| r1041_13(glval<char>) = VariableAddress[#return] :
|
||||
# 1041| v1041_14(void) = ReturnValue : &:r1041_13, ~m?
|
||||
# 1041| v1041_15(void) = AliasedUse : ~m?
|
||||
# 1041| v1041_16(void) = ExitFunction :
|
||||
# 1041| v1041_13(void) = ReturnIndirection[#this] : &:r1041_6, ~m?
|
||||
# 1041| r1041_14(glval<char>) = VariableAddress[#return] :
|
||||
# 1041| v1041_15(void) = ReturnValue : &:r1041_14, ~m?
|
||||
# 1041| v1041_16(void) = AliasedUse : ~m?
|
||||
# 1041| v1041_17(void) = ExitFunction :
|
||||
|
||||
# 1041| char(* (void Lambda(int, String const&))::(lambda [] type at line 1041, col. 23)::operator char (*)(float)() const)(float)
|
||||
# 1041| Block 0
|
||||
@@ -5836,10 +5867,11 @@ ir.cpp:
|
||||
# 1041| r1041_8(glval<..(*)(..)>) = VariableAddress[#return] :
|
||||
# 1041| r1041_9(..(*)(..)) = FunctionAddress[_FUN] :
|
||||
# 1041| mu1041_10(..(*)(..)) = Store : &:r1041_8, r1041_9
|
||||
# 1041| r1041_11(glval<..(*)(..)>) = VariableAddress[#return] :
|
||||
# 1041| v1041_12(void) = ReturnValue : &:r1041_11, ~m?
|
||||
# 1041| v1041_13(void) = AliasedUse : ~m?
|
||||
# 1041| v1041_14(void) = ExitFunction :
|
||||
# 1041| v1041_11(void) = ReturnIndirection[#this] : &:r1041_6, ~m?
|
||||
# 1041| r1041_12(glval<..(*)(..)>) = VariableAddress[#return] :
|
||||
# 1041| v1041_13(void) = ReturnValue : &:r1041_12, ~m?
|
||||
# 1041| v1041_14(void) = AliasedUse : ~m?
|
||||
# 1041| v1041_15(void) = ExitFunction :
|
||||
|
||||
# 1043| char (void Lambda(int, String const&))::(lambda [] type at line 1043, col. 21)::operator()(float) const
|
||||
# 1043| Block 0
|
||||
@@ -5871,10 +5903,11 @@ ir.cpp:
|
||||
# 1043| r1043_26(glval<char>) = PointerAdd[1] : r1043_17, r1043_25
|
||||
# 1043| r1043_27(char) = Load : &:r1043_26, ~m?
|
||||
# 1043| mu1043_28(char) = Store : &:r1043_10, r1043_27
|
||||
# 1043| r1043_29(glval<char>) = VariableAddress[#return] :
|
||||
# 1043| v1043_30(void) = ReturnValue : &:r1043_29, ~m?
|
||||
# 1043| v1043_31(void) = AliasedUse : ~m?
|
||||
# 1043| v1043_32(void) = ExitFunction :
|
||||
# 1043| v1043_29(void) = ReturnIndirection[#this] : &:r1043_6, ~m?
|
||||
# 1043| r1043_30(glval<char>) = VariableAddress[#return] :
|
||||
# 1043| v1043_31(void) = ReturnValue : &:r1043_30, ~m?
|
||||
# 1043| v1043_32(void) = AliasedUse : ~m?
|
||||
# 1043| v1043_33(void) = ExitFunction :
|
||||
|
||||
# 1045| void (void Lambda(int, String const&))::(lambda [] type at line 1045, col. 21)::~<unnamed>()
|
||||
# 1045| Block 0
|
||||
@@ -5890,9 +5923,10 @@ ir.cpp:
|
||||
# 1045| r1045_9(glval<unknown>) = FunctionAddress[~String] :
|
||||
# 1045| v1045_10(void) = Call : func:r1045_9, this:r1045_8
|
||||
# 1045| mu1045_11(unknown) = ^CallSideEffect : ~m?
|
||||
# 1045| v1045_12(void) = ReturnVoid :
|
||||
# 1045| v1045_13(void) = AliasedUse : ~m?
|
||||
# 1045| v1045_14(void) = ExitFunction :
|
||||
# 1045| v1045_12(void) = ReturnIndirection[#this] : &:r1045_6, ~m?
|
||||
# 1045| v1045_13(void) = ReturnVoid :
|
||||
# 1045| v1045_14(void) = AliasedUse : ~m?
|
||||
# 1045| v1045_15(void) = ExitFunction :
|
||||
|
||||
# 1045| char (void Lambda(int, String const&))::(lambda [] type at line 1045, col. 21)::operator()(float) const
|
||||
# 1045| Block 0
|
||||
@@ -5921,10 +5955,11 @@ ir.cpp:
|
||||
# 1045| r1045_23(glval<char>) = PointerAdd[1] : r1045_15, r1045_22
|
||||
# 1045| r1045_24(char) = Load : &:r1045_23, ~m?
|
||||
# 1045| mu1045_25(char) = Store : &:r1045_10, r1045_24
|
||||
# 1045| r1045_26(glval<char>) = VariableAddress[#return] :
|
||||
# 1045| v1045_27(void) = ReturnValue : &:r1045_26, ~m?
|
||||
# 1045| v1045_28(void) = AliasedUse : ~m?
|
||||
# 1045| v1045_29(void) = ExitFunction :
|
||||
# 1045| v1045_26(void) = ReturnIndirection[#this] : &:r1045_6, ~m?
|
||||
# 1045| r1045_27(glval<char>) = VariableAddress[#return] :
|
||||
# 1045| v1045_28(void) = ReturnValue : &:r1045_27, ~m?
|
||||
# 1045| v1045_29(void) = AliasedUse : ~m?
|
||||
# 1045| v1045_30(void) = ExitFunction :
|
||||
|
||||
# 1047| char (void Lambda(int, String const&))::(lambda [] type at line 1047, col. 30)::operator()(float) const
|
||||
# 1047| Block 0
|
||||
@@ -5952,10 +5987,11 @@ ir.cpp:
|
||||
# 1047| r1047_22(glval<char>) = PointerAdd[1] : r1047_17, r1047_21
|
||||
# 1047| r1047_23(char) = Load : &:r1047_22, ~m?
|
||||
# 1047| mu1047_24(char) = Store : &:r1047_10, r1047_23
|
||||
# 1047| r1047_25(glval<char>) = VariableAddress[#return] :
|
||||
# 1047| v1047_26(void) = ReturnValue : &:r1047_25, ~m?
|
||||
# 1047| v1047_27(void) = AliasedUse : ~m?
|
||||
# 1047| v1047_28(void) = ExitFunction :
|
||||
# 1047| v1047_25(void) = ReturnIndirection[#this] : &:r1047_6, ~m?
|
||||
# 1047| r1047_26(glval<char>) = VariableAddress[#return] :
|
||||
# 1047| v1047_27(void) = ReturnValue : &:r1047_26, ~m?
|
||||
# 1047| v1047_28(void) = AliasedUse : ~m?
|
||||
# 1047| v1047_29(void) = ExitFunction :
|
||||
|
||||
# 1049| void (void Lambda(int, String const&))::(lambda [] type at line 1049, col. 30)::~<unnamed>()
|
||||
# 1049| Block 0
|
||||
@@ -5971,9 +6007,10 @@ ir.cpp:
|
||||
# 1049| r1049_9(glval<unknown>) = FunctionAddress[~String] :
|
||||
# 1049| v1049_10(void) = Call : func:r1049_9, this:r1049_8
|
||||
# 1049| mu1049_11(unknown) = ^CallSideEffect : ~m?
|
||||
# 1049| v1049_12(void) = ReturnVoid :
|
||||
# 1049| v1049_13(void) = AliasedUse : ~m?
|
||||
# 1049| v1049_14(void) = ExitFunction :
|
||||
# 1049| v1049_12(void) = ReturnIndirection[#this] : &:r1049_6, ~m?
|
||||
# 1049| v1049_13(void) = ReturnVoid :
|
||||
# 1049| v1049_14(void) = AliasedUse : ~m?
|
||||
# 1049| v1049_15(void) = ExitFunction :
|
||||
|
||||
# 1049| char (void Lambda(int, String const&))::(lambda [] type at line 1049, col. 30)::operator()(float) const
|
||||
# 1049| Block 0
|
||||
@@ -5999,10 +6036,11 @@ ir.cpp:
|
||||
# 1049| r1049_20(glval<char>) = PointerAdd[1] : r1049_15, r1049_19
|
||||
# 1049| r1049_21(char) = Load : &:r1049_20, ~m?
|
||||
# 1049| mu1049_22(char) = Store : &:r1049_10, r1049_21
|
||||
# 1049| r1049_23(glval<char>) = VariableAddress[#return] :
|
||||
# 1049| v1049_24(void) = ReturnValue : &:r1049_23, ~m?
|
||||
# 1049| v1049_25(void) = AliasedUse : ~m?
|
||||
# 1049| v1049_26(void) = ExitFunction :
|
||||
# 1049| v1049_23(void) = ReturnIndirection[#this] : &:r1049_6, ~m?
|
||||
# 1049| r1049_24(glval<char>) = VariableAddress[#return] :
|
||||
# 1049| v1049_25(void) = ReturnValue : &:r1049_24, ~m?
|
||||
# 1049| v1049_26(void) = AliasedUse : ~m?
|
||||
# 1049| v1049_27(void) = ExitFunction :
|
||||
|
||||
# 1051| char (void Lambda(int, String const&))::(lambda [] type at line 1051, col. 32)::operator()(float) const
|
||||
# 1051| Block 0
|
||||
@@ -6033,10 +6071,11 @@ ir.cpp:
|
||||
# 1051| r1051_25(glval<char>) = PointerAdd[1] : r1051_17, r1051_24
|
||||
# 1051| r1051_26(char) = Load : &:r1051_25, ~m?
|
||||
# 1051| mu1051_27(char) = Store : &:r1051_10, r1051_26
|
||||
# 1051| r1051_28(glval<char>) = VariableAddress[#return] :
|
||||
# 1051| v1051_29(void) = ReturnValue : &:r1051_28, ~m?
|
||||
# 1051| v1051_30(void) = AliasedUse : ~m?
|
||||
# 1051| v1051_31(void) = ExitFunction :
|
||||
# 1051| v1051_28(void) = ReturnIndirection[#this] : &:r1051_6, ~m?
|
||||
# 1051| r1051_29(glval<char>) = VariableAddress[#return] :
|
||||
# 1051| v1051_30(void) = ReturnValue : &:r1051_29, ~m?
|
||||
# 1051| v1051_31(void) = AliasedUse : ~m?
|
||||
# 1051| v1051_32(void) = ExitFunction :
|
||||
|
||||
# 1054| char (void Lambda(int, String const&))::(lambda [] type at line 1054, col. 23)::operator()(float) const
|
||||
# 1054| Block 0
|
||||
@@ -6078,10 +6117,11 @@ ir.cpp:
|
||||
# 1054| r1054_36(glval<char>) = PointerAdd[1] : r1054_17, r1054_35
|
||||
# 1054| r1054_37(char) = Load : &:r1054_36, ~m?
|
||||
# 1054| mu1054_38(char) = Store : &:r1054_10, r1054_37
|
||||
# 1054| r1054_39(glval<char>) = VariableAddress[#return] :
|
||||
# 1054| v1054_40(void) = ReturnValue : &:r1054_39, ~m?
|
||||
# 1054| v1054_41(void) = AliasedUse : ~m?
|
||||
# 1054| v1054_42(void) = ExitFunction :
|
||||
# 1054| v1054_39(void) = ReturnIndirection[#this] : &:r1054_6, ~m?
|
||||
# 1054| r1054_40(glval<char>) = VariableAddress[#return] :
|
||||
# 1054| v1054_41(void) = ReturnValue : &:r1054_40, ~m?
|
||||
# 1054| v1054_42(void) = AliasedUse : ~m?
|
||||
# 1054| v1054_43(void) = ExitFunction :
|
||||
|
||||
# 1077| void RangeBasedFor(vector<int> const&)
|
||||
# 1077| Block 0
|
||||
@@ -7409,9 +7449,10 @@ perf-regression.cpp:
|
||||
# 6| r6_11(unknown[1073741824]) = Constant[0] :
|
||||
# 6| mu6_12(unknown[1073741824]) = Store : &:r6_10, r6_11
|
||||
# 6| v6_13(void) = NoOp :
|
||||
# 6| v6_14(void) = ReturnVoid :
|
||||
# 6| v6_15(void) = AliasedUse : ~m?
|
||||
# 6| v6_16(void) = ExitFunction :
|
||||
# 6| v6_14(void) = ReturnIndirection[#this] : &:r6_6, ~m?
|
||||
# 6| v6_15(void) = ReturnVoid :
|
||||
# 6| v6_16(void) = AliasedUse : ~m?
|
||||
# 6| v6_17(void) = ExitFunction :
|
||||
|
||||
# 9| int main()
|
||||
# 9| Block 0
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
missingOperand
|
||||
| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
|
||||
| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
|
||||
| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
|
||||
| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
|
||||
| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() |
|
||||
| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() |
|
||||
| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() |
|
||||
| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() |
|
||||
unexpectedOperand
|
||||
duplicateOperand
|
||||
missingPhiOperand
|
||||
@@ -24,6 +24,7 @@ switchInstructionWithoutDefaultEdge
|
||||
notMarkedAsConflated
|
||||
wronglyMarkedAsConflated
|
||||
invalidOverlap
|
||||
nonUniqueEnclosingIRFunction
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
missingOperand
|
||||
| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
|
||||
| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
|
||||
| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
|
||||
| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
|
||||
| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() |
|
||||
| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() |
|
||||
| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() |
|
||||
| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | void HierarchyConversions() | void HierarchyConversions() |
|
||||
unexpectedOperand
|
||||
duplicateOperand
|
||||
missingPhiOperand
|
||||
@@ -24,6 +24,7 @@ switchInstructionWithoutDefaultEdge
|
||||
notMarkedAsConflated
|
||||
wronglyMarkedAsConflated
|
||||
invalidOverlap
|
||||
nonUniqueEnclosingIRFunction
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
|
||||
@@ -20,6 +20,7 @@ switchInstructionWithoutDefaultEdge
|
||||
notMarkedAsConflated
|
||||
wronglyMarkedAsConflated
|
||||
invalidOverlap
|
||||
nonUniqueEnclosingIRFunction
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
|
||||
@@ -20,6 +20,7 @@ switchInstructionWithoutDefaultEdge
|
||||
notMarkedAsConflated
|
||||
wronglyMarkedAsConflated
|
||||
invalidOverlap
|
||||
nonUniqueEnclosingIRFunction
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
|
||||
@@ -1020,9 +1020,10 @@ ssa.cpp:
|
||||
# 235| r235_9(glval<int>) = VariableAddress[x] :
|
||||
# 235| m235_10(int) = InitializeParameter[x] : &:r235_9
|
||||
# 235| v235_11(void) = NoOp :
|
||||
# 235| v235_12(void) = ReturnVoid :
|
||||
# 235| v235_13(void) = AliasedUse : m235_3
|
||||
# 235| v235_14(void) = ExitFunction :
|
||||
# 235| v235_12(void) = ReturnIndirection[#this] : &:r235_7, m235_8
|
||||
# 235| v235_13(void) = ReturnVoid :
|
||||
# 235| v235_14(void) = AliasedUse : m235_3
|
||||
# 235| v235_15(void) = ExitFunction :
|
||||
|
||||
# 236| void Constructible::g()
|
||||
# 236| Block 0
|
||||
@@ -1035,9 +1036,10 @@ ssa.cpp:
|
||||
# 236| r236_7(glval<Constructible>) = Load : &:r236_5, m236_6
|
||||
# 236| m236_8(Constructible) = InitializeIndirection[#this] : &:r236_7
|
||||
# 236| v236_9(void) = NoOp :
|
||||
# 236| v236_10(void) = ReturnVoid :
|
||||
# 236| v236_11(void) = AliasedUse : m236_3
|
||||
# 236| v236_12(void) = ExitFunction :
|
||||
# 236| v236_10(void) = ReturnIndirection[#this] : &:r236_7, m236_8
|
||||
# 236| v236_11(void) = ReturnVoid :
|
||||
# 236| v236_12(void) = AliasedUse : m236_3
|
||||
# 236| v236_13(void) = ExitFunction :
|
||||
|
||||
# 239| void ExplicitConstructorCalls()
|
||||
# 239| Block 0
|
||||
@@ -1307,9 +1309,10 @@ ssa.cpp:
|
||||
# 286| r286_9(glval<int>) = VariableAddress[x] :
|
||||
# 286| m286_10(int) = InitializeParameter[x] : &:r286_9
|
||||
# 286| v286_11(void) = NoOp :
|
||||
# 286| v286_12(void) = ReturnVoid :
|
||||
# 286| v286_13(void) = AliasedUse : m286_3
|
||||
# 286| v286_14(void) = ExitFunction :
|
||||
# 286| v286_12(void) = ReturnIndirection[#this] : &:r286_7, m286_8
|
||||
# 286| v286_13(void) = ReturnVoid :
|
||||
# 286| v286_14(void) = AliasedUse : m286_3
|
||||
# 286| v286_15(void) = ExitFunction :
|
||||
|
||||
# 287| void A::A(A*)
|
||||
# 287| Block 0
|
||||
@@ -1326,10 +1329,11 @@ ssa.cpp:
|
||||
# 287| r287_11(A *) = Load : &:r287_9, m287_10
|
||||
# 287| m287_12(unknown) = InitializeIndirection[p#0] : &:r287_11
|
||||
# 287| v287_13(void) = NoOp :
|
||||
# 287| v287_14(void) = ReturnIndirection[p#0] : &:r287_11, m287_12
|
||||
# 287| v287_15(void) = ReturnVoid :
|
||||
# 287| v287_16(void) = AliasedUse : m287_3
|
||||
# 287| v287_17(void) = ExitFunction :
|
||||
# 287| v287_14(void) = ReturnIndirection[#this] : &:r287_7, m287_8
|
||||
# 287| v287_15(void) = ReturnIndirection[p#0] : &:r287_11, m287_12
|
||||
# 287| v287_16(void) = ReturnVoid :
|
||||
# 287| v287_17(void) = AliasedUse : m287_3
|
||||
# 287| v287_18(void) = ExitFunction :
|
||||
|
||||
# 288| void A::A()
|
||||
# 288| Block 0
|
||||
@@ -1342,9 +1346,10 @@ ssa.cpp:
|
||||
# 288| r288_7(glval<A>) = Load : &:r288_5, m288_6
|
||||
# 288| m288_8(A) = InitializeIndirection[#this] : &:r288_7
|
||||
# 288| v288_9(void) = NoOp :
|
||||
# 288| v288_10(void) = ReturnVoid :
|
||||
# 288| v288_11(void) = AliasedUse : m288_3
|
||||
# 288| v288_12(void) = ExitFunction :
|
||||
# 288| v288_10(void) = ReturnIndirection[#this] : &:r288_7, m288_8
|
||||
# 288| v288_11(void) = ReturnVoid :
|
||||
# 288| v288_12(void) = AliasedUse : m288_3
|
||||
# 288| v288_13(void) = ExitFunction :
|
||||
|
||||
# 291| Point* NewAliasing(int)
|
||||
# 291| Block 0
|
||||
@@ -1499,6 +1504,7 @@ ssa.cpp:
|
||||
# 311| m311_6(int) = Store : &:r311_5, r311_2
|
||||
# 311| m311_7(unknown) = Chi : total:m310_8, partial:m311_6
|
||||
# 312| v312_1(void) = NoOp :
|
||||
# 310| v310_11(void) = ReturnVoid :
|
||||
# 310| v310_12(void) = AliasedUse : m310_3
|
||||
# 310| v310_13(void) = ExitFunction :
|
||||
# 310| v310_11(void) = ReturnIndirection[#this] : &:r310_7, m311_7
|
||||
# 310| v310_12(void) = ReturnVoid :
|
||||
# 310| v310_13(void) = AliasedUse : m310_3
|
||||
# 310| v310_14(void) = ExitFunction :
|
||||
|
||||
@@ -1013,9 +1013,10 @@ ssa.cpp:
|
||||
# 235| r235_9(glval<int>) = VariableAddress[x] :
|
||||
# 235| m235_10(int) = InitializeParameter[x] : &:r235_9
|
||||
# 235| v235_11(void) = NoOp :
|
||||
# 235| v235_12(void) = ReturnVoid :
|
||||
# 235| v235_13(void) = AliasedUse : m235_3
|
||||
# 235| v235_14(void) = ExitFunction :
|
||||
# 235| v235_12(void) = ReturnIndirection[#this] : &:r235_7, m235_8
|
||||
# 235| v235_13(void) = ReturnVoid :
|
||||
# 235| v235_14(void) = AliasedUse : m235_3
|
||||
# 235| v235_15(void) = ExitFunction :
|
||||
|
||||
# 236| void Constructible::g()
|
||||
# 236| Block 0
|
||||
@@ -1028,9 +1029,10 @@ ssa.cpp:
|
||||
# 236| r236_7(glval<Constructible>) = Load : &:r236_5, m236_6
|
||||
# 236| m236_8(Constructible) = InitializeIndirection[#this] : &:r236_7
|
||||
# 236| v236_9(void) = NoOp :
|
||||
# 236| v236_10(void) = ReturnVoid :
|
||||
# 236| v236_11(void) = AliasedUse : m236_3
|
||||
# 236| v236_12(void) = ExitFunction :
|
||||
# 236| v236_10(void) = ReturnIndirection[#this] : &:r236_7, m236_8
|
||||
# 236| v236_11(void) = ReturnVoid :
|
||||
# 236| v236_12(void) = AliasedUse : m236_3
|
||||
# 236| v236_13(void) = ExitFunction :
|
||||
|
||||
# 239| void ExplicitConstructorCalls()
|
||||
# 239| Block 0
|
||||
@@ -1295,9 +1297,10 @@ ssa.cpp:
|
||||
# 286| r286_9(glval<int>) = VariableAddress[x] :
|
||||
# 286| m286_10(int) = InitializeParameter[x] : &:r286_9
|
||||
# 286| v286_11(void) = NoOp :
|
||||
# 286| v286_12(void) = ReturnVoid :
|
||||
# 286| v286_13(void) = AliasedUse : m286_3
|
||||
# 286| v286_14(void) = ExitFunction :
|
||||
# 286| v286_12(void) = ReturnIndirection[#this] : &:r286_7, m286_8
|
||||
# 286| v286_13(void) = ReturnVoid :
|
||||
# 286| v286_14(void) = AliasedUse : m286_3
|
||||
# 286| v286_15(void) = ExitFunction :
|
||||
|
||||
# 287| void A::A(A*)
|
||||
# 287| Block 0
|
||||
@@ -1314,10 +1317,11 @@ ssa.cpp:
|
||||
# 287| r287_11(A *) = Load : &:r287_9, m287_10
|
||||
# 287| m287_12(unknown) = InitializeIndirection[p#0] : &:r287_11
|
||||
# 287| v287_13(void) = NoOp :
|
||||
# 287| v287_14(void) = ReturnIndirection[p#0] : &:r287_11, m287_12
|
||||
# 287| v287_15(void) = ReturnVoid :
|
||||
# 287| v287_16(void) = AliasedUse : m287_3
|
||||
# 287| v287_17(void) = ExitFunction :
|
||||
# 287| v287_14(void) = ReturnIndirection[#this] : &:r287_7, m287_8
|
||||
# 287| v287_15(void) = ReturnIndirection[p#0] : &:r287_11, m287_12
|
||||
# 287| v287_16(void) = ReturnVoid :
|
||||
# 287| v287_17(void) = AliasedUse : m287_3
|
||||
# 287| v287_18(void) = ExitFunction :
|
||||
|
||||
# 288| void A::A()
|
||||
# 288| Block 0
|
||||
@@ -1330,9 +1334,10 @@ ssa.cpp:
|
||||
# 288| r288_7(glval<A>) = Load : &:r288_5, m288_6
|
||||
# 288| m288_8(A) = InitializeIndirection[#this] : &:r288_7
|
||||
# 288| v288_9(void) = NoOp :
|
||||
# 288| v288_10(void) = ReturnVoid :
|
||||
# 288| v288_11(void) = AliasedUse : m288_3
|
||||
# 288| v288_12(void) = ExitFunction :
|
||||
# 288| v288_10(void) = ReturnIndirection[#this] : &:r288_7, m288_8
|
||||
# 288| v288_11(void) = ReturnVoid :
|
||||
# 288| v288_12(void) = AliasedUse : m288_3
|
||||
# 288| v288_13(void) = ExitFunction :
|
||||
|
||||
# 291| Point* NewAliasing(int)
|
||||
# 291| Block 0
|
||||
@@ -1486,6 +1491,7 @@ ssa.cpp:
|
||||
# 311| m311_6(int) = Store : &:r311_5, r311_2
|
||||
# 311| m311_7(unknown) = Chi : total:m310_8, partial:m311_6
|
||||
# 312| v312_1(void) = NoOp :
|
||||
# 310| v310_11(void) = ReturnVoid :
|
||||
# 310| v310_12(void) = AliasedUse : m310_3
|
||||
# 310| v310_13(void) = ExitFunction :
|
||||
# 310| v310_11(void) = ReturnIndirection[#this] : &:r310_7, m311_7
|
||||
# 310| v310_12(void) = ReturnVoid :
|
||||
# 310| v310_13(void) = AliasedUse : m310_3
|
||||
# 310| v310_14(void) = ExitFunction :
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user