mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge branch 'master' into js-team-sprint-merge2
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
|
||||
@@ -53,14 +53,6 @@ After the experimental query is merged, we welcome pull requests to improve it.
|
||||
|
||||
## Using your personal data
|
||||
|
||||
If you contribute to this project, we will record your name and email
|
||||
address (as provided by you with your contributions) as part of the code
|
||||
repositories, which are public. We might also use this information
|
||||
to contact you in relation to your contributions, as well as in the
|
||||
normal course of software development. We also store records of your
|
||||
CLA agreements. Under GDPR legislation, we do this
|
||||
on the basis of our legitimate interest in creating the CodeQL product.
|
||||
|
||||
Please do get in touch (privacy@github.com) if you have any questions about
|
||||
this or our data protection policies.
|
||||
If you contribute to this project, we will record your name and email address (as provided by you with your contributions) as part of the code repositories, which are public. We might also use this information to contact you in relation to your contributions, as well as in the normal course of software development. We also store records of CLA agreements signed in the past, but no longer require contributors to sign a CLA. Under GDPR legislation, we do this on the basis of our legitimate interest in creating the CodeQL product.
|
||||
|
||||
Please do get in touch (privacy@github.com) if you have any questions about this or our data protection policies.
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
- [ssh2-streams](https://www.npmjs.com/package/ssh2-streams)
|
||||
- [ssh2](https://www.npmjs.com/package/ssh2)
|
||||
- [yargs](https://www.npmjs.com/package/yargs)
|
||||
- [webpack-dev-server](https://www.npmjs.com/package/webpack-dev-server)
|
||||
|
||||
* TypeScript 3.9 is now supported.
|
||||
|
||||
@@ -55,6 +56,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. |
|
||||
|
||||
@@ -53,114 +53,122 @@
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll"
|
||||
],
|
||||
"IR IRBlock": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRBlock.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRBlock.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll"
|
||||
],
|
||||
"IR IRVariable": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll"
|
||||
],
|
||||
"IR IRFunction": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRFunction.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRFunction.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRFunction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRFunction.qll"
|
||||
],
|
||||
"IR Operand": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Operand.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Operand.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/Operand.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll"
|
||||
],
|
||||
"IR IRType": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/IRType.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/IRType.qll"
|
||||
],
|
||||
"IR IRConfiguration": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/IRConfiguration.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/IRConfiguration.qll"
|
||||
],
|
||||
"IR UseSoundEscapeAnalysis": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/UseSoundEscapeAnalysis.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/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/semmle/code/csharp/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/semmle/code/csharp/ir/implementation/internal/TIRVariable.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/TIRVariable.qll"
|
||||
],
|
||||
"IR IR": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IR.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IR.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IR.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll"
|
||||
],
|
||||
"IR IRConsistency": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRConsistency.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRConsistency.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRConsistency.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRConsistency.qll"
|
||||
],
|
||||
"IR PrintIR": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/PrintIR.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/PrintIR.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll"
|
||||
],
|
||||
"IR IntegerConstant": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/internal/IntegerConstant.qll"
|
||||
"csharp/ql/src/experimental/ir/internal/IntegerConstant.qll"
|
||||
],
|
||||
"IR IntegerInteval": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerInterval.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/internal/IntegerInterval.qll"
|
||||
"csharp/ql/src/experimental/ir/internal/IntegerInterval.qll"
|
||||
],
|
||||
"IR IntegerPartial": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerPartial.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/internal/IntegerPartial.qll"
|
||||
"csharp/ql/src/experimental/ir/internal/IntegerPartial.qll"
|
||||
],
|
||||
"IR Overlap": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/internal/Overlap.qll"
|
||||
"csharp/ql/src/experimental/ir/internal/Overlap.qll"
|
||||
],
|
||||
"IR EdgeKind": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/EdgeKind.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/EdgeKind.qll"
|
||||
],
|
||||
"IR MemoryAccessKind": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/MemoryAccessKind.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/MemoryAccessKind.qll"
|
||||
],
|
||||
"IR TempVariableTag": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/TempVariableTag.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/TempVariableTag.qll"
|
||||
],
|
||||
"IR Opcode": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/Opcode.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/Opcode.qll"
|
||||
],
|
||||
"IR SSAConsistency": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll"
|
||||
],
|
||||
"C++ IR InstructionImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll",
|
||||
@@ -177,6 +185,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",
|
||||
@@ -199,7 +212,7 @@
|
||||
"SSA AliasAnalysis": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll"
|
||||
],
|
||||
"C++ SSA AliasAnalysisImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll",
|
||||
@@ -212,42 +225,42 @@
|
||||
],
|
||||
"IR SSA SimpleSSA": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll"
|
||||
],
|
||||
"IR AliasConfiguration (unaliased_ssa)": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll"
|
||||
],
|
||||
"IR SSA SSAConstruction": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll"
|
||||
],
|
||||
"IR SSA PrintSSA": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintSSA.qll"
|
||||
],
|
||||
"IR ValueNumberInternal": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll"
|
||||
],
|
||||
"C++ IR ValueNumber": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll"
|
||||
],
|
||||
"C++ IR PrintValueNumbering": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/PrintValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/PrintValueNumbering.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/PrintValueNumbering.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/PrintValueNumbering.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll"
|
||||
],
|
||||
"C++ IR ConstantAnalysis": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll",
|
||||
@@ -276,32 +289,36 @@
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
|
||||
],
|
||||
"C# IR InstructionImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/InstructionImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/InstructionImports.qll"
|
||||
],
|
||||
"C# IR IRImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRImports.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRImports.qll"
|
||||
],
|
||||
"C# IR IRBlockImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRBlockImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll"
|
||||
"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/semmle/code/csharp/ir/implementation/raw/internal/IRVariableImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRVariableImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll"
|
||||
],
|
||||
"C# IR OperandImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/OperandImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/OperandImports.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/OperandImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/OperandImports.qll"
|
||||
],
|
||||
"C# IR PrintIRImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/PrintIRImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/PrintIRImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll"
|
||||
],
|
||||
"C# IR ValueNumberingImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
||||
],
|
||||
"XML": [
|
||||
"cpp/ql/src/semmle/code/cpp/XML.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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2098,14 +2122,31 @@ class PathNode extends TPathNode {
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
private predicate isHidden() {
|
||||
nodeIsHidden(this.getNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
}
|
||||
|
||||
private PathNode getASuccessorIfHidden() {
|
||||
this.isHidden() and
|
||||
result = this.(PathNodeImpl).getASuccessorImpl()
|
||||
}
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
PathNode getASuccessor() { none() }
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNode getASuccessorImpl();
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2180,7 +2221,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNodeImpl getASuccessor() {
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -2217,7 +2258,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getASuccessor() { none() }
|
||||
override PathNode getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { config.isSource(node) }
|
||||
}
|
||||
@@ -2253,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()
|
||||
@@ -2267,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()
|
||||
}
|
||||
|
||||
@@ -2521,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 .. accessPathLimit()] }
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -2533,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
|
||||
@@ -2544,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();
|
||||
@@ -2562,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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2746,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)
|
||||
@@ -2769,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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2098,14 +2122,31 @@ class PathNode extends TPathNode {
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
private predicate isHidden() {
|
||||
nodeIsHidden(this.getNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
}
|
||||
|
||||
private PathNode getASuccessorIfHidden() {
|
||||
this.isHidden() and
|
||||
result = this.(PathNodeImpl).getASuccessorImpl()
|
||||
}
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
PathNode getASuccessor() { none() }
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNode getASuccessorImpl();
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2180,7 +2221,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNodeImpl getASuccessor() {
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -2217,7 +2258,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getASuccessor() { none() }
|
||||
override PathNode getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { config.isSource(node) }
|
||||
}
|
||||
@@ -2253,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()
|
||||
@@ -2267,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()
|
||||
}
|
||||
|
||||
@@ -2521,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 .. accessPathLimit()] }
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -2533,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
|
||||
@@ -2544,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();
|
||||
@@ -2562,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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2746,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)
|
||||
@@ -2769,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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2098,14 +2122,31 @@ class PathNode extends TPathNode {
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
private predicate isHidden() {
|
||||
nodeIsHidden(this.getNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
}
|
||||
|
||||
private PathNode getASuccessorIfHidden() {
|
||||
this.isHidden() and
|
||||
result = this.(PathNodeImpl).getASuccessorImpl()
|
||||
}
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
PathNode getASuccessor() { none() }
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNode getASuccessorImpl();
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2180,7 +2221,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNodeImpl getASuccessor() {
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -2217,7 +2258,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getASuccessor() { none() }
|
||||
override PathNode getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { config.isSource(node) }
|
||||
}
|
||||
@@ -2253,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()
|
||||
@@ -2267,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()
|
||||
}
|
||||
|
||||
@@ -2521,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 .. accessPathLimit()] }
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -2533,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
|
||||
@@ -2544,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();
|
||||
@@ -2562,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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2746,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)
|
||||
@@ -2769,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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2098,14 +2122,31 @@ class PathNode extends TPathNode {
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
private predicate isHidden() {
|
||||
nodeIsHidden(this.getNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
}
|
||||
|
||||
private PathNode getASuccessorIfHidden() {
|
||||
this.isHidden() and
|
||||
result = this.(PathNodeImpl).getASuccessorImpl()
|
||||
}
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
PathNode getASuccessor() { none() }
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNode getASuccessorImpl();
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2180,7 +2221,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNodeImpl getASuccessor() {
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -2217,7 +2258,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getASuccessor() { none() }
|
||||
override PathNode getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { config.isSource(node) }
|
||||
}
|
||||
@@ -2253,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()
|
||||
@@ -2267,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()
|
||||
}
|
||||
|
||||
@@ -2521,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 .. accessPathLimit()] }
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -2533,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
|
||||
@@ -2544,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();
|
||||
@@ -2562,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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2746,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)
|
||||
@@ -2769,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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2098,14 +2122,31 @@ class PathNode extends TPathNode {
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
private predicate isHidden() {
|
||||
nodeIsHidden(this.getNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
}
|
||||
|
||||
private PathNode getASuccessorIfHidden() {
|
||||
this.isHidden() and
|
||||
result = this.(PathNodeImpl).getASuccessorImpl()
|
||||
}
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
PathNode getASuccessor() { none() }
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNode getASuccessorImpl();
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2180,7 +2221,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNodeImpl getASuccessor() {
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -2217,7 +2258,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getASuccessor() { none() }
|
||||
override PathNode getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { config.isSource(node) }
|
||||
}
|
||||
@@ -2253,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()
|
||||
@@ -2267,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()
|
||||
}
|
||||
|
||||
@@ -2521,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 .. accessPathLimit()] }
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -2533,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
|
||||
@@ -2544,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();
|
||||
@@ -2562,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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2746,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)
|
||||
@@ -2769,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() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -314,3 +296,6 @@ predicate isImmutableOrUnobservable(Node n) {
|
||||
// The above list of cases isn't exhaustive, but it narrows down the
|
||||
// consistency alerts enough that most of them are interesting.
|
||||
}
|
||||
|
||||
/** Holds if `n` should be hidden from path explanations. */
|
||||
predicate nodeIsHidden(Node n) { none() }
|
||||
|
||||
@@ -82,47 +82,45 @@ private module VirtualDispatch {
|
||||
exists(LoadInstruction load, GlobalOrNamespaceVariable var |
|
||||
var = src.asVariable() and
|
||||
other.asInstruction() = load and
|
||||
addressOfGlobal(load.getSourceAddress(), var) and
|
||||
// The `allowFromArg` concept doesn't play a role when `src` is a
|
||||
// global variable, so we just set it to a single arbitrary value for
|
||||
// performance.
|
||||
allowFromArg = true
|
||||
|
|
||||
// Load directly from the global variable
|
||||
load.getSourceAddress().(VariableAddressInstruction).getASTVariable() = var
|
||||
or
|
||||
// Load from a field on a global union
|
||||
exists(FieldAddressInstruction fa |
|
||||
fa = load.getSourceAddress() and
|
||||
fa.getObjectAddress().(VariableAddressInstruction).getASTVariable() = var and
|
||||
fa.getField().getDeclaringType() instanceof Union
|
||||
)
|
||||
)
|
||||
or
|
||||
// Flow from store to global variable. These cases are similar to the
|
||||
// above but have `StoreInstruction` instead of `LoadInstruction` and
|
||||
// have the roles swapped between `other` and `src`.
|
||||
// Flow from store to global variable.
|
||||
exists(StoreInstruction store, GlobalOrNamespaceVariable var |
|
||||
var = other.asVariable() and
|
||||
store = src.asInstruction() and
|
||||
storeIntoGlobal(store, var) and
|
||||
// Setting `allowFromArg` to `true` like in the base case means we
|
||||
// treat a store to a global variable like the dispatch itself: flow
|
||||
// may come from anywhere.
|
||||
allowFromArg = true
|
||||
|
|
||||
// Store directly to the global variable
|
||||
store.getDestinationAddress().(VariableAddressInstruction).getASTVariable() = var
|
||||
or
|
||||
// Store to a field on a global union
|
||||
exists(FieldAddressInstruction fa |
|
||||
fa = store.getDestinationAddress() and
|
||||
fa.getObjectAddress().(VariableAddressInstruction).getASTVariable() = var and
|
||||
fa.getField().getDeclaringType() instanceof Union
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate storeIntoGlobal(StoreInstruction store, GlobalOrNamespaceVariable var) {
|
||||
addressOfGlobal(store.getDestinationAddress(), var)
|
||||
}
|
||||
|
||||
/** Holds if `addressInstr` is an instruction that produces the address of `var`. */
|
||||
private predicate addressOfGlobal(Instruction addressInstr, GlobalOrNamespaceVariable var) {
|
||||
// Access directly to the global variable
|
||||
addressInstr.(VariableAddressInstruction).getASTVariable() = var
|
||||
or
|
||||
// Access to a field on a global union
|
||||
exists(FieldAddressInstruction fa |
|
||||
fa = addressInstr and
|
||||
fa.getObjectAddress().(VariableAddressInstruction).getASTVariable() = var and
|
||||
fa.getField().getDeclaringType() instanceof Union
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A ReturnNode with its ReturnKind and its enclosing callable.
|
||||
*
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2098,14 +2122,31 @@ class PathNode extends TPathNode {
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
private predicate isHidden() {
|
||||
nodeIsHidden(this.getNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
}
|
||||
|
||||
private PathNode getASuccessorIfHidden() {
|
||||
this.isHidden() and
|
||||
result = this.(PathNodeImpl).getASuccessorImpl()
|
||||
}
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
PathNode getASuccessor() { none() }
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNode getASuccessorImpl();
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2180,7 +2221,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNodeImpl getASuccessor() {
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -2217,7 +2258,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getASuccessor() { none() }
|
||||
override PathNode getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { config.isSource(node) }
|
||||
}
|
||||
@@ -2253,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()
|
||||
@@ -2267,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()
|
||||
}
|
||||
|
||||
@@ -2521,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 .. accessPathLimit()] }
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -2533,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
|
||||
@@ -2544,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();
|
||||
@@ -2562,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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2746,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)
|
||||
@@ -2769,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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2098,14 +2122,31 @@ class PathNode extends TPathNode {
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
private predicate isHidden() {
|
||||
nodeIsHidden(this.getNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
}
|
||||
|
||||
private PathNode getASuccessorIfHidden() {
|
||||
this.isHidden() and
|
||||
result = this.(PathNodeImpl).getASuccessorImpl()
|
||||
}
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
PathNode getASuccessor() { none() }
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNode getASuccessorImpl();
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2180,7 +2221,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNodeImpl getASuccessor() {
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -2217,7 +2258,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getASuccessor() { none() }
|
||||
override PathNode getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { config.isSource(node) }
|
||||
}
|
||||
@@ -2253,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()
|
||||
@@ -2267,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()
|
||||
}
|
||||
|
||||
@@ -2521,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 .. accessPathLimit()] }
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -2533,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
|
||||
@@ -2544,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();
|
||||
@@ -2562,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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2746,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)
|
||||
@@ -2769,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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2098,14 +2122,31 @@ class PathNode extends TPathNode {
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
private predicate isHidden() {
|
||||
nodeIsHidden(this.getNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
}
|
||||
|
||||
private PathNode getASuccessorIfHidden() {
|
||||
this.isHidden() and
|
||||
result = this.(PathNodeImpl).getASuccessorImpl()
|
||||
}
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
PathNode getASuccessor() { none() }
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNode getASuccessorImpl();
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2180,7 +2221,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNodeImpl getASuccessor() {
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -2217,7 +2258,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getASuccessor() { none() }
|
||||
override PathNode getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { config.isSource(node) }
|
||||
}
|
||||
@@ -2253,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()
|
||||
@@ -2267,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()
|
||||
}
|
||||
|
||||
@@ -2521,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 .. accessPathLimit()] }
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -2533,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
|
||||
@@ -2544,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();
|
||||
@@ -2562,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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2746,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)
|
||||
@@ -2769,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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2098,14 +2122,31 @@ class PathNode extends TPathNode {
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
private predicate isHidden() {
|
||||
nodeIsHidden(this.getNode()) and
|
||||
not this.isSource() and
|
||||
not this instanceof PathNodeSink
|
||||
}
|
||||
|
||||
private PathNode getASuccessorIfHidden() {
|
||||
this.isHidden() and
|
||||
result = this.(PathNodeImpl).getASuccessorImpl()
|
||||
}
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
PathNode getASuccessor() { none() }
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNode getASuccessorImpl();
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2180,7 +2221,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
result.getConfiguration() = unbind(this.getConfiguration())
|
||||
}
|
||||
|
||||
override PathNodeImpl getASuccessor() {
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
or
|
||||
@@ -2217,7 +2258,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getASuccessor() { none() }
|
||||
override PathNode getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { config.isSource(node) }
|
||||
}
|
||||
@@ -2253,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()
|
||||
@@ -2267,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()
|
||||
}
|
||||
|
||||
@@ -2521,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 .. accessPathLimit()] }
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -2533,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
|
||||
@@ -2544,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();
|
||||
@@ -2562,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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2746,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)
|
||||
@@ -2769,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,44 +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() }
|
||||
}
|
||||
|
||||
private predicate storeStepNoChi(Node node1, Content f, PostUpdateNode node2) {
|
||||
exists(FieldAddressInstruction fa, StoreInstruction store |
|
||||
store = node2.asInstruction() and
|
||||
store.getDestinationAddress() = fa and
|
||||
store.getSourceValue() = node1.asInstruction() and
|
||||
f.(FieldContent).getField() = fa.getField()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate storeStepChi(Node node1, Content f, PostUpdateNode node2) {
|
||||
exists(FieldAddressInstruction fa, StoreInstruction store |
|
||||
node1.asInstruction() = store and
|
||||
store.getDestinationAddress() = fa and
|
||||
node2.asInstruction().(ChiInstruction).getPartial() = store and
|
||||
f.(FieldContent).getField() = fa.getField()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,9 +189,9 @@ private predicate storeStepChi(Node node1, Content f, PostUpdateNode node2) {
|
||||
* Thus, `node2` references an object with a field `f` that contains the
|
||||
* value of `node1`.
|
||||
*/
|
||||
predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
|
||||
storeStepNoChi(node1, f, node2) or
|
||||
storeStepChi(node1, f, node2)
|
||||
predicate storeStep(Node node1, Content f, StoreStepNode node2) {
|
||||
node2.getStoredValue() = node1 and
|
||||
f.(FieldContent).getField() = node2.getAField()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -225,13 +199,9 @@ predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
|
||||
* Thus, `node1` references an object with a field `f` whose value ends up in
|
||||
* `node2`.
|
||||
*/
|
||||
predicate readStep(Node node1, Content f, Node node2) {
|
||||
exists(FieldAddressInstruction fa, LoadInstruction load |
|
||||
load.getSourceAddress() = fa and
|
||||
node1.asInstruction() = load.getSourceValueOperand().getAnyDef() and
|
||||
fa.getField() = f.(FieldContent).getField() and
|
||||
load = node2.asInstruction()
|
||||
)
|
||||
predicate readStep(Node node1, Content f, ReadStepNode node2) {
|
||||
node2.getReadValue() = node1 and
|
||||
f.(FieldContent).getField() = node2.getAField()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -306,3 +276,6 @@ predicate isImmutableOrUnobservable(Node n) {
|
||||
// complex to model here.
|
||||
any()
|
||||
}
|
||||
|
||||
/** Holds if `n` should be hidden from path explanations. */
|
||||
predicate nodeIsHidden(Node n) { none() }
|
||||
|
||||
@@ -13,7 +13,9 @@ private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
|
||||
private newtype TIRDataFlowNode =
|
||||
TInstructionNode(Instruction i) or
|
||||
TVariableNode(Variable var)
|
||||
TVariableNode(Variable var) or
|
||||
TStoreNode(StoreChain chain) or
|
||||
TLoadNode(LoadChain load)
|
||||
|
||||
/**
|
||||
* A node in a data flow graph.
|
||||
@@ -271,7 +273,7 @@ deprecated class UninitializedNode extends Node {
|
||||
* This class exists to match the interface used by Java. There are currently no non-abstract
|
||||
* classes that extend it. When we implement field flow, we can revisit this.
|
||||
*/
|
||||
abstract class PostUpdateNode extends InstructionNode {
|
||||
abstract class PostUpdateNode extends Node {
|
||||
/**
|
||||
* Gets the node before the state update.
|
||||
*/
|
||||
@@ -286,59 +288,15 @@ abstract class PostUpdateNode extends InstructionNode {
|
||||
* value, but does not necessarily replace it entirely. For example:
|
||||
* ```
|
||||
* x.y = 1; // a partial definition of the object `x`.
|
||||
* x.y.z = 1; // a partial definition of the object `x.y`.
|
||||
* x.y.z = 1; // a partial definition of the objects `x.y` and `x`.
|
||||
* x.setY(1); // a partial definition of the object `x`.
|
||||
* setY(&x); // a partial definition of the object `x`.
|
||||
* ```
|
||||
*/
|
||||
abstract private class PartialDefinitionNode extends PostUpdateNode, TInstructionNode {
|
||||
abstract private class PartialDefinitionNode extends PostUpdateNode {
|
||||
abstract Expr getDefinedExpr();
|
||||
}
|
||||
|
||||
private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode {
|
||||
override ChiInstruction instr;
|
||||
FieldAddressInstruction field;
|
||||
|
||||
ExplicitFieldStoreQualifierNode() {
|
||||
not instr.isResultConflated() and
|
||||
exists(StoreInstruction store |
|
||||
instr.getPartial() = store and field = store.getDestinationAddress()
|
||||
)
|
||||
}
|
||||
|
||||
// There might be multiple `ChiInstructions` that has a particular instruction as
|
||||
// the total operand - so this definition gives consistency errors in
|
||||
// DataFlowImplConsistency::Consistency. However, it's not clear what (if any) implications
|
||||
// this consistency failure has.
|
||||
override Node getPreUpdateNode() { result.asInstruction() = instr.getTotal() }
|
||||
|
||||
override Expr getDefinedExpr() {
|
||||
result = field.getObjectAddress().getUnconvertedResultExpression()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Not every store instruction generates a chi instruction that we can attach a PostUpdateNode to.
|
||||
* For instance, an update to a field of a struct containing only one field. For these cases we
|
||||
* attach the PostUpdateNode to the store instruction. There's no obvious pre update node for this case
|
||||
* (as the entire memory is updated), so `getPreUpdateNode` is implemented as `none()`.
|
||||
*/
|
||||
private class ExplicitSingleFieldStoreQualifierNode extends PartialDefinitionNode {
|
||||
override StoreInstruction instr;
|
||||
FieldAddressInstruction field;
|
||||
|
||||
ExplicitSingleFieldStoreQualifierNode() {
|
||||
field = instr.getDestinationAddress() and
|
||||
not exists(ChiInstruction chi | chi.getPartial() = instr)
|
||||
}
|
||||
|
||||
override Node getPreUpdateNode() { none() }
|
||||
|
||||
override Expr getDefinedExpr() {
|
||||
result = field.getObjectAddress().getUnconvertedResultExpression()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node that represents the value of a variable after a function call that
|
||||
* may have changed the variable because it's passed by reference.
|
||||
@@ -430,6 +388,413 @@ class VariableNode extends Node, TVariableNode {
|
||||
override string toString() { result = v.toString() }
|
||||
}
|
||||
|
||||
/** The target node of a `readStep`. */
|
||||
abstract class ReadStepNode extends Node {
|
||||
/** Get the field that is read. */
|
||||
abstract Field getAField();
|
||||
|
||||
/** Get the node representing the value that is read. */
|
||||
abstract Node getReadValue();
|
||||
}
|
||||
|
||||
/** The target node of a `storeStep`. */
|
||||
abstract class StoreStepNode extends PostUpdateNode {
|
||||
/** Get the field that is stored into. */
|
||||
abstract Field getAField();
|
||||
|
||||
/** Get the node representing the value that is stored. */
|
||||
abstract Node getStoredValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sometimes a sequence of `FieldAddressInstruction`s does not end with a `StoreInstruction`.
|
||||
* This class abstracts out the information needed to end a `StoreChain`.
|
||||
*/
|
||||
abstract private class StoreChainEndInstruction extends Instruction {
|
||||
abstract FieldAddressInstruction getFieldInstruction();
|
||||
|
||||
abstract Instruction getBeginInstruction();
|
||||
|
||||
abstract Node getPreUpdateNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* A `StoreInstruction` that ends a sequence of `FieldAddressInstruction`s.
|
||||
*/
|
||||
private class StoreChainEndInstructionStoreWithChi extends StoreChainEndInstruction, ChiInstruction {
|
||||
StoreInstruction store;
|
||||
FieldAddressInstruction fi;
|
||||
|
||||
StoreChainEndInstructionStoreWithChi() {
|
||||
not this.isResultConflated() and
|
||||
this.getPartial() = store and
|
||||
fi = skipConversion*(store.getDestinationAddress())
|
||||
}
|
||||
|
||||
override FieldAddressInstruction getFieldInstruction() { result = fi }
|
||||
|
||||
override Node getPreUpdateNode() { result.asInstruction() = this.getTotal() }
|
||||
|
||||
override Instruction getBeginInstruction() { result = store }
|
||||
}
|
||||
|
||||
/**
|
||||
* Not every store instruction generates a chi instruction that we can attach a PostUpdateNode to.
|
||||
* For instance, an update to a field of a struct containing only one field. For these cases we
|
||||
* attach the PostUpdateNode to the store instruction. There's no obvious pre update node for this case
|
||||
* (as the entire memory is updated), so `getPreUpdateNode` is implemented as `none()`.
|
||||
*/
|
||||
private class StoreChainEndInstructionStoreWithoutChi extends StoreChainEndInstruction,
|
||||
StoreInstruction {
|
||||
FieldAddressInstruction fi;
|
||||
|
||||
StoreChainEndInstructionStoreWithoutChi() {
|
||||
not exists(ChiInstruction chi | chi.getPartial() = this) and
|
||||
fi = skipConversion*(this.getDestinationAddress())
|
||||
}
|
||||
|
||||
override FieldAddressInstruction getFieldInstruction() { result = fi }
|
||||
|
||||
override Node getPreUpdateNode() { none() }
|
||||
|
||||
override Instruction getBeginInstruction() { result = this.getSourceValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* When traversing dependencies between an instruction and its operands
|
||||
* it is sometimes convenient to ignore certain instructions. For instance,
|
||||
* the `LoadChain` for `((B&)a.b).c` inserts a `CopyValueInstruction`
|
||||
* between the computed address for `b` and the `FieldAddressInstruction`
|
||||
* for `c`.
|
||||
*/
|
||||
private Instruction skipConversion(Instruction instr) {
|
||||
result = instr.(CopyInstruction).getSourceValue()
|
||||
or
|
||||
result = instr.(ConvertInstruction).getUnary()
|
||||
or
|
||||
result = instr.(CheckedConvertOrNullInstruction).getUnary()
|
||||
or
|
||||
result = instr.(InheritanceConversionInstruction).getUnary()
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends a `StoreChain` with a `WriteSideEffectInstruction` such that we build up
|
||||
* the correct access paths. For example in:
|
||||
* ```
|
||||
* void setter(B *b, int data) {
|
||||
* b->c = data;
|
||||
* }
|
||||
* ...
|
||||
* setter(&a.b, source());
|
||||
* sink(a.b.c)
|
||||
* ```
|
||||
* In order to register `a.b.c` as a `readStep`, the access path must
|
||||
* contain `[a, b, c]`, and thus the access path must be `[a, b]`
|
||||
* before entering `setter`.
|
||||
*/
|
||||
private class StoreChainEndInstructionSideEffect extends StoreChainEndInstruction, ChiInstruction {
|
||||
WriteSideEffectInstruction sideEffect;
|
||||
FieldAddressInstruction fi;
|
||||
|
||||
StoreChainEndInstructionSideEffect() {
|
||||
not this.isResultConflated() and
|
||||
this.getPartial() = sideEffect and
|
||||
fi = skipConversion*(sideEffect.getArgumentDef())
|
||||
}
|
||||
|
||||
override FieldAddressInstruction getFieldInstruction() { result = fi }
|
||||
|
||||
override Node getPreUpdateNode() { result.asInstruction() = this.getTotal() }
|
||||
|
||||
override Instruction getBeginInstruction() { result = sideEffect }
|
||||
}
|
||||
|
||||
private newtype TStoreChain =
|
||||
TStoreChainConsNil(FieldAddressInstruction f, StoreChainEndInstruction end) {
|
||||
end.getFieldInstruction() = f
|
||||
} or
|
||||
TStoreChainConsCons(FieldAddressInstruction f, TStoreChain next) {
|
||||
exists(FieldAddressInstruction g | skipConversion*(g.getObjectAddress()) = f |
|
||||
next = TStoreChainConsCons(g, _) or
|
||||
next = TStoreChainConsNil(g, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A `StoreChain` represents a series of field lookups that compute the destination of a store.
|
||||
* For example, given an assignment such as `a.b.c = x`, there are two `StoreChain`s:
|
||||
* One corresponding to the field `b`, and one corresponding to the field `c`. Here, `b` is the parent
|
||||
* `StoreChain` of `c`.
|
||||
*/
|
||||
private class StoreChain extends TStoreChain {
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets the parent of this `StoreChain`, if any. For example, for the assignment
|
||||
* ```
|
||||
* a.b.c = x;
|
||||
* ```
|
||||
* the parent of `c` is `b`, and `b` has no parent.
|
||||
*/
|
||||
final StoreChainConsCons getParent() { result.getChild() = this }
|
||||
|
||||
/** Gets the child of this `StoreChain`, if any. */
|
||||
StoreChain getChild() { none() }
|
||||
|
||||
/**
|
||||
* Gets the instruction that receives flow from the outermost `StoreChain` of this chain (i.e.,
|
||||
* the `StoreChain` with no parent).
|
||||
*/
|
||||
StoreChainEndInstruction getEndInstruction() { none() }
|
||||
|
||||
/**
|
||||
* Gets the instruction that flows to the innermost `StoreChain` of this chain (i.e.,
|
||||
* the `StoreChain` with no child).
|
||||
*/
|
||||
Instruction getBeginInstruction() { none() }
|
||||
|
||||
/** Gets the `FieldAddressInstruction` of this `StoreChain` */
|
||||
FieldAddressInstruction getFieldInstruction() { none() }
|
||||
|
||||
/** Gets the `FieldAddressInstruction` of any `StoreChain` in this chain. */
|
||||
FieldAddressInstruction getAFieldInstruction() { none() }
|
||||
|
||||
final Location getLocation() { result = getFieldInstruction().getLocation() }
|
||||
}
|
||||
|
||||
private class StoreChainConsNil extends StoreChain, TStoreChainConsNil {
|
||||
FieldAddressInstruction fi;
|
||||
StoreChainEndInstruction end;
|
||||
|
||||
StoreChainConsNil() { this = TStoreChainConsNil(fi, end) }
|
||||
|
||||
override string toString() { result = fi.getField().toString() }
|
||||
|
||||
override StoreChainEndInstruction getEndInstruction() { result = end }
|
||||
|
||||
override Instruction getBeginInstruction() { result = end.getBeginInstruction() }
|
||||
|
||||
override FieldAddressInstruction getFieldInstruction() { result = fi }
|
||||
|
||||
override FieldAddressInstruction getAFieldInstruction() { result = fi }
|
||||
}
|
||||
|
||||
private class StoreChainConsCons extends StoreChain, TStoreChainConsCons {
|
||||
FieldAddressInstruction fi;
|
||||
StoreChain next;
|
||||
|
||||
StoreChainConsCons() { this = TStoreChainConsCons(fi, next) }
|
||||
|
||||
override string toString() { result = fi.getField().toString() + "." + next.toString() }
|
||||
|
||||
override StoreChain getChild() { result = next }
|
||||
|
||||
override FieldAddressInstruction getFieldInstruction() { result = fi }
|
||||
|
||||
override FieldAddressInstruction getAFieldInstruction() {
|
||||
result = [fi, next.getAFieldInstruction()]
|
||||
}
|
||||
|
||||
override StoreChainEndInstruction getEndInstruction() { result = next.getEndInstruction() }
|
||||
|
||||
override Instruction getBeginInstruction() { result = next.getBeginInstruction() }
|
||||
}
|
||||
|
||||
private newtype TLoadChain =
|
||||
TLoadChainConsNil(FieldAddressInstruction fi, LoadChainEndInstruction end) {
|
||||
end.getFieldInstruction() = fi
|
||||
} or
|
||||
TLoadChainConsCons(FieldAddressInstruction fi, TLoadChain next) {
|
||||
exists(FieldAddressInstruction nextFi | skipConversion*(nextFi.getObjectAddress()) = fi |
|
||||
next = TLoadChainConsCons(nextFi, _) or
|
||||
next = TLoadChainConsNil(nextFi, _)
|
||||
)
|
||||
}
|
||||
|
||||
/** This class abstracts out the information needed to end a `LoadChain`. */
|
||||
abstract private class LoadChainEndInstruction extends Instruction {
|
||||
abstract FieldAddressInstruction getFieldInstruction();
|
||||
|
||||
abstract Instruction getReadValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* A `LoadInstruction` that ends a sequence of `FieldAddressInstruction`s.
|
||||
*/
|
||||
private class LoadChainEndInstructionLoad extends LoadChainEndInstruction, LoadInstruction {
|
||||
FieldAddressInstruction fi;
|
||||
|
||||
LoadChainEndInstructionLoad() { fi = skipConversion*(this.getSourceAddress()) }
|
||||
|
||||
override FieldAddressInstruction getFieldInstruction() { result = fi }
|
||||
|
||||
override Instruction getReadValue() { result = getSourceValueOperand().getAnyDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends a `LoadChain` with a `ReadSideEffectInstruction`. This ensures that we pop content from the
|
||||
* access path when passing an argument that reads a field. For example in:
|
||||
* ```
|
||||
* void read_f(Inner* inner) {
|
||||
* sink(inner->f);
|
||||
* }
|
||||
* ...
|
||||
* outer.inner.f = taint();
|
||||
* read_f(&outer.inner);
|
||||
* ```
|
||||
* In order to register `inner->f` as a `readStep`, the head of the access path must
|
||||
* be `f`, and thus reading `&outer.inner` must pop `inner` from the access path
|
||||
* before entering `read_f`.
|
||||
*/
|
||||
private class LoadChainEndInstructionSideEffect extends LoadChainEndInstruction,
|
||||
ReadSideEffectInstruction {
|
||||
FieldAddressInstruction fi;
|
||||
|
||||
LoadChainEndInstructionSideEffect() { fi = skipConversion*(this.getArgumentDef()) }
|
||||
|
||||
override FieldAddressInstruction getFieldInstruction() { result = fi }
|
||||
|
||||
override Instruction getReadValue() { result = getSideEffectOperand().getAnyDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `LoadChain` represents a series of field lookups that compute the source address of a load.
|
||||
* For example, given the field lookup in `f(a.b.c)`, there are two `LoadChains`s:
|
||||
* One corresponding to the field `b`, and one corresponding to the field `c`. Here, `b` is the parent
|
||||
* `LoadChain` of `c`.
|
||||
*/
|
||||
private class LoadChain extends TLoadChain {
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets the instruction that receives flow from the innermost `LoadChain` of this chain (i.e.,
|
||||
* the `LoadChain` with no child).
|
||||
*/
|
||||
LoadChainEndInstruction getEndInstruction() { none() }
|
||||
|
||||
/**
|
||||
* Gets the parent of this `LoadChain`, if any. For example in `f(a.b.c)` the parent of `c` is `b`,
|
||||
* and `b` has no parent.
|
||||
*/
|
||||
final LoadChainConsCons getParent() { result.getChild() = this }
|
||||
|
||||
/** Gets the child of this `LoadChain`, if any. */
|
||||
LoadChain getChild() { none() }
|
||||
|
||||
/** Gets the `FieldAddressInstruction` of this `LoadChain` */
|
||||
FieldAddressInstruction getFieldInstruction() { none() }
|
||||
|
||||
final Location getLocation() { result = getFieldInstruction().getLocation() }
|
||||
}
|
||||
|
||||
private class LoadChainConsNil extends LoadChain, TLoadChainConsNil {
|
||||
FieldAddressInstruction fi;
|
||||
LoadChainEndInstruction end;
|
||||
|
||||
LoadChainConsNil() { this = TLoadChainConsNil(fi, end) }
|
||||
|
||||
override string toString() { result = fi.getField().toString() }
|
||||
|
||||
override LoadChainEndInstruction getEndInstruction() { result = end }
|
||||
|
||||
override FieldAddressInstruction getFieldInstruction() { result = fi }
|
||||
}
|
||||
|
||||
private class LoadChainConsCons extends LoadChain, TLoadChainConsCons {
|
||||
FieldAddressInstruction fi;
|
||||
LoadChain next;
|
||||
|
||||
LoadChainConsCons() { this = TLoadChainConsCons(fi, next) }
|
||||
|
||||
override string toString() { result = fi.getField().toString() + "." + next.toString() }
|
||||
|
||||
override LoadChainEndInstruction getEndInstruction() { result = next.getEndInstruction() }
|
||||
|
||||
override LoadChain getChild() { result = next }
|
||||
|
||||
override FieldAddressInstruction getFieldInstruction() { result = fi }
|
||||
}
|
||||
|
||||
/**
|
||||
* A dataflow node generated by a partial definition.
|
||||
* The `StoreNode` class extends `ReadStepNode` to participate in reverse read steps.
|
||||
* A reverse read is a store step that is "inferred" by the DataFlow library. For example in the
|
||||
* assignment:
|
||||
* ```
|
||||
* a.b.c = x;
|
||||
* ```
|
||||
* Here, the access path after the store must reflect that a value has been stored into the field `c` of
|
||||
* the object at field `b`. The field `c` is added to the access path through a `storeStep`, and the
|
||||
* field `b` is inferred by the DataFlow library because there's a read step (reading the field `b`) from
|
||||
* the pre update node for `b.c` to the pre update node for `c`.
|
||||
*/
|
||||
private class StoreNode extends TStoreNode, StoreStepNode, ReadStepNode, PartialDefinitionNode {
|
||||
StoreChain storeChain;
|
||||
|
||||
StoreNode() { this = TStoreNode(storeChain) }
|
||||
|
||||
override string toString() { result = storeChain.toString() }
|
||||
|
||||
StoreChain getStoreChain() { result = storeChain }
|
||||
|
||||
override Node getPreUpdateNode() {
|
||||
result.(StoreNode).getStoreChain() = storeChain.getParent()
|
||||
or
|
||||
not exists(storeChain.getParent()) and
|
||||
result = storeChain.getEndInstruction().getPreUpdateNode()
|
||||
}
|
||||
|
||||
override Field getAField() { result = storeChain.getFieldInstruction().getField() }
|
||||
|
||||
override Node getStoredValue() {
|
||||
// Only the `StoreNode` attached to the end of the `StoreChain` has a `getStoredValue()`, so
|
||||
// this is the only `StoreNode` that matches storeStep.
|
||||
not exists(storeChain.getChild()) and result.asInstruction() = storeChain.getBeginInstruction()
|
||||
}
|
||||
|
||||
override Node getReadValue() { result = getPreUpdateNode() }
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
override Function getFunction() { result = storeChain.getEndInstruction().getEnclosingFunction() }
|
||||
|
||||
override Type getType() { result = storeChain.getEndInstruction().getResultType() }
|
||||
|
||||
override Location getLocation() { result = storeChain.getEndInstruction().getLocation() }
|
||||
|
||||
override Expr getDefinedExpr() {
|
||||
result = storeChain.getAFieldInstruction().getObjectAddress().getUnconvertedResultExpression()
|
||||
}
|
||||
}
|
||||
|
||||
/** A dataflow node generated by loading from an address computed by a sequence of fields lookups. */
|
||||
private class LoadNode extends TLoadNode, ReadStepNode {
|
||||
LoadChain loadChain;
|
||||
|
||||
LoadNode() { this = TLoadNode(loadChain) }
|
||||
|
||||
override Field getAField() { result = loadChain.getFieldInstruction().getField() }
|
||||
|
||||
override Node getReadValue() {
|
||||
result.(LoadNode).getLoadChain() = loadChain.getParent()
|
||||
or
|
||||
not exists(loadChain.getParent()) and
|
||||
result.asInstruction() = loadChain.getEndInstruction().getReadValue()
|
||||
}
|
||||
|
||||
LoadChain getLoadChain() { result = loadChain }
|
||||
|
||||
override string toString() { result = loadChain.toString() }
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
override Function getFunction() { result = loadChain.getEndInstruction().getEnclosingFunction() }
|
||||
|
||||
override Type getType() { result = loadChain.getEndInstruction().getResultType() }
|
||||
|
||||
override Location getLocation() { result = loadChain.getEndInstruction().getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the node corresponding to `instr`.
|
||||
*/
|
||||
@@ -483,6 +848,22 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFr
|
||||
*/
|
||||
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
simpleInstructionLocalFlowStep(nodeFrom.asInstruction(), nodeTo.asInstruction())
|
||||
or
|
||||
// When flow has gone all the way through the chain of field accesses
|
||||
// `[f1,f2, ..., fn]` (from right to left) we add flow from f1 to the end instruction.
|
||||
exists(StoreNode synthFrom |
|
||||
synthFrom = nodeFrom and
|
||||
not exists(synthFrom.getStoreChain().getParent()) and
|
||||
synthFrom.getStoreChain().getEndInstruction() = nodeTo.asInstruction()
|
||||
)
|
||||
or
|
||||
// When flow has gone all the way through the chain of field accesses
|
||||
// `[f1, f2, ..., fn]` (from left to right) we add flow from fn to the end instruction.
|
||||
exists(LoadNode synthFrom |
|
||||
synthFrom = nodeFrom and
|
||||
not exists(synthFrom.getLoadChain().getChild()) and
|
||||
synthFrom.getLoadChain().getEndInstruction() = nodeTo.asInstruction()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -564,10 +945,11 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
|
||||
or
|
||||
// Flow from stores to structs with a single field to a load of that field.
|
||||
iTo.(LoadInstruction).getSourceValueOperand().getAnyDef() = iFrom and
|
||||
exists(int size, Type type |
|
||||
exists(int size, Type type, Class cTo |
|
||||
type = iFrom.getResultType() and
|
||||
iTo.getResultType().getSize() = size and
|
||||
getFieldSizeOfClass(iTo.getResultType(), type, size)
|
||||
cTo = iTo.getResultType() and
|
||||
cTo.getSize() = size and
|
||||
getFieldSizeOfClass(cTo, type, size)
|
||||
)
|
||||
or
|
||||
// Flow through modeled functions
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -20,7 +20,7 @@ unreachableNodeCCtx
|
||||
localCallNodes
|
||||
postIsNotPre
|
||||
postHasUniquePre
|
||||
| simple.cpp:65:5:65:22 | Store | PostUpdateNode should have one pre-update node but has 0. |
|
||||
| simple.cpp:65:5:65:22 | i | PostUpdateNode should have one pre-update node but has 0. |
|
||||
uniquePostUpdate
|
||||
postIsInSameCallable
|
||||
reverseRead
|
||||
|
||||
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,166 +1,657 @@
|
||||
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 | Chi [a] | A.cpp:101:8:101:9 | Argument 0 indirection [a] |
|
||||
| A.cpp:100:5:100:13 | Store | A.cpp:100:5:100:13 | Chi [a] |
|
||||
| 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: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 | Chi [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:151:18:151:18 | Chi [c] | A.cpp:154:13:154:13 | c |
|
||||
| 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 | Chi [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] |
|
||||
| aliasing.cpp:9:11:9:20 | call to user_input | aliasing.cpp:9:3:9:22 | Store |
|
||||
| aliasing.cpp:13:3:13:21 | Chi [m1] | aliasing.cpp:26:19:26:20 | referenceSetter output argument [m1] |
|
||||
| aliasing.cpp:13:3:13:21 | Store | aliasing.cpp:13:3:13:21 | Chi [m1] |
|
||||
| aliasing.cpp:13:3:13:21 | Store | aliasing.cpp:13:3:13:21 | m1 [m1] |
|
||||
| aliasing.cpp:13:3:13:21 | m1 [m1] | aliasing.cpp:13:3:13:21 | Chi [m1] |
|
||||
| aliasing.cpp:13:10:13:19 | call to user_input | aliasing.cpp:13:3:13:21 | Store |
|
||||
| aliasing.cpp:25:17:25:19 | Chi [m1] | aliasing.cpp:29:11:29:12 | m1 |
|
||||
| aliasing.cpp:25:17:25:19 | pointerSetter output argument [m1] | aliasing.cpp:25:17:25:19 | Chi [m1] |
|
||||
| aliasing.cpp:26:19:26:20 | Chi [m1] | aliasing.cpp:30:11:30:12 | m1 |
|
||||
| aliasing.cpp:26:19:26:20 | referenceSetter output argument [m1] | aliasing.cpp:26:19:26:20 | Chi [m1] |
|
||||
| aliasing.cpp:29:11:29:12 | m1 | aliasing.cpp:29:11:29:12 | m1 |
|
||||
| aliasing.cpp:30:11:30:12 | m1 | aliasing.cpp:30:11:30:12 | m1 |
|
||||
| aliasing.cpp:37:13:37:22 | call to user_input | aliasing.cpp:38:11:38:12 | m1 |
|
||||
| aliasing.cpp:42:11:42:20 | call to user_input | aliasing.cpp:43:13:43:14 | m1 |
|
||||
| aliasing.cpp:60:3:60:22 | Chi [m1] | aliasing.cpp:61:13:61:14 | Store [m1] |
|
||||
| aliasing.cpp:60:3:60:22 | Store | aliasing.cpp:60:3:60:22 | Chi [m1] |
|
||||
| aliasing.cpp:60:3:60:22 | Store | aliasing.cpp:60:3:60:22 | m1 [m1] |
|
||||
| aliasing.cpp:60:3:60:22 | m1 [m1] | aliasing.cpp:61:13:61:14 | Store [m1] |
|
||||
| aliasing.cpp:60:11:60:20 | call to user_input | aliasing.cpp:60:3:60:22 | Store |
|
||||
| aliasing.cpp:61:13:61:14 | Store [m1] | aliasing.cpp:62:14:62:15 | m1 |
|
||||
| aliasing.cpp:62:14:62:15 | m1 | aliasing.cpp:62:14:62:15 | m1 |
|
||||
| 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 |
|
||||
| by_reference.cpp:84:3:84:25 | Chi [a] | by_reference.cpp:102:21:102:39 | taint_inner_a_ptr output argument [a] |
|
||||
| by_reference.cpp:84:3:84:25 | Chi [a] | by_reference.cpp:106:21:106:41 | taint_inner_a_ptr output argument [a] |
|
||||
| by_reference.cpp:84:3:84:25 | Store | by_reference.cpp:84:3:84:25 | Chi [a] |
|
||||
| by_reference.cpp:84:3:84:25 | Store | by_reference.cpp:84:3:84:25 | a [a] |
|
||||
| by_reference.cpp:84:3:84:25 | a [a] | by_reference.cpp:84:3:84:25 | Chi [a] |
|
||||
| by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:84:3:84:25 | Store |
|
||||
| by_reference.cpp:88:3:88:24 | Chi [a] | by_reference.cpp:122:21:122:38 | taint_inner_a_ref output argument [a] |
|
||||
| by_reference.cpp:88:3:88:24 | Chi [a] | by_reference.cpp:126:21:126:40 | taint_inner_a_ref output argument [a] |
|
||||
| by_reference.cpp:88:3:88:24 | Store | by_reference.cpp:88:3:88:24 | Chi [a] |
|
||||
| by_reference.cpp:88:3:88:24 | Store | by_reference.cpp:88:3:88:24 | a [a] |
|
||||
| by_reference.cpp:88:3:88:24 | a [a] | by_reference.cpp:88:3:88:24 | Chi [a] |
|
||||
| by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:88:3:88:24 | Store |
|
||||
| by_reference.cpp:102:21:102:39 | Chi [a] | by_reference.cpp:110:27:110:27 | a |
|
||||
| by_reference.cpp:102:21:102:39 | taint_inner_a_ptr output argument [a] | by_reference.cpp:102:21:102:39 | Chi [a] |
|
||||
| by_reference.cpp:106:21:106:41 | Chi [a] | by_reference.cpp:114:29:114:29 | a |
|
||||
| by_reference.cpp:106:21:106:41 | taint_inner_a_ptr output argument [a] | by_reference.cpp:106:21:106:41 | Chi [a] |
|
||||
| by_reference.cpp:122:21:122:38 | Chi [a] | by_reference.cpp:130:27:130:27 | a |
|
||||
| by_reference.cpp:122:21:122:38 | taint_inner_a_ref output argument [a] | by_reference.cpp:122:21:122:38 | Chi [a] |
|
||||
| by_reference.cpp:126:21:126:40 | Chi [a] | by_reference.cpp:134:29:134:29 | a |
|
||||
| by_reference.cpp:126:21:126:40 | taint_inner_a_ref output argument [a] | by_reference.cpp:126:21:126:40 | Chi [a] |
|
||||
| simple.cpp:65:5:65:22 | Store [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 | Store [i] |
|
||||
| by_reference.cpp:102:21:102:39 | Chi [inner_nested, a] | by_reference.cpp:110:27:110:27 | inner_nested.a [a] |
|
||||
| by_reference.cpp:102:21:102:39 | inner_nested [inner_nested, a] | by_reference.cpp:102:21:102:39 | Chi [inner_nested, a] |
|
||||
| by_reference.cpp:102:21:102:39 | taint_inner_a_ptr output argument [a] | by_reference.cpp:102:21:102:39 | inner_nested [inner_nested, a] |
|
||||
| by_reference.cpp:106:21:106:41 | Chi [inner_nested, a] | by_reference.cpp:114:29:114:29 | inner_nested.a [a] |
|
||||
| by_reference.cpp:106:21:106:41 | inner_nested [inner_nested, a] | by_reference.cpp:106:21:106:41 | Chi [inner_nested, a] |
|
||||
| by_reference.cpp:106:21:106:41 | taint_inner_a_ptr output argument [a] | by_reference.cpp:106:21:106:41 | inner_nested [inner_nested, a] |
|
||||
| by_reference.cpp:110:27:110:27 | a | by_reference.cpp:110:27:110:27 | a |
|
||||
| by_reference.cpp:110:27:110:27 | inner_nested.a [a] | by_reference.cpp:110:27:110:27 | a |
|
||||
| by_reference.cpp:114:29:114:29 | a | by_reference.cpp:114:29:114:29 | a |
|
||||
| by_reference.cpp:114:29:114:29 | inner_nested.a [a] | by_reference.cpp:114:29:114:29 | a |
|
||||
| by_reference.cpp:122:21:122:38 | Chi [inner_nested, a] | by_reference.cpp:130:27:130:27 | inner_nested.a [a] |
|
||||
| by_reference.cpp:122:21:122:38 | inner_nested [inner_nested, a] | by_reference.cpp:122:21:122:38 | Chi [inner_nested, a] |
|
||||
| by_reference.cpp:122:21:122:38 | taint_inner_a_ref output argument [a] | by_reference.cpp:122:21:122:38 | inner_nested [inner_nested, a] |
|
||||
| by_reference.cpp:126:21:126:40 | Chi [inner_nested, a] | by_reference.cpp:134:29:134:29 | inner_nested.a [a] |
|
||||
| by_reference.cpp:126:21:126:40 | inner_nested [inner_nested, a] | by_reference.cpp:126:21:126:40 | Chi [inner_nested, a] |
|
||||
| by_reference.cpp:126:21:126:40 | taint_inner_a_ref output argument [a] | by_reference.cpp:126:21:126:40 | inner_nested [inner_nested, a] |
|
||||
| by_reference.cpp:130:27:130:27 | a | by_reference.cpp:130:27:130:27 | a |
|
||||
| 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 |
|
||||
| simple.cpp:83:9:83:28 | Chi [f1] | simple.cpp:84:14:84:20 | Argument -1 indirection [f1] |
|
||||
| simple.cpp:83:9:83:28 | Store | simple.cpp:83:9:83:28 | Chi [f1] |
|
||||
| simple.cpp:67:13:67:13 | i | simple.cpp:67:13:67:13 | i |
|
||||
| simple.cpp:83:9:83:28 | Store | simple.cpp:83:9:83:28 | f1 [f1] |
|
||||
| simple.cpp:83:9:83:28 | f1 [f1] | simple.cpp:83:9:83:28 | f2.f1 [f2, f1] |
|
||||
| simple.cpp:83:9:83:28 | f2.f1 [f2, f1] | simple.cpp:84:14:84:20 | Argument -1 indirection [f2, f1] |
|
||||
| simple.cpp:83:17:83:26 | call to user_input | simple.cpp:83:9:83:28 | Store |
|
||||
| simple.cpp:84:14:84:20 | Argument -1 indirection [f1] | simple.cpp:84:14:84:20 | call to getf2f1 |
|
||||
| simple.cpp:84:14:84:20 | Argument -1 indirection [f2, f1] | simple.cpp:84:14:84:20 | call to getf2f1 |
|
||||
| simple.cpp:108:30:108:31 | d2 [d1_2, y] | simple.cpp:111:18:111:18 | d1_2.y [y] |
|
||||
| simple.cpp:111:18:111:18 | d1_2.y [y] | simple.cpp:111:18:111:18 | y |
|
||||
| simple.cpp:111:18:111:18 | y | simple.cpp:111:18:111:18 | y |
|
||||
| simple.cpp:114:37:114:38 | *d2 [d1_2, y] | simple.cpp:117:19:117:19 | d1_2.y [y] |
|
||||
| simple.cpp:117:19:117:19 | d1_2.y [y] | simple.cpp:117:19:117:19 | y |
|
||||
| simple.cpp:117:19:117:19 | y | simple.cpp:117:19:117:19 | y |
|
||||
| simple.cpp:122:5:122:33 | Chi [d2_1, d1_1, ... (3)] | simple.cpp:123:27:123:30 | d2_1 [d1_1, x] |
|
||||
| simple.cpp:122:5:122:33 | Store | simple.cpp:122:5:122:33 | x [x] |
|
||||
| simple.cpp:122:5:122:33 | d1_1.x [d1_1, x] | simple.cpp:122:5:122:33 | d2_1.d1_1.x [d2_1, d1_1, ... (3)] |
|
||||
| simple.cpp:122:5:122:33 | d2_1.d1_1.x [d2_1, d1_1, ... (3)] | simple.cpp:122:5:122:33 | Chi [d2_1, d1_1, ... (3)] |
|
||||
| simple.cpp:122:5:122:33 | x [x] | simple.cpp:122:5:122:33 | d1_1.x [d1_1, x] |
|
||||
| simple.cpp:122:22:122:31 | call to user_input | simple.cpp:122:5:122:33 | Store |
|
||||
| simple.cpp:123:27:123:30 | Store [d1_1, x] | simple.cpp:124:20:124:20 | d1_1.x [x] |
|
||||
| simple.cpp:123:27:123:30 | Store [d1_1, x] | simple.cpp:130:15:130:15 | d1_1.x [x] |
|
||||
| simple.cpp:123:27:123:30 | d2_1 [d1_1, x] | simple.cpp:123:27:123:30 | Store [d1_1, x] |
|
||||
| simple.cpp:124:20:124:20 | d1_1.x [x] | simple.cpp:124:20:124:20 | x |
|
||||
| simple.cpp:124:20:124:20 | x | simple.cpp:124:20:124:20 | x |
|
||||
| simple.cpp:130:15:130:15 | d1_1.x [x] | simple.cpp:130:15:130:15 | x |
|
||||
| simple.cpp:130:15:130:15 | x | simple.cpp:130:15:130:15 | x |
|
||||
| simple.cpp:136:21:136:28 | Chi [d2_1, d1_2, ... (3)] | simple.cpp:139:23:139:23 | d2_1.d1_2.y [d1_2, y] |
|
||||
| simple.cpp:136:21:136:28 | Chi [d2_1, d1_2, ... (3)] | simple.cpp:141:20:141:23 | d2_1 [d1_2, y] |
|
||||
| simple.cpp:136:21:136:28 | Chi [d2_1, d1_2, ... (3)] | simple.cpp:143:23:143:30 | d2_1 [d1_2, y] |
|
||||
| simple.cpp:136:21:136:28 | Chi [d2_1, d1_2, ... (3)] | simple.cpp:143:23:143:30 | d2_1 [d1_2, y] |
|
||||
| simple.cpp:136:21:136:28 | d2_1 [d2_1, d1_2, ... (3)] | simple.cpp:136:21:136:28 | Chi [d2_1, d1_2, ... (3)] |
|
||||
| simple.cpp:136:21:136:28 | write_to_d1_2_y output argument [d1_2, y] | simple.cpp:136:21:136:28 | d2_1 [d2_1, d1_2, ... (3)] |
|
||||
| simple.cpp:136:21:136:28 | write_to_d1_2_y output argument [d1_2, y] | simple.cpp:143:23:143:30 | Argument 0 indirection [d1_2, y] |
|
||||
| simple.cpp:136:21:136:28 | write_to_d1_2_y output argument [d1_2, y] | simple.cpp:144:23:144:30 | Argument 0 indirection [d1_2, y] |
|
||||
| simple.cpp:136:31:136:40 | call to user_input | simple.cpp:136:21:136:28 | write_to_d1_2_y output argument [d1_2, y] |
|
||||
| simple.cpp:139:23:139:23 | d1_2.y [y] | simple.cpp:139:23:139:23 | y |
|
||||
| simple.cpp:139:23:139:23 | d2_1.d1_2.y [d1_2, y] | simple.cpp:139:23:139:23 | d1_2.y [y] |
|
||||
| simple.cpp:139:23:139:23 | y | simple.cpp:139:23:139:23 | y |
|
||||
| simple.cpp:141:20:141:23 | d2_1 [d1_2, y] | simple.cpp:108:30:108:31 | d2 [d1_2, y] |
|
||||
| simple.cpp:141:20:141:23 | d2_1 [d1_2, y] | simple.cpp:141:20:141:23 | d2_1 [d1_2, y] |
|
||||
| simple.cpp:143:23:143:30 | Argument 0 indirection [d1_2, y] | simple.cpp:114:37:114:38 | *d2 [d1_2, y] |
|
||||
| simple.cpp:143:23:143:30 | Argument 0 indirection [d1_2, y] | simple.cpp:143:23:143:30 | read_from_y_deref output argument [d1_2, y] |
|
||||
| simple.cpp:143:23:143:30 | d2_1 [d1_2, y] | simple.cpp:143:23:143:30 | Argument 0 indirection [d1_2, y] |
|
||||
| simple.cpp:143:23:143:30 | d2_1 [d1_2, y] | simple.cpp:144:23:144:30 | Argument 0 indirection [d1_2, y] |
|
||||
| simple.cpp:143:23:143:30 | read_from_y_deref output argument [d1_2, y] | simple.cpp:144:23:144:30 | Argument 0 indirection [d1_2, y] |
|
||||
| simple.cpp:144:23:144:30 | Argument 0 indirection [d1_2, y] | simple.cpp:114:37:114:38 | *d2 [d1_2, y] |
|
||||
| simple.cpp:159:20:159:24 | *inner [f] | simple.cpp:161:17:161:17 | f |
|
||||
| simple.cpp:161:17:161:17 | f | simple.cpp:161:17:161:17 | f |
|
||||
| simple.cpp:167:5:167:32 | Chi [inner, f] | simple.cpp:168:12:168:23 | inner [f] |
|
||||
| simple.cpp:167:5:167:32 | Store | simple.cpp:167:5:167:32 | f [f] |
|
||||
| simple.cpp:167:5:167:32 | f [f] | simple.cpp:167:5:167:32 | inner.f [inner, f] |
|
||||
| simple.cpp:167:5:167:32 | inner.f [inner, f] | simple.cpp:167:5:167:32 | Chi [inner, f] |
|
||||
| simple.cpp:167:21:167:30 | call to user_input | simple.cpp:167:5:167:32 | Store |
|
||||
| simple.cpp:168:12:168:23 | Argument 0 indirection [f] | simple.cpp:159:20:159:24 | *inner [f] |
|
||||
| simple.cpp:168:12:168:23 | inner [f] | simple.cpp:168:12:168:23 | Argument 0 indirection [f] |
|
||||
| struct_init.c:14:24:14:25 | *ab [a] | struct_init.c:15:12:15:12 | a |
|
||||
| struct_init.c:20:20:20:29 | Chi [a] | struct_init.c:24:10:24:12 | Argument 0 indirection [a] |
|
||||
| struct_init.c:20:20:20:29 | Store | struct_init.c:20:20:20:29 | Chi [a] |
|
||||
| struct_init.c:15:12:15:12 | a | struct_init.c:15:12:15:12 | a |
|
||||
| struct_init.c:20:20:20:29 | Store | struct_init.c:20:20:20:29 | a [a] |
|
||||
| struct_init.c:20:20:20:29 | a [a] | struct_init.c:24:10:24:12 | Argument 0 indirection [a] |
|
||||
| struct_init.c:20:20:20:29 | call to user_input | struct_init.c:20:20:20:29 | Store |
|
||||
| struct_init.c:20:20:20:29 | call to user_input | struct_init.c:22:11:22:11 | a |
|
||||
| struct_init.c:24:10:24:12 | Argument 0 indirection [a] | struct_init.c:14:24:14:25 | *ab [a] |
|
||||
| struct_init.c:27:7:27:16 | Chi [a] | struct_init.c:36:10:36:24 | Argument 0 indirection [a] |
|
||||
| struct_init.c:27:7:27:16 | Store | struct_init.c:27:7:27:16 | Chi [a] |
|
||||
| struct_init.c:27:7:27:16 | Chi [nestedAB, a] | struct_init.c:27:21:27:21 | nestedAB.b [a] |
|
||||
| struct_init.c:27:7:27:16 | Store | struct_init.c:27:7:27:16 | a [a] |
|
||||
| struct_init.c:27:7:27:16 | a [a] | struct_init.c:27:7:27:16 | nestedAB.a [nestedAB, a] |
|
||||
| struct_init.c:27:7:27:16 | call to user_input | struct_init.c:27:7:27:16 | Store |
|
||||
| struct_init.c:27:7:27:16 | call to user_input | struct_init.c:31:23:31:23 | a |
|
||||
| struct_init.c:27:7:27:16 | nestedAB.a [nestedAB, a] | struct_init.c:27:7:27:16 | Chi [nestedAB, a] |
|
||||
| struct_init.c:27:7:27:16 | nestedAB.a [nestedAB, a] | struct_init.c:28:5:28:7 | Chi [nestedAB, a] |
|
||||
| struct_init.c:27:21:27:21 | nestedAB.b [a] | struct_init.c:36:10:36:24 | Argument 0 indirection [a] |
|
||||
| struct_init.c:28:5:28:7 | Chi [nestedAB, a] | struct_init.c:36:10:36:24 | nestedAB [a] |
|
||||
| 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 | Chi [a] | semmle.label | Chi [a] |
|
||||
| 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] |
|
||||
| aliasing.cpp:9:11:9:20 | call to user_input | semmle.label | call to user_input |
|
||||
| aliasing.cpp:13:3:13:21 | Chi [m1] | semmle.label | Chi [m1] |
|
||||
| aliasing.cpp:13:3:13:21 | Store | semmle.label | Store |
|
||||
| aliasing.cpp:13:3:13:21 | m1 [m1] | semmle.label | m1 [m1] |
|
||||
| aliasing.cpp:13:10:13:19 | call to user_input | semmle.label | call to user_input |
|
||||
| aliasing.cpp:25:17:25:19 | Chi [m1] | semmle.label | Chi [m1] |
|
||||
| aliasing.cpp:25:17:25:19 | pointerSetter output argument [m1] | semmle.label | pointerSetter output argument [m1] |
|
||||
| aliasing.cpp:26:19:26:20 | Chi [m1] | semmle.label | Chi [m1] |
|
||||
| aliasing.cpp:26:19:26:20 | referenceSetter output argument [m1] | semmle.label | referenceSetter output argument [m1] |
|
||||
| aliasing.cpp:29:11:29:12 | m1 | semmle.label | m1 |
|
||||
| aliasing.cpp:29:11:29:12 | m1 | semmle.label | m1 |
|
||||
| aliasing.cpp:30:11:30:12 | m1 | semmle.label | m1 |
|
||||
| aliasing.cpp:30:11:30:12 | m1 | semmle.label | m1 |
|
||||
| aliasing.cpp:37:13:37:22 | call to user_input | semmle.label | call to user_input |
|
||||
| aliasing.cpp:38:11:38:12 | m1 | semmle.label | m1 |
|
||||
| aliasing.cpp:42:11:42:20 | call to user_input | semmle.label | call to user_input |
|
||||
| aliasing.cpp:43:13:43:14 | m1 | semmle.label | m1 |
|
||||
| aliasing.cpp:60:3:60:22 | Chi [m1] | semmle.label | Chi [m1] |
|
||||
| aliasing.cpp:60:3:60:22 | Store | semmle.label | Store |
|
||||
| aliasing.cpp:60:3:60:22 | m1 [m1] | semmle.label | m1 [m1] |
|
||||
| aliasing.cpp:60:11:60:20 | call to user_input | semmle.label | call to user_input |
|
||||
| aliasing.cpp:61:13:61:14 | Store [m1] | semmle.label | Store [m1] |
|
||||
| aliasing.cpp:62:14:62:15 | m1 | semmle.label | m1 |
|
||||
| aliasing.cpp:62:14:62:15 | m1 | semmle.label | m1 |
|
||||
| aliasing.cpp:79:11:79:20 | call to user_input | semmle.label | call to user_input |
|
||||
| aliasing.cpp:80:12:80:13 | m1 | semmle.label | m1 |
|
||||
| aliasing.cpp:86:10:86:19 | call to user_input | semmle.label | call to user_input |
|
||||
| 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 |
|
||||
| by_reference.cpp:69:22:69:23 | Argument 0 indirection [a] | semmle.label | Argument 0 indirection [a] |
|
||||
| by_reference.cpp:84:3:84:25 | Chi [a] | semmle.label | Chi [a] |
|
||||
| by_reference.cpp:84:3:84:25 | Store | semmle.label | Store |
|
||||
| by_reference.cpp:84:3:84:25 | a [a] | semmle.label | a [a] |
|
||||
| by_reference.cpp:84:14:84:23 | call to user_input | semmle.label | call to user_input |
|
||||
| by_reference.cpp:88:3:88:24 | Chi [a] | semmle.label | Chi [a] |
|
||||
| by_reference.cpp:88:3:88:24 | Store | semmle.label | Store |
|
||||
| by_reference.cpp:88:3:88:24 | a [a] | semmle.label | a [a] |
|
||||
| by_reference.cpp:88:13:88:22 | call to user_input | semmle.label | call to user_input |
|
||||
| by_reference.cpp:102:21:102:39 | Chi [a] | semmle.label | Chi [a] |
|
||||
| by_reference.cpp:102:21:102:39 | Chi [inner_nested, a] | semmle.label | Chi [inner_nested, a] |
|
||||
| by_reference.cpp:102:21:102:39 | inner_nested [inner_nested, a] | semmle.label | inner_nested [inner_nested, a] |
|
||||
| by_reference.cpp:102:21:102:39 | taint_inner_a_ptr output argument [a] | semmle.label | taint_inner_a_ptr output argument [a] |
|
||||
| by_reference.cpp:106:21:106:41 | Chi [a] | semmle.label | Chi [a] |
|
||||
| by_reference.cpp:106:21:106:41 | Chi [inner_nested, a] | semmle.label | Chi [inner_nested, a] |
|
||||
| by_reference.cpp:106:21:106:41 | inner_nested [inner_nested, a] | semmle.label | inner_nested [inner_nested, a] |
|
||||
| by_reference.cpp:106:21:106:41 | taint_inner_a_ptr output argument [a] | semmle.label | taint_inner_a_ptr output argument [a] |
|
||||
| by_reference.cpp:110:27:110:27 | a | semmle.label | a |
|
||||
| by_reference.cpp:110:27:110:27 | a | semmle.label | a |
|
||||
| by_reference.cpp:110:27:110:27 | inner_nested.a [a] | semmle.label | inner_nested.a [a] |
|
||||
| by_reference.cpp:114:29:114:29 | a | semmle.label | a |
|
||||
| by_reference.cpp:122:21:122:38 | Chi [a] | semmle.label | Chi [a] |
|
||||
| by_reference.cpp:114:29:114:29 | a | semmle.label | a |
|
||||
| by_reference.cpp:114:29:114:29 | inner_nested.a [a] | semmle.label | inner_nested.a [a] |
|
||||
| by_reference.cpp:122:21:122:38 | Chi [inner_nested, a] | semmle.label | Chi [inner_nested, a] |
|
||||
| by_reference.cpp:122:21:122:38 | inner_nested [inner_nested, a] | semmle.label | inner_nested [inner_nested, a] |
|
||||
| by_reference.cpp:122:21:122:38 | taint_inner_a_ref output argument [a] | semmle.label | taint_inner_a_ref output argument [a] |
|
||||
| by_reference.cpp:126:21:126:40 | Chi [a] | semmle.label | Chi [a] |
|
||||
| by_reference.cpp:126:21:126:40 | Chi [inner_nested, a] | semmle.label | Chi [inner_nested, a] |
|
||||
| by_reference.cpp:126:21:126:40 | inner_nested [inner_nested, a] | semmle.label | inner_nested [inner_nested, a] |
|
||||
| by_reference.cpp:126:21:126:40 | taint_inner_a_ref output argument [a] | semmle.label | taint_inner_a_ref output argument [a] |
|
||||
| by_reference.cpp:130:27:130:27 | a | semmle.label | a |
|
||||
| by_reference.cpp:130:27:130:27 | a | semmle.label | a |
|
||||
| by_reference.cpp:130:27:130:27 | inner_nested.a [a] | semmle.label | inner_nested.a [a] |
|
||||
| by_reference.cpp:134:29:134:29 | a | semmle.label | a |
|
||||
| simple.cpp:65:5:65:22 | Store [i] | semmle.label | Store [i] |
|
||||
| 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] |
|
||||
| simple.cpp:67:13:67:13 | i | semmle.label | i |
|
||||
| simple.cpp:83:9:83:28 | Chi [f1] | semmle.label | Chi [f1] |
|
||||
| simple.cpp:67:13:67:13 | i | semmle.label | i |
|
||||
| simple.cpp:83:9:83:28 | Store | semmle.label | Store |
|
||||
| simple.cpp:83:9:83:28 | f1 [f1] | semmle.label | f1 [f1] |
|
||||
| simple.cpp:83:9:83:28 | f2.f1 [f2, f1] | semmle.label | f2.f1 [f2, f1] |
|
||||
| simple.cpp:83:17:83:26 | call to user_input | semmle.label | call to user_input |
|
||||
| simple.cpp:84:14:84:20 | Argument -1 indirection [f1] | semmle.label | Argument -1 indirection [f1] |
|
||||
| simple.cpp:84:14:84:20 | Argument -1 indirection [f2, f1] | semmle.label | Argument -1 indirection [f2, f1] |
|
||||
| simple.cpp:84:14:84:20 | call to getf2f1 | semmle.label | call to getf2f1 |
|
||||
| simple.cpp:108:30:108:31 | d2 [d1_2, y] | semmle.label | d2 [d1_2, y] |
|
||||
| simple.cpp:111:18:111:18 | d1_2.y [y] | semmle.label | d1_2.y [y] |
|
||||
| simple.cpp:111:18:111:18 | y | semmle.label | y |
|
||||
| simple.cpp:111:18:111:18 | y | semmle.label | y |
|
||||
| simple.cpp:114:37:114:38 | *d2 [d1_2, y] | semmle.label | *d2 [d1_2, y] |
|
||||
| simple.cpp:117:19:117:19 | d1_2.y [y] | semmle.label | d1_2.y [y] |
|
||||
| simple.cpp:117:19:117:19 | y | semmle.label | y |
|
||||
| simple.cpp:117:19:117:19 | y | semmle.label | y |
|
||||
| simple.cpp:122:5:122:33 | Chi [d2_1, d1_1, ... (3)] | semmle.label | Chi [d2_1, d1_1, ... (3)] |
|
||||
| simple.cpp:122:5:122:33 | Store | semmle.label | Store |
|
||||
| simple.cpp:122:5:122:33 | d1_1.x [d1_1, x] | semmle.label | d1_1.x [d1_1, x] |
|
||||
| simple.cpp:122:5:122:33 | d2_1.d1_1.x [d2_1, d1_1, ... (3)] | semmle.label | d2_1.d1_1.x [d2_1, d1_1, ... (3)] |
|
||||
| simple.cpp:122:5:122:33 | x [x] | semmle.label | x [x] |
|
||||
| simple.cpp:122:22:122:31 | call to user_input | semmle.label | call to user_input |
|
||||
| simple.cpp:123:27:123:30 | Store [d1_1, x] | semmle.label | Store [d1_1, x] |
|
||||
| simple.cpp:123:27:123:30 | d2_1 [d1_1, x] | semmle.label | d2_1 [d1_1, x] |
|
||||
| simple.cpp:124:20:124:20 | d1_1.x [x] | semmle.label | d1_1.x [x] |
|
||||
| simple.cpp:124:20:124:20 | x | semmle.label | x |
|
||||
| simple.cpp:124:20:124:20 | x | semmle.label | x |
|
||||
| simple.cpp:130:15:130:15 | d1_1.x [x] | semmle.label | d1_1.x [x] |
|
||||
| simple.cpp:130:15:130:15 | x | semmle.label | x |
|
||||
| simple.cpp:130:15:130:15 | x | semmle.label | x |
|
||||
| simple.cpp:136:21:136:28 | Chi [d2_1, d1_2, ... (3)] | semmle.label | Chi [d2_1, d1_2, ... (3)] |
|
||||
| simple.cpp:136:21:136:28 | d2_1 [d2_1, d1_2, ... (3)] | semmle.label | d2_1 [d2_1, d1_2, ... (3)] |
|
||||
| simple.cpp:136:21:136:28 | write_to_d1_2_y output argument [d1_2, y] | semmle.label | write_to_d1_2_y output argument [d1_2, y] |
|
||||
| simple.cpp:136:31:136:40 | call to user_input | semmle.label | call to user_input |
|
||||
| simple.cpp:139:23:139:23 | d1_2.y [y] | semmle.label | d1_2.y [y] |
|
||||
| simple.cpp:139:23:139:23 | d2_1.d1_2.y [d1_2, y] | semmle.label | d2_1.d1_2.y [d1_2, y] |
|
||||
| simple.cpp:139:23:139:23 | y | semmle.label | y |
|
||||
| simple.cpp:139:23:139:23 | y | semmle.label | y |
|
||||
| simple.cpp:141:20:141:23 | d2_1 [d1_2, y] | semmle.label | d2_1 [d1_2, y] |
|
||||
| simple.cpp:141:20:141:23 | d2_1 [d1_2, y] | semmle.label | d2_1 [d1_2, y] |
|
||||
| simple.cpp:143:23:143:30 | Argument 0 indirection [d1_2, y] | semmle.label | Argument 0 indirection [d1_2, y] |
|
||||
| simple.cpp:143:23:143:30 | d2_1 [d1_2, y] | semmle.label | d2_1 [d1_2, y] |
|
||||
| simple.cpp:143:23:143:30 | d2_1 [d1_2, y] | semmle.label | d2_1 [d1_2, y] |
|
||||
| simple.cpp:143:23:143:30 | read_from_y_deref output argument [d1_2, y] | semmle.label | read_from_y_deref output argument [d1_2, y] |
|
||||
| simple.cpp:144:23:144:30 | Argument 0 indirection [d1_2, y] | semmle.label | Argument 0 indirection [d1_2, y] |
|
||||
| simple.cpp:159:20:159:24 | *inner [f] | semmle.label | *inner [f] |
|
||||
| simple.cpp:161:17:161:17 | f | semmle.label | f |
|
||||
| simple.cpp:161:17:161:17 | f | semmle.label | f |
|
||||
| simple.cpp:167:5:167:32 | Chi [inner, f] | semmle.label | Chi [inner, f] |
|
||||
| simple.cpp:167:5:167:32 | Store | semmle.label | Store |
|
||||
| simple.cpp:167:5:167:32 | f [f] | semmle.label | f [f] |
|
||||
| simple.cpp:167:5:167:32 | inner.f [inner, f] | semmle.label | inner.f [inner, f] |
|
||||
| simple.cpp:167:21:167:30 | call to user_input | semmle.label | call to user_input |
|
||||
| simple.cpp:168:12:168:23 | Argument 0 indirection [f] | semmle.label | Argument 0 indirection [f] |
|
||||
| simple.cpp:168:12:168:23 | inner [f] | semmle.label | inner [f] |
|
||||
| struct_init.c:14:24:14:25 | *ab [a] | semmle.label | *ab [a] |
|
||||
| struct_init.c:15:12:15:12 | a | semmle.label | a |
|
||||
| struct_init.c:20:20:20:29 | Chi [a] | semmle.label | Chi [a] |
|
||||
| struct_init.c:15:12:15:12 | a | semmle.label | a |
|
||||
| struct_init.c:20:20:20:29 | Store | semmle.label | Store |
|
||||
| struct_init.c:20:20:20:29 | a [a] | semmle.label | a [a] |
|
||||
| struct_init.c:20:20:20:29 | call to user_input | semmle.label | call to user_input |
|
||||
| struct_init.c:22:11:22:11 | a | semmle.label | a |
|
||||
| struct_init.c:24:10:24:12 | Argument 0 indirection [a] | semmle.label | Argument 0 indirection [a] |
|
||||
| struct_init.c:27:7:27:16 | Chi [a] | semmle.label | Chi [a] |
|
||||
| struct_init.c:27:7:27:16 | Chi [nestedAB, a] | semmle.label | Chi [nestedAB, a] |
|
||||
| struct_init.c:27:7:27:16 | Store | semmle.label | Store |
|
||||
| struct_init.c:27:7:27:16 | a [a] | semmle.label | a [a] |
|
||||
| struct_init.c:27:7:27:16 | call to user_input | semmle.label | call to user_input |
|
||||
| struct_init.c:27:7:27:16 | nestedAB.a [nestedAB, a] | semmle.label | nestedAB.a [nestedAB, a] |
|
||||
| struct_init.c:27:21:27:21 | nestedAB.b [a] | semmle.label | nestedAB.b [a] |
|
||||
| struct_init.c:28:5:28:7 | Chi [nestedAB, a] | semmle.label | Chi [nestedAB, a] |
|
||||
| struct_init.c:31:23:31:23 | a | semmle.label | a |
|
||||
| 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 |
|
||||
@@ -169,13 +660,38 @@ 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 |
|
||||
| simple.cpp:117:19:117:19 | y | simple.cpp:136:31:136:40 | call to user_input | simple.cpp:117:19:117:19 | y | y flows from $@ | simple.cpp:136:31:136:40 | call to user_input | call to user_input |
|
||||
| simple.cpp:124:20:124:20 | x | simple.cpp:122:22:122:31 | call to user_input | simple.cpp:124:20:124:20 | x | x flows from $@ | simple.cpp:122:22:122:31 | call to user_input | call to user_input |
|
||||
| simple.cpp:130:15:130:15 | x | simple.cpp:122:22:122:31 | call to user_input | simple.cpp:130:15:130:15 | x | x flows from $@ | simple.cpp:122:22:122:31 | call to user_input | call to user_input |
|
||||
| simple.cpp:139:23:139:23 | y | simple.cpp:136:31:136:40 | call to user_input | simple.cpp:139:23:139:23 | y | y flows from $@ | simple.cpp:136:31:136:40 | call to user_input | call to user_input |
|
||||
| simple.cpp:161:17:161:17 | f | simple.cpp:167:21:167:30 | call to user_input | simple.cpp:161:17:161:17 | f | f flows from $@ | simple.cpp:167:21:167:30 | call to user_input | call to user_input |
|
||||
| struct_init.c:15:12:15:12 | a | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:20:20:20:29 | call to user_input | call to user_input |
|
||||
| struct_init.c:15:12:15:12 | a | struct_init.c:27:7:27:16 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:27:7:27:16 | call to user_input | call to user_input |
|
||||
| struct_init.c:22:11:22:11 | a | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:22:11:22:11 | a | a flows from $@ | struct_init.c:20:20:20:29 | call to user_input | call to user_input |
|
||||
|
||||
@@ -155,7 +155,6 @@
|
||||
| aliasing.cpp:72:5:72:6 | m1 | AST only |
|
||||
| aliasing.cpp:79:6:79:7 | m1 | AST only |
|
||||
| aliasing.cpp:86:5:86:6 | m1 | AST only |
|
||||
| aliasing.cpp:92:3:92:3 | w | AST only |
|
||||
| aliasing.cpp:92:7:92:8 | m1 | AST only |
|
||||
| by_reference.cpp:12:8:12:8 | a | AST only |
|
||||
| by_reference.cpp:16:11:16:11 | a | AST only |
|
||||
@@ -178,17 +177,13 @@
|
||||
| by_reference.cpp:84:10:84:10 | a | AST only |
|
||||
| by_reference.cpp:88:9:88:9 | a | AST only |
|
||||
| by_reference.cpp:102:21:102:39 | & ... | AST only |
|
||||
| by_reference.cpp:102:22:102:26 | outer | AST only |
|
||||
| by_reference.cpp:103:21:103:25 | outer | AST only |
|
||||
| by_reference.cpp:103:27:103:35 | inner_ptr | AST only |
|
||||
| by_reference.cpp:104:15:104:22 | & ... | AST only |
|
||||
| by_reference.cpp:104:16:104:20 | outer | AST only |
|
||||
| by_reference.cpp:106:21:106:41 | & ... | AST only |
|
||||
| by_reference.cpp:106:22:106:27 | pouter | AST only |
|
||||
| by_reference.cpp:107:21:107:26 | pouter | AST only |
|
||||
| by_reference.cpp:107:29:107:37 | inner_ptr | AST only |
|
||||
| by_reference.cpp:108:15:108:24 | & ... | AST only |
|
||||
| by_reference.cpp:108:16:108:21 | pouter | AST only |
|
||||
| by_reference.cpp:110:8:110:12 | outer | AST only |
|
||||
| by_reference.cpp:110:14:110:25 | inner_nested | AST only |
|
||||
| by_reference.cpp:110:27:110:27 | a | AST only |
|
||||
@@ -205,17 +200,13 @@
|
||||
| by_reference.cpp:115:27:115:27 | a | AST only |
|
||||
| by_reference.cpp:116:8:116:13 | pouter | AST only |
|
||||
| by_reference.cpp:116:16:116:16 | a | AST only |
|
||||
| by_reference.cpp:122:21:122:25 | outer | AST only |
|
||||
| by_reference.cpp:122:27:122:38 | inner_nested | AST only |
|
||||
| by_reference.cpp:123:21:123:36 | * ... | AST only |
|
||||
| by_reference.cpp:123:22:123:26 | outer | AST only |
|
||||
| by_reference.cpp:124:15:124:19 | outer | AST only |
|
||||
| by_reference.cpp:124:21:124:21 | a | AST only |
|
||||
| by_reference.cpp:126:21:126:26 | pouter | AST only |
|
||||
| by_reference.cpp:126:29:126:40 | inner_nested | AST only |
|
||||
| by_reference.cpp:127:21:127:38 | * ... | AST only |
|
||||
| by_reference.cpp:127:22:127:27 | pouter | AST only |
|
||||
| by_reference.cpp:128:15:128:20 | pouter | AST only |
|
||||
| by_reference.cpp:128:23:128:23 | a | AST only |
|
||||
| by_reference.cpp:130:8:130:12 | outer | AST only |
|
||||
| by_reference.cpp:130:14:130:25 | inner_nested | AST only |
|
||||
@@ -235,23 +226,11 @@
|
||||
| by_reference.cpp:136:16:136:16 | a | AST only |
|
||||
| complex.cpp:11:22:11:23 | a_ | AST only |
|
||||
| complex.cpp:12:22:12:23 | b_ | AST only |
|
||||
| complex.cpp:51:8:51:8 | b | AST only |
|
||||
| complex.cpp:51:10:51:14 | inner | AST only |
|
||||
| complex.cpp:51:16:51:16 | f | AST only |
|
||||
| complex.cpp:52:8:52:8 | b | AST only |
|
||||
| complex.cpp:52:10:52:14 | inner | AST only |
|
||||
| complex.cpp:52:16:52:16 | f | AST only |
|
||||
| complex.cpp:62:3:62:4 | b1 | AST only |
|
||||
| complex.cpp:62:6:62:10 | inner | AST only |
|
||||
| complex.cpp:62:12:62:12 | f | AST only |
|
||||
| complex.cpp:63:3:63:4 | b2 | AST only |
|
||||
| complex.cpp:63:6:63:10 | inner | AST only |
|
||||
| complex.cpp:63:12:63:12 | f | AST only |
|
||||
| complex.cpp:64:3:64:4 | b3 | AST only |
|
||||
| complex.cpp:64:6:64:10 | inner | AST only |
|
||||
| complex.cpp:64:12:64:12 | f | AST only |
|
||||
| complex.cpp:65:3:65:4 | b3 | AST only |
|
||||
| complex.cpp:65:6:65:10 | inner | AST only |
|
||||
| complex.cpp:65:12:65:12 | f | AST only |
|
||||
| complex.cpp:68:7:68:8 | b1 | AST only |
|
||||
| complex.cpp:71:7:71:8 | b2 | AST only |
|
||||
@@ -317,9 +296,15 @@
|
||||
| simple.cpp:51:9:51:9 | h | AST only |
|
||||
| simple.cpp:54:9:54:9 | i | AST only |
|
||||
| simple.cpp:65:7:65:7 | i | AST only |
|
||||
| simple.cpp:83:9:83:10 | this | AST only |
|
||||
| simple.cpp:83:12:83:13 | f1 | AST only |
|
||||
| simple.cpp:84:14:84:20 | this | AST only |
|
||||
| simple.cpp:105:14:105:14 | y | AST only |
|
||||
| simple.cpp:122:18:122:18 | x | AST only |
|
||||
| simple.cpp:136:21:136:28 | & ... | AST only |
|
||||
| simple.cpp:143:23:143:30 | & ... | AST only |
|
||||
| simple.cpp:144:23:144:30 | & ... | AST only |
|
||||
| simple.cpp:167:17:167:17 | f | AST only |
|
||||
| simple.cpp:168:12:168:23 | & ... | AST only |
|
||||
| struct_init.c:15:8:15:9 | ab | AST only |
|
||||
| struct_init.c:15:12:15:12 | a | AST only |
|
||||
| struct_init.c:16:8:16:9 | ab | AST only |
|
||||
@@ -342,6 +327,5 @@
|
||||
| struct_init.c:34:14:34:22 | pointerAB | AST only |
|
||||
| struct_init.c:34:25:34:25 | b | AST only |
|
||||
| struct_init.c:36:10:36:24 | & ... | AST only |
|
||||
| struct_init.c:36:11:36:15 | outer | AST only |
|
||||
| struct_init.c:46:10:46:14 | outer | AST only |
|
||||
| struct_init.c:46:16:46:24 | pointerAB | AST only |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -23,15 +23,38 @@
|
||||
| aliasing.cpp:54:3:54:4 | s2 |
|
||||
| aliasing.cpp:60:3:60:4 | s2 |
|
||||
| aliasing.cpp:72:3:72:3 | s |
|
||||
| aliasing.cpp:78:11:78:11 | w |
|
||||
| aliasing.cpp:79:3:79:3 | s |
|
||||
| aliasing.cpp:85:10:85:10 | w |
|
||||
| aliasing.cpp:86:3:86:3 | s |
|
||||
| aliasing.cpp:92:3:92:3 | w |
|
||||
| aliasing.cpp:92:5:92:5 | s |
|
||||
| by_reference.cpp:12:5:12:5 | s |
|
||||
| by_reference.cpp:16:5:16:8 | this |
|
||||
| by_reference.cpp:84:3:84:7 | inner |
|
||||
| by_reference.cpp:88:3:88:7 | inner |
|
||||
| by_reference.cpp:102:22:102:26 | outer |
|
||||
| by_reference.cpp:104:16:104:20 | outer |
|
||||
| by_reference.cpp:106:22:106:27 | pouter |
|
||||
| by_reference.cpp:108:16:108:21 | pouter |
|
||||
| by_reference.cpp:122:21:122:25 | outer |
|
||||
| by_reference.cpp:124:15:124:19 | outer |
|
||||
| by_reference.cpp:126:21:126:26 | pouter |
|
||||
| by_reference.cpp:128:15:128:20 | pouter |
|
||||
| complex.cpp:11:22:11:23 | this |
|
||||
| complex.cpp:12:22:12:23 | this |
|
||||
| complex.cpp:51:8:51:8 | b |
|
||||
| complex.cpp:51:10:51:14 | inner |
|
||||
| complex.cpp:52:8:52:8 | b |
|
||||
| complex.cpp:52:10:52:14 | inner |
|
||||
| complex.cpp:62:3:62:4 | b1 |
|
||||
| complex.cpp:62:6:62:10 | inner |
|
||||
| complex.cpp:63:3:63:4 | b2 |
|
||||
| complex.cpp:63:6:63:10 | inner |
|
||||
| complex.cpp:64:3:64:4 | b3 |
|
||||
| complex.cpp:64:6:64:10 | inner |
|
||||
| complex.cpp:65:3:65:4 | b3 |
|
||||
| complex.cpp:65:6:65:10 | inner |
|
||||
| constructors.cpp:20:24:20:25 | this |
|
||||
| constructors.cpp:21:24:21:25 | this |
|
||||
| qualifiers.cpp:9:30:9:33 | this |
|
||||
@@ -41,3 +64,16 @@
|
||||
| simple.cpp:21:24:21:25 | this |
|
||||
| simple.cpp:65:5:65:5 | a |
|
||||
| simple.cpp:83:9:83:10 | f2 |
|
||||
| simple.cpp:83:9:83:10 | this |
|
||||
| simple.cpp:105:5:105:6 | d2 |
|
||||
| simple.cpp:105:9:105:12 | d1_2 |
|
||||
| simple.cpp:122:5:122:6 | d3 |
|
||||
| simple.cpp:122:8:122:11 | d2_1 |
|
||||
| simple.cpp:122:13:122:16 | d1_1 |
|
||||
| simple.cpp:136:22:136:23 | d3 |
|
||||
| simple.cpp:143:24:143:25 | d3 |
|
||||
| simple.cpp:144:24:144:25 | d3 |
|
||||
| simple.cpp:167:5:167:9 | outer |
|
||||
| simple.cpp:167:11:167:15 | inner |
|
||||
| simple.cpp:168:13:168:17 | outer |
|
||||
| struct_init.c:36:11:36:15 | outer |
|
||||
|
||||
@@ -363,6 +363,24 @@
|
||||
| simple.cpp:83:9:83:10 | this |
|
||||
| simple.cpp:83:12:83:13 | f1 |
|
||||
| simple.cpp:84:14:84:20 | this |
|
||||
| simple.cpp:105:5:105:6 | d2 |
|
||||
| simple.cpp:105:9:105:12 | d1_2 |
|
||||
| simple.cpp:105:14:105:14 | y |
|
||||
| simple.cpp:122:5:122:6 | d3 |
|
||||
| simple.cpp:122:8:122:11 | d2_1 |
|
||||
| simple.cpp:122:13:122:16 | d1_1 |
|
||||
| simple.cpp:122:18:122:18 | x |
|
||||
| simple.cpp:136:21:136:28 | & ... |
|
||||
| simple.cpp:136:22:136:23 | d3 |
|
||||
| simple.cpp:143:23:143:30 | & ... |
|
||||
| simple.cpp:143:24:143:25 | d3 |
|
||||
| simple.cpp:144:23:144:30 | & ... |
|
||||
| simple.cpp:144:24:144:25 | d3 |
|
||||
| simple.cpp:167:5:167:9 | outer |
|
||||
| simple.cpp:167:11:167:15 | inner |
|
||||
| simple.cpp:167:17:167:17 | f |
|
||||
| simple.cpp:168:12:168:23 | & ... |
|
||||
| simple.cpp:168:13:168:17 | outer |
|
||||
| struct_init.c:15:8:15:9 | ab |
|
||||
| struct_init.c:15:12:15:12 | a |
|
||||
| struct_init.c:16:8:16:9 | ab |
|
||||
|
||||
@@ -332,6 +332,48 @@ edges
|
||||
| simple.cpp:83:9:83:28 | ... = ... | simple.cpp:83:9:83:10 | f2 [post update] [f1] |
|
||||
| simple.cpp:83:17:83:26 | call to user_input | simple.cpp:83:9:83:28 | ... = ... |
|
||||
| simple.cpp:84:14:84:20 | this [f2, f1] | simple.cpp:84:14:84:20 | call to getf2f1 |
|
||||
| simple.cpp:108:30:108:31 | d2 [d1_2, y] | simple.cpp:111:10:111:11 | d2 [d1_2, y] |
|
||||
| simple.cpp:111:10:111:11 | d2 [d1_2, y] | simple.cpp:111:13:111:16 | d1_2 [y] |
|
||||
| simple.cpp:111:13:111:16 | d1_2 [y] | simple.cpp:111:18:111:18 | y |
|
||||
| simple.cpp:114:37:114:38 | d2 [d1_2, y] | simple.cpp:117:10:117:11 | d2 [d1_2, y] |
|
||||
| simple.cpp:117:10:117:11 | d2 [d1_2, y] | simple.cpp:117:14:117:17 | d1_2 [y] |
|
||||
| simple.cpp:117:14:117:17 | d1_2 [y] | simple.cpp:117:19:117:19 | y |
|
||||
| simple.cpp:122:5:122:6 | d3 [post update] [d2_1, d1_1, ... (3)] | simple.cpp:123:24:123:25 | d3 [d2_1, d1_1, ... (3)] |
|
||||
| simple.cpp:122:5:122:33 | ... = ... | simple.cpp:122:13:122:16 | d1_1 [post update] [x] |
|
||||
| simple.cpp:122:8:122:11 | d2_1 [post update] [d1_1, x] | simple.cpp:122:5:122:6 | d3 [post update] [d2_1, d1_1, ... (3)] |
|
||||
| simple.cpp:122:13:122:16 | d1_1 [post update] [x] | simple.cpp:122:8:122:11 | d2_1 [post update] [d1_1, x] |
|
||||
| simple.cpp:122:22:122:31 | call to user_input | simple.cpp:122:5:122:33 | ... = ... |
|
||||
| simple.cpp:123:24:123:25 | d3 [d2_1, d1_1, ... (3)] | simple.cpp:123:27:123:30 | d2_1 [d1_1, x] |
|
||||
| simple.cpp:123:27:123:30 | d2_1 [d1_1, x] | simple.cpp:124:10:124:13 | d2_1 [d1_1, x] |
|
||||
| simple.cpp:123:27:123:30 | d2_1 [d1_1, x] | simple.cpp:129:25:129:28 | d2_1 [d1_1, x] |
|
||||
| simple.cpp:124:10:124:13 | d2_1 [d1_1, x] | simple.cpp:124:15:124:18 | d1_1 [x] |
|
||||
| simple.cpp:124:15:124:18 | d1_1 [x] | simple.cpp:124:20:124:20 | x |
|
||||
| simple.cpp:129:25:129:28 | d2_1 [d1_1, x] | simple.cpp:129:30:129:33 | d1_1 [x] |
|
||||
| simple.cpp:129:30:129:33 | d1_1 [x] | simple.cpp:130:10:130:12 | pd1 [x] |
|
||||
| simple.cpp:130:10:130:12 | pd1 [x] | simple.cpp:130:15:130:15 | x |
|
||||
| simple.cpp:136:21:136:28 | ref arg & ... [d1_2, y] | simple.cpp:136:25:136:28 | d2_1 [inner post update] [d1_2, y] |
|
||||
| simple.cpp:136:22:136:23 | d3 [post update] [d2_1, d1_2, ... (3)] | simple.cpp:139:10:139:11 | d3 [d2_1, d1_2, ... (3)] |
|
||||
| simple.cpp:136:22:136:23 | d3 [post update] [d2_1, d1_2, ... (3)] | simple.cpp:141:17:141:18 | d3 [d2_1, d1_2, ... (3)] |
|
||||
| simple.cpp:136:22:136:23 | d3 [post update] [d2_1, d1_2, ... (3)] | simple.cpp:143:24:143:25 | d3 [d2_1, d1_2, ... (3)] |
|
||||
| simple.cpp:136:25:136:28 | d2_1 [inner post update] [d1_2, y] | simple.cpp:136:22:136:23 | d3 [post update] [d2_1, d1_2, ... (3)] |
|
||||
| simple.cpp:136:31:136:40 | call to user_input | simple.cpp:136:21:136:28 | ref arg & ... [d1_2, y] |
|
||||
| simple.cpp:139:10:139:11 | d3 [d2_1, d1_2, ... (3)] | simple.cpp:139:13:139:16 | d2_1 [d1_2, y] |
|
||||
| simple.cpp:139:13:139:16 | d2_1 [d1_2, y] | simple.cpp:139:18:139:21 | d1_2 [y] |
|
||||
| simple.cpp:139:18:139:21 | d1_2 [y] | simple.cpp:139:23:139:23 | y |
|
||||
| simple.cpp:141:17:141:18 | d3 [d2_1, d1_2, ... (3)] | simple.cpp:141:20:141:23 | d2_1 [d1_2, y] |
|
||||
| simple.cpp:141:20:141:23 | d2_1 [d1_2, y] | simple.cpp:108:30:108:31 | d2 [d1_2, y] |
|
||||
| simple.cpp:143:23:143:30 | & ... [d1_2, y] | simple.cpp:114:37:114:38 | d2 [d1_2, y] |
|
||||
| simple.cpp:143:24:143:25 | d3 [d2_1, d1_2, ... (3)] | simple.cpp:143:27:143:30 | d2_1 [d1_2, y] |
|
||||
| simple.cpp:143:27:143:30 | d2_1 [d1_2, y] | simple.cpp:143:23:143:30 | & ... [d1_2, y] |
|
||||
| simple.cpp:159:20:159:24 | inner [f] | simple.cpp:161:10:161:14 | inner [f] |
|
||||
| simple.cpp:161:10:161:14 | inner [f] | simple.cpp:161:17:161:17 | f |
|
||||
| simple.cpp:167:5:167:9 | outer [post update] [inner, f] | simple.cpp:168:13:168:17 | outer [inner, f] |
|
||||
| simple.cpp:167:5:167:32 | ... = ... | simple.cpp:167:11:167:15 | inner [post update] [f] |
|
||||
| simple.cpp:167:11:167:15 | inner [post update] [f] | simple.cpp:167:5:167:9 | outer [post update] [inner, f] |
|
||||
| simple.cpp:167:21:167:30 | call to user_input | simple.cpp:167:5:167:32 | ... = ... |
|
||||
| simple.cpp:168:12:168:23 | & ... [f] | simple.cpp:159:20:159:24 | inner [f] |
|
||||
| simple.cpp:168:13:168:17 | outer [inner, f] | simple.cpp:168:19:168:23 | inner [f] |
|
||||
| simple.cpp:168:19:168:23 | inner [f] | simple.cpp:168:12:168:23 | & ... [f] |
|
||||
| struct_init.c:14:24:14:25 | ab [a] | struct_init.c:15:8:15:9 | ab [a] |
|
||||
| struct_init.c:15:8:15:9 | ab [a] | struct_init.c:15:12:15:12 | a |
|
||||
| struct_init.c:20:17:20:36 | {...} [a] | struct_init.c:22:8:22:9 | ab [a] |
|
||||
@@ -732,6 +774,51 @@ nodes
|
||||
| simple.cpp:83:17:83:26 | call to user_input | semmle.label | call to user_input |
|
||||
| simple.cpp:84:14:84:20 | call to getf2f1 | semmle.label | call to getf2f1 |
|
||||
| simple.cpp:84:14:84:20 | this [f2, f1] | semmle.label | this [f2, f1] |
|
||||
| simple.cpp:108:30:108:31 | d2 [d1_2, y] | semmle.label | d2 [d1_2, y] |
|
||||
| simple.cpp:111:10:111:11 | d2 [d1_2, y] | semmle.label | d2 [d1_2, y] |
|
||||
| simple.cpp:111:13:111:16 | d1_2 [y] | semmle.label | d1_2 [y] |
|
||||
| simple.cpp:111:18:111:18 | y | semmle.label | y |
|
||||
| simple.cpp:114:37:114:38 | d2 [d1_2, y] | semmle.label | d2 [d1_2, y] |
|
||||
| simple.cpp:117:10:117:11 | d2 [d1_2, y] | semmle.label | d2 [d1_2, y] |
|
||||
| simple.cpp:117:14:117:17 | d1_2 [y] | semmle.label | d1_2 [y] |
|
||||
| simple.cpp:117:19:117:19 | y | semmle.label | y |
|
||||
| simple.cpp:122:5:122:6 | d3 [post update] [d2_1, d1_1, ... (3)] | semmle.label | d3 [post update] [d2_1, d1_1, ... (3)] |
|
||||
| simple.cpp:122:5:122:33 | ... = ... | semmle.label | ... = ... |
|
||||
| simple.cpp:122:8:122:11 | d2_1 [post update] [d1_1, x] | semmle.label | d2_1 [post update] [d1_1, x] |
|
||||
| simple.cpp:122:13:122:16 | d1_1 [post update] [x] | semmle.label | d1_1 [post update] [x] |
|
||||
| simple.cpp:122:22:122:31 | call to user_input | semmle.label | call to user_input |
|
||||
| simple.cpp:123:24:123:25 | d3 [d2_1, d1_1, ... (3)] | semmle.label | d3 [d2_1, d1_1, ... (3)] |
|
||||
| simple.cpp:123:27:123:30 | d2_1 [d1_1, x] | semmle.label | d2_1 [d1_1, x] |
|
||||
| simple.cpp:124:10:124:13 | d2_1 [d1_1, x] | semmle.label | d2_1 [d1_1, x] |
|
||||
| simple.cpp:124:15:124:18 | d1_1 [x] | semmle.label | d1_1 [x] |
|
||||
| simple.cpp:124:20:124:20 | x | semmle.label | x |
|
||||
| simple.cpp:129:25:129:28 | d2_1 [d1_1, x] | semmle.label | d2_1 [d1_1, x] |
|
||||
| simple.cpp:129:30:129:33 | d1_1 [x] | semmle.label | d1_1 [x] |
|
||||
| simple.cpp:130:10:130:12 | pd1 [x] | semmle.label | pd1 [x] |
|
||||
| simple.cpp:130:15:130:15 | x | semmle.label | x |
|
||||
| simple.cpp:136:21:136:28 | ref arg & ... [d1_2, y] | semmle.label | ref arg & ... [d1_2, y] |
|
||||
| simple.cpp:136:22:136:23 | d3 [post update] [d2_1, d1_2, ... (3)] | semmle.label | d3 [post update] [d2_1, d1_2, ... (3)] |
|
||||
| simple.cpp:136:25:136:28 | d2_1 [inner post update] [d1_2, y] | semmle.label | d2_1 [inner post update] [d1_2, y] |
|
||||
| simple.cpp:136:31:136:40 | call to user_input | semmle.label | call to user_input |
|
||||
| simple.cpp:139:10:139:11 | d3 [d2_1, d1_2, ... (3)] | semmle.label | d3 [d2_1, d1_2, ... (3)] |
|
||||
| simple.cpp:139:13:139:16 | d2_1 [d1_2, y] | semmle.label | d2_1 [d1_2, y] |
|
||||
| simple.cpp:139:18:139:21 | d1_2 [y] | semmle.label | d1_2 [y] |
|
||||
| simple.cpp:139:23:139:23 | y | semmle.label | y |
|
||||
| simple.cpp:141:17:141:18 | d3 [d2_1, d1_2, ... (3)] | semmle.label | d3 [d2_1, d1_2, ... (3)] |
|
||||
| simple.cpp:141:20:141:23 | d2_1 [d1_2, y] | semmle.label | d2_1 [d1_2, y] |
|
||||
| simple.cpp:143:23:143:30 | & ... [d1_2, y] | semmle.label | & ... [d1_2, y] |
|
||||
| simple.cpp:143:24:143:25 | d3 [d2_1, d1_2, ... (3)] | semmle.label | d3 [d2_1, d1_2, ... (3)] |
|
||||
| simple.cpp:143:27:143:30 | d2_1 [d1_2, y] | semmle.label | d2_1 [d1_2, y] |
|
||||
| simple.cpp:159:20:159:24 | inner [f] | semmle.label | inner [f] |
|
||||
| simple.cpp:161:10:161:14 | inner [f] | semmle.label | inner [f] |
|
||||
| simple.cpp:161:17:161:17 | f | semmle.label | f |
|
||||
| simple.cpp:167:5:167:9 | outer [post update] [inner, f] | semmle.label | outer [post update] [inner, f] |
|
||||
| simple.cpp:167:5:167:32 | ... = ... | semmle.label | ... = ... |
|
||||
| simple.cpp:167:11:167:15 | inner [post update] [f] | semmle.label | inner [post update] [f] |
|
||||
| simple.cpp:167:21:167:30 | call to user_input | semmle.label | call to user_input |
|
||||
| simple.cpp:168:12:168:23 | & ... [f] | semmle.label | & ... [f] |
|
||||
| simple.cpp:168:13:168:17 | outer [inner, f] | semmle.label | outer [inner, f] |
|
||||
| simple.cpp:168:19:168:23 | inner [f] | semmle.label | inner [f] |
|
||||
| struct_init.c:14:24:14:25 | ab [a] | semmle.label | ab [a] |
|
||||
| struct_init.c:15:8:15:9 | ab [a] | semmle.label | ab [a] |
|
||||
| struct_init.c:15:12:15:12 | a | semmle.label | a |
|
||||
@@ -830,6 +917,12 @@ nodes
|
||||
| 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 |
|
||||
| simple.cpp:117:19:117:19 | y | simple.cpp:136:31:136:40 | call to user_input | simple.cpp:117:19:117:19 | y | y flows from $@ | simple.cpp:136:31:136:40 | call to user_input | call to user_input |
|
||||
| simple.cpp:124:20:124:20 | x | simple.cpp:122:22:122:31 | call to user_input | simple.cpp:124:20:124:20 | x | x flows from $@ | simple.cpp:122:22:122:31 | call to user_input | call to user_input |
|
||||
| simple.cpp:130:15:130:15 | x | simple.cpp:122:22:122:31 | call to user_input | simple.cpp:130:15:130:15 | x | x flows from $@ | simple.cpp:122:22:122:31 | call to user_input | call to user_input |
|
||||
| simple.cpp:139:23:139:23 | y | simple.cpp:136:31:136:40 | call to user_input | simple.cpp:139:23:139:23 | y | y flows from $@ | simple.cpp:136:31:136:40 | call to user_input | call to user_input |
|
||||
| simple.cpp:161:17:161:17 | f | simple.cpp:167:21:167:30 | call to user_input | simple.cpp:161:17:161:17 | f | f flows from $@ | simple.cpp:167:21:167:30 | call to user_input | call to user_input |
|
||||
| struct_init.c:15:12:15:12 | a | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:20:20:20:29 | call to user_input | call to user_input |
|
||||
| struct_init.c:15:12:15:12 | a | struct_init.c:27:7:27:16 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:27:7:27:16 | call to user_input | call to user_input |
|
||||
| struct_init.c:15:12:15:12 | a | struct_init.c:40:20:40:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:40:20:40:29 | call to user_input | call to user_input |
|
||||
|
||||
@@ -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()
|
||||
@@ -85,4 +85,87 @@ struct C2
|
||||
}
|
||||
};
|
||||
|
||||
struct DeepStruct1 {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
struct DeepStruct2 {
|
||||
DeepStruct1 d1_1;
|
||||
DeepStruct1 d1_2;
|
||||
};
|
||||
|
||||
struct DeepStruct3 {
|
||||
DeepStruct2 d2_1;
|
||||
DeepStruct2 d2_2;
|
||||
DeepStruct1 d1_1;
|
||||
};
|
||||
|
||||
void write_to_d1_2_y(DeepStruct2* d2, int val) {
|
||||
d2->d1_2.y = val;
|
||||
}
|
||||
|
||||
void read_from_y(DeepStruct2 d2) {
|
||||
sink(d2.d1_1.y);
|
||||
|
||||
sink(d2.d1_2.y); //$ast,ir
|
||||
}
|
||||
|
||||
void read_from_y_deref(DeepStruct2* d2) {
|
||||
sink(d2->d1_1.y);
|
||||
|
||||
sink(d2->d1_2.y); //$ast,ir
|
||||
}
|
||||
|
||||
void test_deep_structs() {
|
||||
DeepStruct3 d3;
|
||||
d3.d2_1.d1_1.x = user_input();
|
||||
DeepStruct2 d2_1 = d3.d2_1;
|
||||
sink(d2_1.d1_1.x); //$ast,ir
|
||||
sink(d2_1.d1_1.y);
|
||||
|
||||
sink(d2_1.d1_2.x);
|
||||
|
||||
DeepStruct1* pd1 = &d2_1.d1_1;
|
||||
sink(pd1->x); //$ast,ir
|
||||
}
|
||||
|
||||
void test_deep_structs_setter() {
|
||||
DeepStruct3 d3;
|
||||
|
||||
write_to_d1_2_y(&d3.d2_1, user_input());
|
||||
|
||||
sink(d3.d2_1.d1_1.y);
|
||||
sink(d3.d2_1.d1_2.y); //$ast,ir
|
||||
|
||||
read_from_y(d3.d2_1);
|
||||
read_from_y(d3.d2_2);
|
||||
read_from_y_deref(&d3.d2_1);
|
||||
read_from_y_deref(&d3.d2_2);
|
||||
}
|
||||
|
||||
struct Inner
|
||||
{
|
||||
int f;
|
||||
int g;
|
||||
};
|
||||
|
||||
struct Outer
|
||||
{
|
||||
Inner inner;
|
||||
int h;
|
||||
};
|
||||
|
||||
void read_f(Inner *inner)
|
||||
{
|
||||
sink(inner->f); //$ast,ir
|
||||
}
|
||||
|
||||
void test()
|
||||
{
|
||||
Outer outer;
|
||||
outer.inner.f = user_input();
|
||||
read_f(&outer.inner);
|
||||
}
|
||||
|
||||
} // namespace Simple
|
||||
|
||||
@@ -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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user