Merge branch 'master' into js-team-sprint-merge2

This commit is contained in:
Asger Feldthaus
2020-06-23 00:18:09 +01:00
515 changed files with 20302 additions and 10514 deletions

4
.github/codeql/codeql-config.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
name: "CodeQL config"
queries:
- uses: security-and-quality

52
.github/workflows/codeql-analysis.yml vendored Normal file
View 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

View File

@@ -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.

View File

@@ -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. |

View File

@@ -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",

View File

@@ -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
)
}

View File

@@ -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

View File

@@ -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:

View 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

View 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

View 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

View File

@@ -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

View File

@@ -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:

View 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" }
}

View File

@@ -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`
}
}

View File

@@ -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) {

View File

@@ -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(

View File

@@ -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(

View File

@@ -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(

View File

@@ -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(

View File

@@ -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 }
}

View File

@@ -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(

View File

@@ -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() }

View File

@@ -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.
*

View File

@@ -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(

View File

@@ -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(

View File

@@ -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(

View File

@@ -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(

View File

@@ -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 }
}

View File

@@ -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() }

View File

@@ -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

View File

@@ -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() {

View File

@@ -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)
)
}
}

View File

@@ -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.
*/

View File

@@ -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() }
}

View File

@@ -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 }

View File

@@ -0,0 +1 @@
import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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() }
}

View File

@@ -0,0 +1,2 @@
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction

View File

@@ -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 {

View File

@@ -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)
}
}

View File

@@ -0,0 +1,2 @@
import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.implementation.Opcode as Opcode

View File

@@ -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

View File

@@ -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)
)
}
}

View File

@@ -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.
*/

View File

@@ -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() }
}

View File

@@ -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 }

View File

@@ -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

View File

@@ -0,0 +1 @@
import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase

View File

@@ -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

View File

@@ -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() }
/**

View File

@@ -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

View File

@@ -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

View File

@@ -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)
)
}
}

View File

@@ -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.
*/

View File

@@ -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() }
}

View File

@@ -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 }

View File

@@ -0,0 +1 @@
import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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()
/**

View File

@@ -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.

View File

@@ -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

View 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]
}

View File

@@ -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 *)... |

View File

@@ -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 |

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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()
)
}

View 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() }
}

View File

@@ -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() {

View File

@@ -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()

View File

@@ -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()

View File

@@ -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

View 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 |

View 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

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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

View File

@@ -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 | |

View File

@@ -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
}

View File

@@ -0,0 +1,5 @@
namespace std
{
template <class T>
constexpr void swap(T &a, T &b);
}

View 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
}

View 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
}

View File

@@ -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);
}
}

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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

View File

@@ -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