diff --git a/.codeqlmanifest.json b/.codeqlmanifest.json
index 30efc842b00f..81d370e793d2 100644
--- a/.codeqlmanifest.json
+++ b/.codeqlmanifest.json
@@ -1,5 +1,6 @@
{ "provide": [ "*/ql/src/qlpack.yml",
"*/ql/test/qlpack.yml",
+ "cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml",
"*/ql/examples/qlpack.yml",
"*/upgrades/qlpack.yml",
"misc/legacy-support/*/qlpack.yml",
diff --git a/.github/workflows/check-change-note.yml b/.github/workflows/check-change-note.yml
index 0b90ffbc6686..8c036c2f1b33 100644
--- a/.github/workflows/check-change-note.yml
+++ b/.github/workflows/check-change-note.yml
@@ -19,5 +19,5 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
- gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate |
- jq 'any(.[].filename ; test("/change-notes/.*[.]md$"))' --exit-status
+ gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq 'any(.[].filename ; test("/change-notes/.*[.]md$"))' |
+ grep true -c
diff --git a/.github/workflows/close-stale.yml b/.github/workflows/close-stale.yml
index 746a13317b22..c63a6be690c2 100644
--- a/.github/workflows/close-stale.yml
+++ b/.github/workflows/close-stale.yml
@@ -15,16 +15,16 @@ jobs:
- uses: actions/stale@v3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- stale-issue-message: 'This issue is stale because it has been open 14 days with no activity. Comment or remove the `stale` label in order to avoid having this issue closed in 7 days.'
+ stale-issue-message: 'This issue is stale because it has been open 14 days with no activity. Comment or remove the `Stale` label in order to avoid having this issue closed in 7 days.'
close-issue-message: 'This issue was closed because it has been inactive for 7 days.'
days-before-stale: 14
days-before-close: 7
- only-labels: question
-
+ only-labels: awaiting-response
+
# do not mark PRs as stale
days-before-pr-stale: -1
days-before-pr-close: -1
-
+
# Uncomment for dry-run
# debug-only: true
# operations-per-run: 1000
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index b86009ef6da9..87d6632d03e7 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -19,13 +19,18 @@ jobs:
runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ security-events: write
+ pull-requests: read
+
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@v1
+ uses: github/codeql-action/init@main
# Override language selection by uncommenting this and choosing your languages
with:
languages: csharp
@@ -34,7 +39,7 @@ jobs:
# 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
+ uses: github/codeql-action/autobuild@main
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -48,4 +53,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v1
+ uses: github/codeql-action/analyze@main
diff --git a/.github/workflows/csv-coverage-pr-artifacts.yml b/.github/workflows/csv-coverage-pr-artifacts.yml
new file mode 100644
index 000000000000..201eea5c0734
--- /dev/null
+++ b/.github/workflows/csv-coverage-pr-artifacts.yml
@@ -0,0 +1,97 @@
+name: Check framework coverage changes
+
+on:
+ pull_request:
+ paths:
+ - '.github/workflows/csv-coverage-pr-comment.yml'
+ - '*/ql/src/**/*.ql'
+ - '*/ql/src/**/*.qll'
+ - 'misc/scripts/library-coverage/*.py'
+ # input data files
+ - '*/documentation/library-coverage/cwe-sink.csv'
+ - '*/documentation/library-coverage/frameworks.csv'
+ branches:
+ - main
+ - 'rc/*'
+
+jobs:
+ generate:
+ name: Generate framework coverage artifacts
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Dump GitHub context
+ env:
+ GITHUB_CONTEXT: ${{ toJSON(github.event) }}
+ run: echo "$GITHUB_CONTEXT"
+ - name: Clone self (github/codeql) - MERGE
+ uses: actions/checkout@v2
+ with:
+ path: merge
+ - name: Clone self (github/codeql) - BASE
+ uses: actions/checkout@v2
+ with:
+ fetch-depth: 2
+ path: base
+ - run: |
+ git checkout HEAD^1
+ git log -1 --format='%H'
+ working-directory: base
+ - name: Set up Python 3.8
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.8
+ - name: Download CodeQL CLI
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ gh release download --repo "github/codeql-cli-binaries" --pattern "codeql-linux64.zip"
+ - name: Unzip CodeQL CLI
+ run: unzip -d codeql-cli codeql-linux64.zip
+ - name: Generate CSV files on merge commit of the PR
+ run: |
+ echo "Running generator on merge"
+ PATH="$PATH:codeql-cli/codeql" python merge/misc/scripts/library-coverage/generate-report.py ci merge merge
+ mkdir out_merge
+ cp framework-coverage-*.csv out_merge/
+ cp framework-coverage-*.rst out_merge/
+ - name: Generate CSV files on base commit of the PR
+ run: |
+ echo "Running generator on base"
+ PATH="$PATH:codeql-cli/codeql" python base/misc/scripts/library-coverage/generate-report.py ci base base
+ mkdir out_base
+ cp framework-coverage-*.csv out_base/
+ cp framework-coverage-*.rst out_base/
+ - name: Generate diff of coverage reports
+ run: |
+ python base/misc/scripts/library-coverage/compare-folders.py out_base out_merge comparison.md
+ - name: Upload CSV package list
+ uses: actions/upload-artifact@v2
+ with:
+ name: csv-framework-coverage-merge
+ path: |
+ out_merge/framework-coverage-*.csv
+ out_merge/framework-coverage-*.rst
+ - name: Upload CSV package list
+ uses: actions/upload-artifact@v2
+ with:
+ name: csv-framework-coverage-base
+ path: |
+ out_base/framework-coverage-*.csv
+ out_base/framework-coverage-*.rst
+ - name: Upload comparison results
+ uses: actions/upload-artifact@v2
+ with:
+ name: comparison
+ path: |
+ comparison.md
+ - name: Save PR number
+ run: |
+ mkdir -p pr
+ echo ${{ github.event.pull_request.number }} > pr/NR
+ - name: Upload PR number
+ uses: actions/upload-artifact@v2
+ with:
+ name: pr
+ path: pr/
diff --git a/.github/workflows/csv-coverage-pr-comment.yml b/.github/workflows/csv-coverage-pr-comment.yml
new file mode 100644
index 000000000000..399fee33e5e1
--- /dev/null
+++ b/.github/workflows/csv-coverage-pr-comment.yml
@@ -0,0 +1,34 @@
+name: Comment on PR with framework coverage changes
+
+on:
+ workflow_run:
+ workflows: ["Check framework coverage changes"]
+ types:
+ - completed
+
+jobs:
+ check:
+ name: Check framework coverage differences and comment
+ runs-on: ubuntu-latest
+ if: >
+ ${{ github.event.workflow_run.event == 'pull_request' &&
+ github.event.workflow_run.conclusion == 'success' }}
+
+ steps:
+ - name: Dump GitHub context
+ env:
+ GITHUB_CONTEXT: ${{ toJSON(github.event) }}
+ run: echo "$GITHUB_CONTEXT"
+ - name: Clone self (github/codeql)
+ uses: actions/checkout@v2
+ - name: Set up Python 3.8
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.8
+
+ - name: Check coverage difference file and comment
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ RUN_ID: ${{ github.event.workflow_run.id }}
+ run: |
+ python misc/scripts/library-coverage/comment-pr.py "$GITHUB_REPOSITORY" "$RUN_ID"
diff --git a/.github/workflows/csv-coverage-timeseries.yml b/.github/workflows/csv-coverage-timeseries.yml
new file mode 100644
index 000000000000..e7c01623e04f
--- /dev/null
+++ b/.github/workflows/csv-coverage-timeseries.yml
@@ -0,0 +1,42 @@
+name: Build framework coverage timeseries reports
+
+on:
+ workflow_dispatch:
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Clone self (github/codeql)
+ uses: actions/checkout@v2
+ with:
+ path: script
+ - name: Clone self (github/codeql) for analysis
+ uses: actions/checkout@v2
+ with:
+ path: codeqlModels
+ fetch-depth: 0
+ - name: Set up Python 3.8
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.8
+ - name: Download CodeQL CLI
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ gh release download --repo "github/codeql-cli-binaries" --pattern "codeql-linux64.zip"
+ - name: Unzip CodeQL CLI
+ run: unzip -d codeql-cli codeql-linux64.zip
+ - name: Build modeled package list
+ run: |
+ CLI=$(realpath "codeql-cli/codeql")
+ echo $CLI
+ PATH="$PATH:$CLI" python script/misc/scripts/library-coverage/generate-timeseries.py codeqlModels
+ - name: Upload timeseries CSV
+ uses: actions/upload-artifact@v2
+ with:
+ name: framework-coverage-timeseries
+ path: framework-coverage-timeseries-*.csv
+
diff --git a/.github/workflows/csv-coverage-update.yml b/.github/workflows/csv-coverage-update.yml
new file mode 100644
index 000000000000..10834bdd36a9
--- /dev/null
+++ b/.github/workflows/csv-coverage-update.yml
@@ -0,0 +1,44 @@
+name: Update framework coverage reports
+
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: "0 0 * * *"
+
+jobs:
+ update:
+ name: Update framework coverage report
+ if: github.event.repository.fork == false
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Dump GitHub context
+ env:
+ GITHUB_CONTEXT: ${{ toJSON(github.event) }}
+ run: echo "$GITHUB_CONTEXT"
+ - name: Clone self (github/codeql)
+ uses: actions/checkout@v2
+ with:
+ path: ql
+ fetch-depth: 0
+ - name: Set up Python 3.8
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.8
+ - name: Download CodeQL CLI
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ gh release download --repo "github/codeql-cli-binaries" --pattern "codeql-linux64.zip"
+ - name: Unzip CodeQL CLI
+ run: unzip -d codeql-cli codeql-linux64.zip
+
+ - name: Generate coverage files
+ run: |
+ PATH="$PATH:codeql-cli/codeql" python ql/misc/scripts/library-coverage/generate-report.py ci ql ql
+
+ - name: Create pull request with changes
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ python ql/misc/scripts/library-coverage/create-pr.py ql "$GITHUB_REPOSITORY"
diff --git a/.github/workflows/csv-coverage.yml b/.github/workflows/csv-coverage.yml
new file mode 100644
index 000000000000..c7ab92eb0f31
--- /dev/null
+++ b/.github/workflows/csv-coverage.yml
@@ -0,0 +1,49 @@
+name: Build framework coverage reports
+
+on:
+ workflow_dispatch:
+ inputs:
+ qlModelShaOverride:
+ description: 'github/codeql repo SHA used for looking up the CSV models'
+ required: false
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Clone self (github/codeql)
+ uses: actions/checkout@v2
+ with:
+ path: script
+ - name: Clone self (github/codeql) for analysis
+ uses: actions/checkout@v2
+ with:
+ path: codeqlModels
+ ref: ${{ github.event.inputs.qlModelShaOverride || github.ref }}
+ - name: Set up Python 3.8
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.8
+ - name: Download CodeQL CLI
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ gh release download --repo "github/codeql-cli-binaries" --pattern "codeql-linux64.zip"
+ - name: Unzip CodeQL CLI
+ run: unzip -d codeql-cli codeql-linux64.zip
+ - name: Build modeled package list
+ run: |
+ PATH="$PATH:codeql-cli/codeql" python script/misc/scripts/library-coverage/generate-report.py ci codeqlModels script
+ - name: Upload CSV package list
+ uses: actions/upload-artifact@v2
+ with:
+ name: framework-coverage-csv
+ path: framework-coverage-*.csv
+ - name: Upload RST package list
+ uses: actions/upload-artifact@v2
+ with:
+ name: framework-coverage-rst
+ path: framework-coverage-*.rst
+
diff --git a/config/identical-files.json b/config/identical-files.json
index 3916e95a0cf0..582e4c7b6dc3 100644
--- a/config/identical-files.json
+++ b/config/identical-files.json
@@ -5,6 +5,7 @@
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll",
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll",
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll",
+ "java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll",
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll",
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll",
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll",
@@ -249,6 +250,10 @@
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll",
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll"
],
+ "SSA PrintAliasAnalysis": [
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintAliasAnalysis.qll",
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasAnalysis.qll"
+ ],
"C++ SSA AliasAnalysisImports": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysisImports.qll"
@@ -438,6 +443,10 @@
],
"CryptoAlgorithms Python/JS": [
"javascript/ql/src/semmle/javascript/security/CryptoAlgorithms.qll",
- "python/ql/src/semmle/crypto/Crypto.qll"
+ "python/ql/src/semmle/python/concepts/CryptoAlgorithms.qll"
+ ],
+ "SensitiveDataHeuristics Python/JS": [
+ "javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll",
+ "python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll"
]
-}
\ No newline at end of file
+}
diff --git a/cpp/change-notes/2021-03-11-overflow-abs.md b/cpp/change-notes/2021-03-11-overflow-abs.md
new file mode 100644
index 000000000000..66854412f723
--- /dev/null
+++ b/cpp/change-notes/2021-03-11-overflow-abs.md
@@ -0,0 +1,2 @@
+lgtm
+* The `cpp/tainted-arithmetic`, `cpp/arithmetic-with-extreme-values`, and `cpp/uncontrolled-arithmetic` queries now recognize more functions as returning the absolute value of their input. As a result, they produce fewer false positives.
diff --git a/cpp/change-notes/2021-04-09-unsigned-difference-expression-compared-zero.md b/cpp/change-notes/2021-04-09-unsigned-difference-expression-compared-zero.md
new file mode 100644
index 000000000000..3297dfce9a97
--- /dev/null
+++ b/cpp/change-notes/2021-04-09-unsigned-difference-expression-compared-zero.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* The 'Unsigned difference expression compared to zero' (cpp/unsigned-difference-expression-compared-zero) query has been improved to produce fewer false positive results.
\ No newline at end of file
diff --git a/cpp/change-notes/2021-04-13-arithmetic-queries.md b/cpp/change-notes/2021-04-13-arithmetic-queries.md
new file mode 100644
index 000000000000..4d0f8833adc9
--- /dev/null
+++ b/cpp/change-notes/2021-04-13-arithmetic-queries.md
@@ -0,0 +1,2 @@
+lgtm
+* The queries cpp/tainted-arithmetic, cpp/uncontrolled-arithmetic, and cpp/arithmetic-with-extreme-values have been improved to produce fewer false positives.
diff --git a/cpp/change-notes/2021-04-21-return-stack-allocated-object.md b/cpp/change-notes/2021-04-21-return-stack-allocated-object.md
new file mode 100644
index 000000000000..1876f4cf5f7a
--- /dev/null
+++ b/cpp/change-notes/2021-04-21-return-stack-allocated-object.md
@@ -0,0 +1,2 @@
+codescanning
+* The 'Pointer to stack object used as return value' (cpp/return-stack-allocated-object) query has been deprecated, and any uses should be replaced with `Returning stack-allocated memory` (cpp/return-stack-allocated-memory).
\ No newline at end of file
diff --git a/cpp/change-notes/2021-04-26-more-sound-expr-might-overflow.md b/cpp/change-notes/2021-04-26-more-sound-expr-might-overflow.md
new file mode 100644
index 000000000000..5a7b8414fade
--- /dev/null
+++ b/cpp/change-notes/2021-04-26-more-sound-expr-might-overflow.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* The `exprMightOverflowPositively` and `exprMightOverflowNegatively` predicates from the `SimpleRangeAnalysis` library now recognize more expressions that might overflow.
diff --git a/cpp/change-notes/2021-05-10-comparison-with-wider-type.md b/cpp/change-notes/2021-05-10-comparison-with-wider-type.md
new file mode 100644
index 000000000000..f06895a3c1aa
--- /dev/null
+++ b/cpp/change-notes/2021-05-10-comparison-with-wider-type.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* The 'Comparison with wider type' (cpp/comparison-with-wider-type) query has been improved to produce fewer false positives.
\ No newline at end of file
diff --git a/cpp/change-notes/2021-05-12-uncontrolled-arithmetic.md b/cpp/change-notes/2021-05-12-uncontrolled-arithmetic.md
new file mode 100644
index 000000000000..56fbc9a44cef
--- /dev/null
+++ b/cpp/change-notes/2021-05-12-uncontrolled-arithmetic.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* The query "Uncontrolled arithmetic" (`cpp/uncontrolled-arithmetic`) has been improved to produce fewer false positives.
diff --git a/cpp/change-notes/2021-05-14-uncontrolled-allocation-size.md b/cpp/change-notes/2021-05-14-uncontrolled-allocation-size.md
new file mode 100644
index 000000000000..6f0c9d6fa982
--- /dev/null
+++ b/cpp/change-notes/2021-05-14-uncontrolled-allocation-size.md
@@ -0,0 +1,2 @@
+lgtm
+* The "Tainted allocation size" query (cpp/uncontrolled-allocation-size) has been improved to produce fewer false positives.
diff --git a/cpp/change-notes/2021-05-18-static-buffer-overflow.md b/cpp/change-notes/2021-05-18-static-buffer-overflow.md
new file mode 100644
index 000000000000..f56040fe8aa9
--- /dev/null
+++ b/cpp/change-notes/2021-05-18-static-buffer-overflow.md
@@ -0,0 +1,2 @@
+lgtm
+* The "Static buffer overflow" query (cpp/static-buffer-overflow) has been improved to produce fewer false positives.
diff --git a/cpp/change-notes/2021-05-19-weak-cryptographic-algorithm.md b/cpp/change-notes/2021-05-19-weak-cryptographic-algorithm.md
new file mode 100644
index 000000000000..5e48ba166b7c
--- /dev/null
+++ b/cpp/change-notes/2021-05-19-weak-cryptographic-algorithm.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* The "Use of a broken or risky cryptographic algorithm" (`cpp/weak-cryptographic-algorithm`) query has been enhanced to reduce false positive results, and (rarely) find more true positive results.
diff --git a/cpp/change-notes/2021-05-20-incorrect-allocation-error-handling.md b/cpp/change-notes/2021-05-20-incorrect-allocation-error-handling.md
new file mode 100644
index 000000000000..f00856ed7110
--- /dev/null
+++ b/cpp/change-notes/2021-05-20-incorrect-allocation-error-handling.md
@@ -0,0 +1,2 @@
+lgtm
+* A new query (`cpp/incorrect-allocation-error-handling`) has been added. The query finds incorrect error-handling of calls to `operator new`. This query was originally [submitted as an experimental query by @ihsinme](https://github.com/github/codeql/pull/5010).
diff --git a/cpp/change-notes/2021-05-20-ref-qualifiers.md b/cpp/change-notes/2021-05-20-ref-qualifiers.md
new file mode 100644
index 000000000000..e4b394a21731
--- /dev/null
+++ b/cpp/change-notes/2021-05-20-ref-qualifiers.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* lvalue/rvalue ref qualifiers are now accessible via the new predicates on `MemberFunction`(`.isLValueRefQualified`, `.isRValueRefQualified`, and `isRefQualified`).
diff --git a/cpp/change-notes/2021-05-21-unsafe-strncat.md b/cpp/change-notes/2021-05-21-unsafe-strncat.md
new file mode 100644
index 000000000000..135b5278df40
--- /dev/null
+++ b/cpp/change-notes/2021-05-21-unsafe-strncat.md
@@ -0,0 +1,2 @@
+lgtm
+* The "Potentially unsafe call to strncat" query (cpp/unsafe-strncat) query has been improved to detect more cases of unsafe calls to `strncat`.
diff --git a/cpp/change-notes/2021-06-10-std-types.md b/cpp/change-notes/2021-06-10-std-types.md
new file mode 100644
index 000000000000..86577ce14a0e
--- /dev/null
+++ b/cpp/change-notes/2021-06-10-std-types.md
@@ -0,0 +1,4 @@
+lgtm,codescanning
+* Added definitions for types found in `cstdint`. Added types `FixedWidthIntegralType`, `MinimumWidthIntegralType`, `FastestMinimumWidthIntegralType`, and `MaximumWidthIntegralType` to describe types such as `int8_t`, `int_least8_t`, `int_fast8_t`, and `intmax_t` respectively.
+* Changed definition of `Intmax_t` and `Uintmax_t` to be part of the new type structure.
+* Added a type `FixedWidthEnumType` which describes enums based on a fixed-width integer type. For instance, `enum e: uint8_t = { a, b };`.
diff --git a/cpp/change-notes/2021-06-21-weak-cryptographic-algorithm.md b/cpp/change-notes/2021-06-21-weak-cryptographic-algorithm.md
new file mode 100644
index 000000000000..8a4bb065eb5c
--- /dev/null
+++ b/cpp/change-notes/2021-06-21-weak-cryptographic-algorithm.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* The "Use of a broken or risky cryptographic algorithm" (`cpp/weak-cryptographic-algorithm`) query has been further improved to reduce false positives and its `@precision` increased to `high`.
diff --git a/cpp/change-notes/2021-06-24-dataflow-implicit-reads.md b/cpp/change-notes/2021-06-24-dataflow-implicit-reads.md
new file mode 100644
index 000000000000..c96152ed05b3
--- /dev/null
+++ b/cpp/change-notes/2021-06-24-dataflow-implicit-reads.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* The DataFlow libraries have been augmented with support for `Configuration`-specific in-place read steps at, for example, sinks and custom taint steps. This means that it is now possible to specify sinks that accept flow with non-empty access paths.
diff --git a/cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.ql b/cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.ql
index 36024ddbc70d..c8bf38427730 100644
--- a/cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.ql
+++ b/cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.ql
@@ -5,6 +5,7 @@
* @kind problem
* @id cpp/offset-use-before-range-check
* @problem.severity warning
+ * @security-severity 8.2
* @precision medium
* @tags reliability
* security
diff --git a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.qhelp b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.qhelp
index 16fc75fc7ad8..aa97965996f2 100644
--- a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.qhelp
+++ b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.qhelp
@@ -39,7 +39,7 @@ then replace all the relevant occurrences in the code.
Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997).
- Chapter 5: Object Life Cycle, Rec 5.4 (PDF).
+ Chapter 5: Object Life Cycle, Rec 5.4 (PDF).
DCL06-C. Use meaningful symbolic constants to represent literal values
diff --git a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.qhelp b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.qhelp
index ca01aac69f02..19182fe5b19d 100644
--- a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.qhelp
+++ b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.qhelp
@@ -38,7 +38,7 @@ constant.
Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997).
- Chapter 5: Object Life Cycle, Rec 5.4 (PDF).
+ Chapter 5: Object Life Cycle, Rec 5.4 (PDF).
DCL06-C. Use meaningful symbolic constants to represent literal values
diff --git a/cpp/ql/src/Best Practices/SloppyGlobal.qhelp b/cpp/ql/src/Best Practices/SloppyGlobal.qhelp
index a6255cc6a9e2..48b36e6d4767 100644
--- a/cpp/ql/src/Best Practices/SloppyGlobal.qhelp
+++ b/cpp/ql/src/Best Practices/SloppyGlobal.qhelp
@@ -21,7 +21,7 @@ Review the purpose of the each global variable flagged by this rule and update e
Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997).
- Chapter 1: Naming, Rec 1.1 (PDF).
+ Chapter 1: Naming, Rec 1.1 (PDF).
Global variables.
diff --git a/cpp/ql/src/Best Practices/UseOfGoto.qhelp b/cpp/ql/src/Best Practices/UseOfGoto.qhelp
index dd5702aabe4f..cfceb144605b 100644
--- a/cpp/ql/src/Best Practices/UseOfGoto.qhelp
+++ b/cpp/ql/src/Best Practices/UseOfGoto.qhelp
@@ -45,7 +45,7 @@ this rule.
Mats Henricson and Erik Nyquist, Industrial Strength C++, Rule 4.6. Prentice Hall PTR, 1997.
- (PDF).
+ (PDF).
cplusplus.com: Control Structures.
diff --git a/cpp/ql/src/Critical/DescriptorMayNotBeClosed.ql b/cpp/ql/src/Critical/DescriptorMayNotBeClosed.ql
index 135b9a644d10..3ef487fbec29 100644
--- a/cpp/ql/src/Critical/DescriptorMayNotBeClosed.ql
+++ b/cpp/ql/src/Critical/DescriptorMayNotBeClosed.ql
@@ -4,6 +4,7 @@
* @kind problem
* @id cpp/descriptor-may-not-be-closed
* @problem.severity warning
+ * @security-severity 7.8
* @tags efficiency
* security
* external/cwe/cwe-775
diff --git a/cpp/ql/src/Critical/DescriptorNeverClosed.ql b/cpp/ql/src/Critical/DescriptorNeverClosed.ql
index ae50e6256020..85e41ad19280 100644
--- a/cpp/ql/src/Critical/DescriptorNeverClosed.ql
+++ b/cpp/ql/src/Critical/DescriptorNeverClosed.ql
@@ -4,6 +4,7 @@
* @kind problem
* @id cpp/descriptor-never-closed
* @problem.severity warning
+ * @security-severity 7.8
* @tags efficiency
* security
* external/cwe/cwe-775
diff --git a/cpp/ql/src/Critical/FileMayNotBeClosed.ql b/cpp/ql/src/Critical/FileMayNotBeClosed.ql
index c97e7cacca3f..af38b437778b 100644
--- a/cpp/ql/src/Critical/FileMayNotBeClosed.ql
+++ b/cpp/ql/src/Critical/FileMayNotBeClosed.ql
@@ -4,6 +4,7 @@
* @kind problem
* @id cpp/file-may-not-be-closed
* @problem.severity warning
+ * @security-severity 7.8
* @tags efficiency
* security
* external/cwe/cwe-775
diff --git a/cpp/ql/src/Critical/FileNeverClosed.ql b/cpp/ql/src/Critical/FileNeverClosed.ql
index 0286c78437f7..b9e71978359b 100644
--- a/cpp/ql/src/Critical/FileNeverClosed.ql
+++ b/cpp/ql/src/Critical/FileNeverClosed.ql
@@ -4,6 +4,7 @@
* @kind problem
* @id cpp/file-never-closed
* @problem.severity warning
+ * @security-severity 7.8
* @tags efficiency
* security
* external/cwe/cwe-775
diff --git a/cpp/ql/src/Critical/GlobalUseBeforeInit.ql b/cpp/ql/src/Critical/GlobalUseBeforeInit.ql
index 5483a0f2fbe8..6c3435eeba91 100644
--- a/cpp/ql/src/Critical/GlobalUseBeforeInit.ql
+++ b/cpp/ql/src/Critical/GlobalUseBeforeInit.ql
@@ -4,6 +4,7 @@
* @kind problem
* @id cpp/global-use-before-init
* @problem.severity warning
+ * @security-severity 7.8
* @tags reliability
* security
* external/cwe/cwe-457
diff --git a/cpp/ql/src/Critical/InconsistentNullnessTesting.ql b/cpp/ql/src/Critical/InconsistentNullnessTesting.ql
index 86e2cb4fb299..da64be1fdb93 100644
--- a/cpp/ql/src/Critical/InconsistentNullnessTesting.ql
+++ b/cpp/ql/src/Critical/InconsistentNullnessTesting.ql
@@ -4,6 +4,7 @@
* @kind problem
* @id cpp/inconsistent-nullness-testing
* @problem.severity warning
+ * @security-severity 7.5
* @tags reliability
* security
* external/cwe/cwe-476
diff --git a/cpp/ql/src/Critical/InitialisationNotRun.ql b/cpp/ql/src/Critical/InitialisationNotRun.ql
index 6295543084b8..ba575c55921b 100644
--- a/cpp/ql/src/Critical/InitialisationNotRun.ql
+++ b/cpp/ql/src/Critical/InitialisationNotRun.ql
@@ -4,6 +4,7 @@
* @kind problem
* @id cpp/initialization-not-run
* @problem.severity warning
+ * @security-severity 7.5
* @tags reliability
* security
* external/cwe/cwe-456
diff --git a/cpp/ql/src/Critical/LateNegativeTest.ql b/cpp/ql/src/Critical/LateNegativeTest.ql
index 098a224c8182..5de36fcc5a98 100644
--- a/cpp/ql/src/Critical/LateNegativeTest.ql
+++ b/cpp/ql/src/Critical/LateNegativeTest.ql
@@ -6,6 +6,7 @@
* @kind problem
* @id cpp/late-negative-test
* @problem.severity warning
+ * @security-severity 9.3
* @tags reliability
* security
* external/cwe/cwe-823
diff --git a/cpp/ql/src/Critical/MemoryMayNotBeFreed.ql b/cpp/ql/src/Critical/MemoryMayNotBeFreed.ql
index 9c09ec4c5f35..51467b52be87 100644
--- a/cpp/ql/src/Critical/MemoryMayNotBeFreed.ql
+++ b/cpp/ql/src/Critical/MemoryMayNotBeFreed.ql
@@ -4,6 +4,7 @@
* @kind problem
* @id cpp/memory-may-not-be-freed
* @problem.severity warning
+ * @security-severity 7.5
* @tags efficiency
* security
* external/cwe/cwe-401
diff --git a/cpp/ql/src/Critical/MemoryNeverFreed.ql b/cpp/ql/src/Critical/MemoryNeverFreed.ql
index 59f6f1da4df4..e9593e9d7494 100644
--- a/cpp/ql/src/Critical/MemoryNeverFreed.ql
+++ b/cpp/ql/src/Critical/MemoryNeverFreed.ql
@@ -4,6 +4,7 @@
* @kind problem
* @id cpp/memory-never-freed
* @problem.severity warning
+ * @security-severity 7.5
* @tags efficiency
* security
* external/cwe/cwe-401
diff --git a/cpp/ql/src/Critical/MissingNegativityTest.ql b/cpp/ql/src/Critical/MissingNegativityTest.ql
index 565217578e08..a4409f2dabfd 100644
--- a/cpp/ql/src/Critical/MissingNegativityTest.ql
+++ b/cpp/ql/src/Critical/MissingNegativityTest.ql
@@ -5,6 +5,7 @@
* @kind problem
* @id cpp/missing-negativity-test
* @problem.severity warning
+ * @security-severity 9.3
* @tags reliability
* security
* external/cwe/cwe-823
diff --git a/cpp/ql/src/Critical/MissingNullTest.ql b/cpp/ql/src/Critical/MissingNullTest.ql
index ea81eee8eb67..b50d06a8dd1c 100644
--- a/cpp/ql/src/Critical/MissingNullTest.ql
+++ b/cpp/ql/src/Critical/MissingNullTest.ql
@@ -4,6 +4,7 @@
* @kind problem
* @id cpp/missing-null-test
* @problem.severity recommendation
+ * @security-severity 7.5
* @tags reliability
* security
* external/cwe/cwe-476
diff --git a/cpp/ql/src/Critical/NewFreeMismatch.ql b/cpp/ql/src/Critical/NewFreeMismatch.ql
index 68e58b3acaf4..19b9b197214a 100644
--- a/cpp/ql/src/Critical/NewFreeMismatch.ql
+++ b/cpp/ql/src/Critical/NewFreeMismatch.ql
@@ -3,6 +3,7 @@
* @description An object that was allocated with 'malloc' or 'new' is being freed using a mismatching 'free' or 'delete'.
* @kind problem
* @problem.severity warning
+ * @security-severity 7.5
* @precision high
* @id cpp/new-free-mismatch
* @tags reliability
diff --git a/cpp/ql/src/Critical/OverflowCalculated.ql b/cpp/ql/src/Critical/OverflowCalculated.ql
index a52e0d82670c..d8a08cc6a69f 100644
--- a/cpp/ql/src/Critical/OverflowCalculated.ql
+++ b/cpp/ql/src/Critical/OverflowCalculated.ql
@@ -4,6 +4,7 @@
* @kind problem
* @id cpp/overflow-calculated
* @problem.severity warning
+ * @security-severity 9.8
* @tags reliability
* security
* external/cwe/cwe-131
diff --git a/cpp/ql/src/Critical/OverflowDestination.ql b/cpp/ql/src/Critical/OverflowDestination.ql
index bff3cac9326c..94d460016606 100644
--- a/cpp/ql/src/Critical/OverflowDestination.ql
+++ b/cpp/ql/src/Critical/OverflowDestination.ql
@@ -5,6 +5,7 @@
* @kind problem
* @id cpp/overflow-destination
* @problem.severity warning
+ * @security-severity 9.3
* @precision low
* @tags reliability
* security
diff --git a/cpp/ql/src/Critical/OverflowStatic.ql b/cpp/ql/src/Critical/OverflowStatic.ql
index 833ee45499e5..7c447c12323d 100644
--- a/cpp/ql/src/Critical/OverflowStatic.ql
+++ b/cpp/ql/src/Critical/OverflowStatic.ql
@@ -4,6 +4,7 @@
* may result in a buffer overflow.
* @kind problem
* @problem.severity warning
+ * @security-severity 9.3
* @precision medium
* @id cpp/static-buffer-overflow
* @tags reliability
@@ -14,6 +15,7 @@
import cpp
import semmle.code.cpp.commons.Buffer
+import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
import LoopBounds
private predicate staticBufferBase(VariableAccess access, Variable v) {
@@ -51,6 +53,8 @@ predicate overflowOffsetInLoop(BufferAccess bufaccess, string msg) {
loop.getStmt().getAChild*() = bufaccess.getEnclosingStmt() and
loop.limit() >= bufaccess.bufferSize() and
loop.counter().getAnAccess() = bufaccess.getArrayOffset() and
+ // Ensure that we don't have an upper bound on the array index that's less than the buffer size.
+ not upperBound(bufaccess.getArrayOffset().getFullyConverted()) < bufaccess.bufferSize() and
msg =
"Potential buffer-overflow: counter '" + loop.counter().toString() + "' <= " +
loop.limit().toString() + " but '" + bufaccess.buffer().getName() + "' has " +
@@ -94,17 +98,22 @@ class CallWithBufferSize extends FunctionCall {
}
int statedSizeValue() {
- exists(Expr statedSizeSrc |
- DataFlow::localExprFlow(statedSizeSrc, statedSizeExpr()) and
- result = statedSizeSrc.getValue().toInt()
- )
+ // `upperBound(e)` defaults to `exprMaxVal(e)` when `e` isn't analyzable. So to get a meaningful
+ // result in this case we pick the minimum value obtainable from dataflow and range analysis.
+ result =
+ upperBound(statedSizeExpr())
+ .minimum(min(Expr statedSizeSrc |
+ DataFlow::localExprFlow(statedSizeSrc, statedSizeExpr())
+ |
+ statedSizeSrc.getValue().toInt()
+ ))
}
}
predicate wrongBufferSize(Expr error, string msg) {
exists(CallWithBufferSize call, int bufsize, Variable buf, int statedSize |
staticBuffer(call.buffer(), buf, bufsize) and
- statedSize = min(call.statedSizeValue()) and
+ statedSize = call.statedSizeValue() and
statedSize > bufsize and
error = call.statedSizeExpr() and
msg =
diff --git a/cpp/ql/src/Critical/ReturnStackAllocatedObject.ql b/cpp/ql/src/Critical/ReturnStackAllocatedObject.ql
index 353e51daa71a..40082ad5d9c9 100644
--- a/cpp/ql/src/Critical/ReturnStackAllocatedObject.ql
+++ b/cpp/ql/src/Critical/ReturnStackAllocatedObject.ql
@@ -4,9 +4,12 @@
* @kind problem
* @id cpp/return-stack-allocated-object
* @problem.severity warning
+ * @security-severity 2.1
* @tags reliability
* security
* external/cwe/cwe-562
+ * @deprecated This query is not suitable for production use and has been deprecated. Use
+ * cpp/return-stack-allocated-memory instead.
*/
import semmle.code.cpp.pointsto.PointsTo
diff --git a/cpp/ql/src/Critical/ReturnValueIgnored.qhelp b/cpp/ql/src/Critical/ReturnValueIgnored.qhelp
index 26d0f5c6e848..d82daf6d1494 100644
--- a/cpp/ql/src/Critical/ReturnValueIgnored.qhelp
+++ b/cpp/ql/src/Critical/ReturnValueIgnored.qhelp
@@ -7,7 +7,7 @@
This rule finds calls to a function that ignore the return value. A function call is only marked
-as a violation if at least 80% of the total calls to that function check the return value. Not
+as a violation if at least 90% of the total calls to that function check the return value. Not
checking a return value is a common source of defects from standard library functions like malloc
or fread
.
These functions return the status information and the return values should always be checked
to see if the operation succeeded before operating on any data modified or resources allocated by these functions.
@@ -32,7 +32,7 @@ Check the return value of functions that return status information.
- M. Henricson and E. Nyquist, Industrial Strength C++, Chapter 12: Error handling. Prentice Hall PTR, 1997 (available online).
+ M. Henricson and E. Nyquist, Industrial Strength C++, Chapter 12: Error handling. Prentice Hall PTR, 1997 (available online).
The CERT C Secure Coding Standard: EXP32-PL. Do not ignore function return values.
diff --git a/cpp/ql/src/Critical/ReturnValueIgnored.ql b/cpp/ql/src/Critical/ReturnValueIgnored.ql
index b91430857208..b4a4a0440689 100644
--- a/cpp/ql/src/Critical/ReturnValueIgnored.ql
+++ b/cpp/ql/src/Critical/ReturnValueIgnored.ql
@@ -1,6 +1,6 @@
/**
* @name Return value of a function is ignored
- * @description A call to a function ignores its return value, but more than 80% of the total number of calls to the function check the return value. Check the return value of functions consistently, especially for functions like 'fread' or the 'scanf' functions that return the status of the operation.
+ * @description A call to a function ignores its return value, but at least 90% of the total number of calls to the function check the return value. Check the return value of functions consistently, especially for functions like 'fread' or the 'scanf' functions that return the status of the operation.
* @kind problem
* @id cpp/return-value-ignored
* @problem.severity recommendation
diff --git a/cpp/ql/src/Critical/SizeCheck.ql b/cpp/ql/src/Critical/SizeCheck.ql
index 849b4ba5f775..e7a00ea3621d 100644
--- a/cpp/ql/src/Critical/SizeCheck.ql
+++ b/cpp/ql/src/Critical/SizeCheck.ql
@@ -4,6 +4,7 @@
* an instance of the type of the pointer may result in a buffer overflow
* @kind problem
* @problem.severity warning
+ * @security-severity 8.1
* @precision medium
* @id cpp/allocation-too-small
* @tags reliability
diff --git a/cpp/ql/src/Critical/SizeCheck2.ql b/cpp/ql/src/Critical/SizeCheck2.ql
index 31364cbfe2d5..eb3aec9a5fe5 100644
--- a/cpp/ql/src/Critical/SizeCheck2.ql
+++ b/cpp/ql/src/Critical/SizeCheck2.ql
@@ -4,6 +4,7 @@
* multiple instances of the type of the pointer may result in a buffer overflow
* @kind problem
* @problem.severity warning
+ * @security-severity 8.1
* @precision medium
* @id cpp/suspicious-allocation-size
* @tags reliability
diff --git a/cpp/ql/src/Critical/UseAfterFree.ql b/cpp/ql/src/Critical/UseAfterFree.ql
index 8fd228ca7e45..d770a42b3c2a 100644
--- a/cpp/ql/src/Critical/UseAfterFree.ql
+++ b/cpp/ql/src/Critical/UseAfterFree.ql
@@ -4,6 +4,7 @@
* @kind problem
* @id cpp/use-after-free
* @problem.severity warning
+ * @security-severity 9.3
* @tags reliability
* security
* external/cwe/cwe-416
diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.ql b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.ql
index c503a8f3ee25..5a7389205f90 100644
--- a/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.ql
+++ b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.ql
@@ -6,6 +6,7 @@
* to a larger type.
* @kind problem
* @problem.severity error
+ * @security-severity 8.1
* @precision very-high
* @id cpp/bad-addition-overflow-check
* @tags reliability
diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql
index 76ff682f7e55..03ad085b6d33 100644
--- a/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql
+++ b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql
@@ -4,6 +4,7 @@
* be a sign that the result can overflow the type converted from.
* @kind problem
* @problem.severity warning
+ * @security-severity 8.1
* @precision high
* @id cpp/integer-multiplication-cast-to-long
* @tags reliability
diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.ql b/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.ql
index 1cc7b74375d3..7911049599a2 100644
--- a/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.ql
+++ b/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.ql
@@ -5,10 +5,13 @@
* unsigned integer values.
* @kind problem
* @problem.severity warning
+ * @security-severity 8.1
* @precision high
* @id cpp/signed-overflow-check
* @tags correctness
* security
+ * external/cwe/cwe-128
+ * external/cwe/cwe-190
*/
import cpp
diff --git a/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql b/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql
index ff9afff9e7fb..9032919da443 100644
--- a/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql
+++ b/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql
@@ -6,13 +6,14 @@
* use the width of the base type, leading to misaligned reads.
* @kind path-problem
* @problem.severity warning
+ * @security-severity 9.3
* @precision high
+ * @id cpp/upcast-array-pointer-arithmetic
* @tags correctness
* reliability
* security
* external/cwe/cwe-119
* external/cwe/cwe-843
- * @id cpp/upcast-array-pointer-arithmetic
*/
import cpp
diff --git a/cpp/ql/src/Likely Bugs/Format/NonConstantFormat.ql b/cpp/ql/src/Likely Bugs/Format/NonConstantFormat.ql
index 324c9128ba5e..f00dfa2213b4 100644
--- a/cpp/ql/src/Likely Bugs/Format/NonConstantFormat.ql
+++ b/cpp/ql/src/Likely Bugs/Format/NonConstantFormat.ql
@@ -6,6 +6,7 @@
* from an untrusted source, this can be used for exploits.
* @kind problem
* @problem.severity recommendation
+ * @security-severity 9.3
* @precision high
* @id cpp/non-constant-format
* @tags maintainability
diff --git a/cpp/ql/src/Likely Bugs/Format/SnprintfOverflow.ql b/cpp/ql/src/Likely Bugs/Format/SnprintfOverflow.ql
index 029f11dd8c67..7da8db7f226e 100644
--- a/cpp/ql/src/Likely Bugs/Format/SnprintfOverflow.ql
+++ b/cpp/ql/src/Likely Bugs/Format/SnprintfOverflow.ql
@@ -3,11 +3,14 @@
* @description Using the return value from snprintf without proper checks can cause overflow.
* @kind problem
* @problem.severity warning
+ * @security-severity 8.1
* @precision high
* @id cpp/overflowing-snprintf
* @tags reliability
* correctness
* security
+ * external/cwe/cwe-190
+ * external/cwe/cwe-253
*/
import cpp
diff --git a/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.ql b/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.ql
index 0f112e1ca6f9..cc3510ee5eb7 100644
--- a/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.ql
+++ b/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.ql
@@ -4,11 +4,13 @@
* a source of security issues.
* @kind problem
* @problem.severity error
+ * @security-severity 5.0
* @precision high
* @id cpp/wrong-number-format-arguments
* @tags reliability
* correctness
* security
+ * external/cwe/cwe-234
* external/cwe/cwe-685
*/
diff --git a/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql b/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql
index 92486a030b18..a7306e401e4a 100644
--- a/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql
+++ b/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql
@@ -4,6 +4,7 @@
* behavior.
* @kind problem
* @problem.severity error
+ * @security-severity 7.5
* @precision high
* @id cpp/wrong-type-format-argument
* @tags reliability
diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/IncorrectNotOperatorUsage.ql b/cpp/ql/src/Likely Bugs/Likely Typos/IncorrectNotOperatorUsage.ql
index bb20c220186a..30664869adc8 100644
--- a/cpp/ql/src/Likely Bugs/Likely Typos/IncorrectNotOperatorUsage.ql
+++ b/cpp/ql/src/Likely Bugs/Likely Typos/IncorrectNotOperatorUsage.ql
@@ -6,6 +6,7 @@
* @kind problem
* @id cpp/incorrect-not-operator-usage
* @problem.severity warning
+ * @security-severity 7.5
* @precision medium
* @tags security
* external/cwe/cwe-480
diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.qhelp b/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.qhelp
index 88a150a0ba25..3c9a68b97e2d 100644
--- a/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.qhelp
+++ b/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.qhelp
@@ -26,7 +26,7 @@ indication that there may be cases unhandled by the switch
statemen
MSDN Library: switch statement (C++)
- M. Henricson and E. Nyquist, Industrial Strength C++, Chapter 4: Control Flow, Rec 4.5. Prentice Hall PTR, 1997 (available online).
+ M. Henricson and E. Nyquist, Industrial Strength C++, Chapter 4: Control Flow, Rec 4.5. Prentice Hall PTR, 1997 (available online).
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.ql b/cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.ql
index e684cb525e7f..61d7a266d860 100644
--- a/cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.ql
+++ b/cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.ql
@@ -3,6 +3,7 @@
* @description Using alloca in a loop can lead to a stack overflow
* @kind problem
* @problem.severity warning
+ * @security-severity 7.5
* @precision high
* @id cpp/alloca-in-loop
* @tags reliability
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql b/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql
index 2a4b2d165075..7f1541f7ea8f 100644
--- a/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql
+++ b/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql
@@ -5,6 +5,7 @@
* @kind problem
* @id cpp/improper-null-termination
* @problem.severity warning
+ * @security-severity 7.8
* @tags security
* external/cwe/cwe-170
* external/cwe/cwe-665
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow.ql b/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow.ql
index 6344addd5473..e11d114d1fb6 100644
--- a/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow.ql
+++ b/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow.ql
@@ -4,10 +4,12 @@
* on undefined behavior and may lead to memory corruption.
* @kind problem
* @problem.severity error
+ * @security-severity 2.1
* @precision high
* @id cpp/pointer-overflow-check
* @tags reliability
* security
+ * external/cwe/cwe-758
*/
import cpp
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/PotentialBufferOverflow.ql b/cpp/ql/src/Likely Bugs/Memory Management/PotentialBufferOverflow.ql
index 9f611ae9bf61..23cf7e8364b0 100644
--- a/cpp/ql/src/Likely Bugs/Memory Management/PotentialBufferOverflow.ql
+++ b/cpp/ql/src/Likely Bugs/Memory Management/PotentialBufferOverflow.ql
@@ -6,6 +6,7 @@
* @kind problem
* @id cpp/potential-buffer-overflow
* @problem.severity warning
+ * @security-severity 10.0
* @tags reliability
* security
* external/cwe/cwe-676
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql
index fc06ed0a500e..f5dda53d4844 100644
--- a/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql
+++ b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql
@@ -13,6 +13,7 @@
import cpp
import semmle.code.cpp.dataflow.EscapesTree
+import semmle.code.cpp.models.interfaces.PointerWrapper
import semmle.code.cpp.dataflow.DataFlow
/**
@@ -39,6 +40,10 @@ predicate hasNontrivialConversion(Expr e) {
e instanceof ParenthesisExpr
)
or
+ // A smart pointer can be stack-allocated while the data it points to is heap-allocated.
+ // So we exclude such "conversions" from this predicate.
+ e = any(PointerWrapper wrapper).getAnUnwrapperFunction().getACallToThisFunction()
+ or
hasNontrivialConversion(e.getConversion())
}
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.ql b/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.ql
index 87120de06031..8e7bc5bfcf45 100644
--- a/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.ql
+++ b/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.ql
@@ -4,6 +4,7 @@
* as the third argument may result in a buffer overflow.
* @kind problem
* @problem.severity warning
+ * @security-severity 9.3
* @precision medium
* @id cpp/bad-strncpy-size
* @tags reliability
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToMemset.ql b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToMemset.ql
index 2ccdda23bfdc..8e41b414794b 100644
--- a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToMemset.ql
+++ b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToMemset.ql
@@ -7,6 +7,7 @@
* @kind problem
* @id cpp/suspicious-call-to-memset
* @problem.severity recommendation
+ * @security-severity 10.0
* @precision medium
* @tags reliability
* correctness
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.cpp b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.cpp
index c5cbcd2d7f1e..d15a123ce66d 100644
--- a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.cpp
+++ b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.cpp
@@ -2,3 +2,7 @@ strncat(dest, src, strlen(dest)); //wrong: should use remaining size of dest
strncat(dest, src, sizeof(dest)); //wrong: should use remaining size of dest.
//Also fails if dest is a pointer and not an array.
+
+strncat(dest, source, sizeof(dest) - strlen(dest)); // wrong: writes a zero byte past the `dest` buffer.
+
+strncat(dest, source, sizeof(dest) - strlen(dest) - 1); // correct: reserves space for the zero byte.
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.qhelp b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.qhelp
index 5424338e1d15..13c1e6d2710a 100644
--- a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.qhelp
+++ b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.qhelp
@@ -4,7 +4,17 @@
The standard library function strncat
appends a source string to a target string.
-The third argument defines the maximum number of characters to append and should be less than or equal to the remaining space in the destination buffer. Calls of the form strncat(dest, src, strlen(dest))
or strncat(dest, src, sizeof(dest))
set the third argument to the entire size of the destination buffer. Executing a call of this type may cause a buffer overflow unless the buffer is known to be empty. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.
+The third argument defines the maximum number of characters to append and should be less than or equal
+to the remaining space in the destination buffer.
+
+Calls of the form strncat(dest, src, strlen(dest))
or strncat(dest, src, sizeof(dest))
set
+the third argument to the entire size of the destination buffer.
+Executing a call of this type may cause a buffer overflow unless the buffer is known to be empty.
+
+Similarly, calls of the form strncat(dest, src, sizeof (dest) - strlen (dest))
allow one
+byte to be written ouside the dest
buffer.
+
+Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.
@@ -25,6 +35,10 @@ The third argument defines the maximum number of characters to append and should
M. Donaldson, Inside the Buffer Overflow Attack: Mechanism, Method & Prevention. SANS Institute InfoSec Reading Room, 2002.
+
+ CERT C Coding Standard:
+STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator.
+
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.ql b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.ql
index eae20876e355..644c48622a2b 100644
--- a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.ql
+++ b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.ql
@@ -1,14 +1,15 @@
/**
* @name Potentially unsafe call to strncat
- * @description Calling 'strncat' with the size of the destination buffer
- * as the third argument may result in a buffer overflow.
+ * @description Calling 'strncat' with an incorrect size argument may result in a buffer overflow.
* @kind problem
* @problem.severity warning
+ * @security-severity 9.3
* @precision medium
* @id cpp/unsafe-strncat
* @tags reliability
* correctness
* security
+ * external/cwe/cwe-788
* external/cwe/cwe-676
* external/cwe/cwe-119
* external/cwe/cwe-251
@@ -16,11 +17,53 @@
import cpp
import Buffer
+import semmle.code.cpp.models.implementations.Strcat
+import semmle.code.cpp.valuenumbering.GlobalValueNumbering
-from FunctionCall fc, VariableAccess va1, VariableAccess va2
-where
- fc.getTarget().(Function).hasName("strncat") and
- va1 = fc.getArgument(0) and
- va2 = fc.getArgument(2).(BufferSizeExpr).getArg() and
- va1.getTarget() = va2.getTarget()
+/**
+ * Holds if `call` is a call to `strncat` such that `sizeArg` and `destArg` are the size and
+ * destination arguments, respectively.
+ */
+predicate interestringCallWithArgs(Call call, Expr sizeArg, Expr destArg) {
+ exists(StrcatFunction strcat |
+ strcat = call.getTarget() and
+ sizeArg = call.getArgument(strcat.getParamSize()) and
+ destArg = call.getArgument(strcat.getParamDest())
+ )
+}
+
+/**
+ * Holds if `fc` is a call to `strncat` with size argument `sizeArg` and destination
+ * argument `destArg`, and `destArg` is the size of the buffer pointed to by `destArg`.
+ */
+predicate case1(FunctionCall fc, Expr sizeArg, VariableAccess destArg) {
+ interestringCallWithArgs(fc, sizeArg, destArg) and
+ exists(VariableAccess va |
+ va = sizeArg.(BufferSizeExpr).getArg() and
+ destArg.getTarget() = va.getTarget()
+ )
+}
+
+/**
+ * Holds if `fc` is a call to `strncat` with size argument `sizeArg` and destination
+ * argument `destArg`, and `sizeArg` computes the value `sizeof (dest) - strlen (dest)`.
+ */
+predicate case2(FunctionCall fc, Expr sizeArg, VariableAccess destArg) {
+ interestringCallWithArgs(fc, sizeArg, destArg) and
+ exists(SubExpr sub, int n |
+ // The destination buffer is an array of size n
+ destArg.getUnspecifiedType().(ArrayType).getSize() = n and
+ // The size argument is equivalent to a subtraction
+ globalValueNumber(sizeArg).getAnExpr() = sub and
+ // ... where the left side of the subtraction is the constant n
+ globalValueNumber(sub.getLeftOperand()).getAnExpr().getValue().toInt() = n and
+ // ... and the right side of the subtraction is a call to `strlen` where the argument is the
+ // destination buffer.
+ globalValueNumber(sub.getRightOperand()).getAnExpr().(StrlenCall).getStringExpr() =
+ globalValueNumber(destArg).getAnExpr()
+ )
+}
+
+from FunctionCall fc, Expr sizeArg, Expr destArg
+where case1(fc, sizeArg, destArg) or case2(fc, sizeArg, destArg)
select fc, "Potentially unsafe call to strncat."
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousSizeof.ql b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousSizeof.ql
index b887894707c7..a80af562bda7 100644
--- a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousSizeof.ql
+++ b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousSizeof.ql
@@ -5,6 +5,7 @@
* the machine pointer size.
* @kind problem
* @problem.severity warning
+ * @security-severity 8.8
* @precision medium
* @id cpp/suspicious-sizeof
* @tags reliability
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.ql b/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.ql
index 57dac90c850f..5861167659f8 100644
--- a/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.ql
+++ b/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.ql
@@ -5,6 +5,7 @@
* @kind problem
* @id cpp/uninitialized-local
* @problem.severity warning
+ * @security-severity 7.8
* @precision medium
* @tags security
* external/cwe/cwe-665
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/UnsafeUseOfStrcat.ql b/cpp/ql/src/Likely Bugs/Memory Management/UnsafeUseOfStrcat.ql
index 2d595c0c0506..d0b0f7f1e71b 100644
--- a/cpp/ql/src/Likely Bugs/Memory Management/UnsafeUseOfStrcat.ql
+++ b/cpp/ql/src/Likely Bugs/Memory Management/UnsafeUseOfStrcat.ql
@@ -4,6 +4,7 @@
* may result in a buffer overflow
* @kind problem
* @problem.severity warning
+ * @security-severity 9.8
* @precision medium
* @id cpp/unsafe-strcat
* @tags reliability
diff --git a/cpp/ql/src/Likely Bugs/OO/SelfAssignmentCheck.ql b/cpp/ql/src/Likely Bugs/OO/SelfAssignmentCheck.ql
index c8e10709f1ce..9ddf445f4bfa 100644
--- a/cpp/ql/src/Likely Bugs/OO/SelfAssignmentCheck.ql
+++ b/cpp/ql/src/Likely Bugs/OO/SelfAssignmentCheck.ql
@@ -6,6 +6,7 @@
* @kind problem
* @id cpp/self-assignment-check
* @problem.severity warning
+ * @security-severity 7.0
* @tags reliability
* security
* external/cwe/cwe-826
diff --git a/cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql b/cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql
index 8e78dab08abb..04325e8497e7 100644
--- a/cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql
+++ b/cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql
@@ -6,10 +6,12 @@
* @kind path-problem
* @id cpp/unsafe-use-of-this
* @problem.severity error
+ * @security-severity 7.5
* @precision very-high
* @tags correctness
* language-features
* security
+ * external/cwe/cwe-670
*/
import cpp
diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.ql b/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.ql
index fdacae48e07d..bc53015c9056 100644
--- a/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.ql
+++ b/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.ql
@@ -7,11 +7,14 @@
* undefined data.
* @kind problem
* @problem.severity error
+ * @security-severity 5.0
* @precision very-high
* @id cpp/too-few-arguments
* @tags correctness
* maintainability
* security
+ * external/cwe/cwe-234
+ * external/cwe/cwe-685
*/
import cpp
diff --git a/cpp/ql/src/Metrics/Files/FTransitiveIncludes.qhelp b/cpp/ql/src/Metrics/Files/FTransitiveIncludes.qhelp
index 4e0b989b1213..846e54b33c9f 100644
--- a/cpp/ql/src/Metrics/Files/FTransitiveIncludes.qhelp
+++ b/cpp/ql/src/Metrics/Files/FTransitiveIncludes.qhelp
@@ -29,7 +29,7 @@ build time: the more included files, the longer the compilation time.
Decoupling C Header Files
- C++ Best Practice -
+ C++ Best Practice -
Designing Header Files
diff --git a/cpp/ql/src/Metrics/Files/FTransitiveSourceIncludes.qhelp b/cpp/ql/src/Metrics/Files/FTransitiveSourceIncludes.qhelp
index a3091c68285d..785ad68847da 100644
--- a/cpp/ql/src/Metrics/Files/FTransitiveSourceIncludes.qhelp
+++ b/cpp/ql/src/Metrics/Files/FTransitiveSourceIncludes.qhelp
@@ -35,7 +35,7 @@ they are contributing to unnecessarily long build times and creating artificial
Decoupling C Header Files
- C++ Best Practice -
+ C++ Best Practice -
Designing Header Files
diff --git a/cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted.ql b/cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted.ql
index bbb77e807e77..33c319722958 100644
--- a/cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted.ql
+++ b/cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted.ql
@@ -5,6 +5,7 @@
* @kind problem
* @id cpp/memset-may-be-deleted
* @problem.severity warning
+ * @security-severity 7.8
* @precision high
* @tags security
* external/cwe/cwe-14
diff --git a/cpp/ql/src/Security/CWE/CWE-020/IRUntrustedDataToExternalAPI.ql b/cpp/ql/src/Security/CWE/CWE-020/IRUntrustedDataToExternalAPI.ql
index e4f0cc7883d5..47a0bf14b7fa 100644
--- a/cpp/ql/src/Security/CWE/CWE-020/IRUntrustedDataToExternalAPI.ql
+++ b/cpp/ql/src/Security/CWE/CWE-020/IRUntrustedDataToExternalAPI.ql
@@ -5,6 +5,7 @@
* @kind path-problem
* @precision low
* @problem.severity error
+ * @security-severity 7.8
* @tags security external/cwe/cwe-20
*/
diff --git a/cpp/ql/src/Security/CWE/CWE-020/UntrustedDataToExternalAPI.ql b/cpp/ql/src/Security/CWE/CWE-020/UntrustedDataToExternalAPI.ql
index ca6d2d00e8c3..b85a5b26a7f5 100644
--- a/cpp/ql/src/Security/CWE/CWE-020/UntrustedDataToExternalAPI.ql
+++ b/cpp/ql/src/Security/CWE/CWE-020/UntrustedDataToExternalAPI.ql
@@ -5,6 +5,7 @@
* @kind path-problem
* @precision low
* @problem.severity error
+ * @security-severity 7.8
* @tags security external/cwe/cwe-20
*/
diff --git a/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql
index 496b957cca32..5e22506d03a5 100644
--- a/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql
+++ b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql
@@ -4,6 +4,7 @@
* attacker to access unexpected resources.
* @kind path-problem
* @problem.severity warning
+ * @security-severity 7.5
* @precision medium
* @id cpp/path-injection
* @tags security
diff --git a/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql
index 69d6ce9b98f4..5f516eec83bf 100644
--- a/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql
+++ b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql
@@ -5,6 +5,7 @@
* to command injection.
* @kind problem
* @problem.severity error
+ * @security-severity 9.8
* @precision low
* @id cpp/command-line-injection
* @tags security
diff --git a/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql b/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql
index d1e2fa129135..bb38609927e2 100644
--- a/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql
+++ b/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql
@@ -4,6 +4,7 @@
* allows for a cross-site scripting vulnerability.
* @kind path-problem
* @problem.severity error
+ * @security-severity 6.1
* @precision high
* @id cpp/cgi-xss
* @tags security
diff --git a/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql b/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql
index 5ed84f452504..a3f935170d77 100644
--- a/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql
+++ b/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql
@@ -5,6 +5,7 @@
* to SQL Injection.
* @kind path-problem
* @problem.severity error
+ * @security-severity 8.8
* @precision high
* @id cpp/sql-injection
* @tags security
diff --git a/cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.ql b/cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.ql
index 943c13f9c5d2..e75f62b0eb77 100644
--- a/cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.ql
+++ b/cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.ql
@@ -5,6 +5,7 @@
* commands.
* @kind path-problem
* @problem.severity warning
+ * @security-severity 8.2
* @precision medium
* @id cpp/uncontrolled-process-operation
* @tags security
diff --git a/cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.ql b/cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.ql
index 00dc569c2f2a..1c903081baf2 100644
--- a/cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.ql
+++ b/cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.ql
@@ -6,6 +6,7 @@
* @kind problem
* @id cpp/overflow-buffer
* @problem.severity recommendation
+ * @security-severity 9.3
* @tags security
* external/cwe/cwe-119
* external/cwe/cwe-121
diff --git a/cpp/ql/src/Security/CWE/CWE-120/BadlyBoundedWrite.ql b/cpp/ql/src/Security/CWE/CWE-120/BadlyBoundedWrite.ql
index 77d514bc7b6c..247606c683de 100644
--- a/cpp/ql/src/Security/CWE/CWE-120/BadlyBoundedWrite.ql
+++ b/cpp/ql/src/Security/CWE/CWE-120/BadlyBoundedWrite.ql
@@ -5,6 +5,7 @@
* overflow.
* @kind problem
* @problem.severity error
+ * @security-severity 9.3
* @precision high
* @id cpp/badly-bounded-write
* @tags reliability
diff --git a/cpp/ql/src/Security/CWE/CWE-120/OverrunWrite.ql b/cpp/ql/src/Security/CWE/CWE-120/OverrunWrite.ql
index 4ee20508e456..ac4144d1c6f6 100644
--- a/cpp/ql/src/Security/CWE/CWE-120/OverrunWrite.ql
+++ b/cpp/ql/src/Security/CWE/CWE-120/OverrunWrite.ql
@@ -4,6 +4,7 @@
* of data written may overflow.
* @kind problem
* @problem.severity error
+ * @security-severity 9.3
* @precision medium
* @id cpp/overrunning-write
* @tags reliability
diff --git a/cpp/ql/src/Security/CWE/CWE-120/OverrunWriteFloat.ql b/cpp/ql/src/Security/CWE/CWE-120/OverrunWriteFloat.ql
index 79ba1e17df27..27adab9b06c9 100644
--- a/cpp/ql/src/Security/CWE/CWE-120/OverrunWriteFloat.ql
+++ b/cpp/ql/src/Security/CWE/CWE-120/OverrunWriteFloat.ql
@@ -5,6 +5,7 @@
* take extreme values.
* @kind problem
* @problem.severity error
+ * @security-severity 9.3
* @precision medium
* @id cpp/overrunning-write-with-float
* @tags reliability
diff --git a/cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.ql b/cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.ql
index f1a8b4e85444..b9922da9c758 100644
--- a/cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.ql
+++ b/cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.ql
@@ -4,6 +4,7 @@
* of data written may overflow.
* @kind path-problem
* @problem.severity error
+ * @security-severity 9.3
* @precision medium
* @id cpp/unbounded-write
* @tags reliability
diff --git a/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.ql b/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.ql
index bfb3a2fbb816..842798102bd8 100644
--- a/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.ql
+++ b/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.ql
@@ -5,6 +5,7 @@
* a specific value to terminate the argument list.
* @kind problem
* @problem.severity warning
+ * @security-severity 8.8
* @precision medium
* @id cpp/unterminated-variadic-call
* @tags reliability
diff --git a/cpp/ql/src/Security/CWE/CWE-129/ImproperArrayIndexValidation.ql b/cpp/ql/src/Security/CWE/CWE-129/ImproperArrayIndexValidation.ql
index c073cf37af80..0621def4d98d 100644
--- a/cpp/ql/src/Security/CWE/CWE-129/ImproperArrayIndexValidation.ql
+++ b/cpp/ql/src/Security/CWE/CWE-129/ImproperArrayIndexValidation.ql
@@ -6,6 +6,7 @@
* @kind problem
* @id cpp/unclear-array-index-validation
* @problem.severity warning
+ * @security-severity 8.8
* @tags security
* external/cwe/cwe-129
*/
diff --git a/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.ql b/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.ql
index 7ee6acdcd59f..1780c2a01998 100644
--- a/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.ql
+++ b/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.ql
@@ -5,6 +5,7 @@
* terminator can cause a buffer overrun.
* @kind problem
* @problem.severity error
+ * @security-severity 9.8
* @precision high
* @id cpp/no-space-for-terminator
* @tags reliability
diff --git a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql
index b64091263e09..f24510bba056 100644
--- a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql
+++ b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql
@@ -5,6 +5,7 @@
* or data representation problems.
* @kind path-problem
* @problem.severity warning
+ * @security-severity 9.3
* @precision high
* @id cpp/tainted-format-string
* @tags reliability
diff --git a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql
index d38f3eb24c2f..d2f5243d4a48 100644
--- a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql
+++ b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql
@@ -5,6 +5,7 @@
* or data representation problems.
* @kind path-problem
* @problem.severity warning
+ * @security-severity 9.3
* @precision high
* @id cpp/tainted-format-string-through-global
* @tags reliability
diff --git a/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql b/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql
index 511c7a1d79fa..b2844c319bad 100644
--- a/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql
+++ b/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql
@@ -5,6 +5,7 @@
* @kind problem
* @id cpp/user-controlled-null-termination-tainted
* @problem.severity warning
+ * @security-severity 10.0
* @tags security
* external/cwe/cwe-170
*/
diff --git a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticTainted.ql b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticTainted.ql
index 7416af158651..e00bd87c86ac 100644
--- a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticTainted.ql
+++ b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticTainted.ql
@@ -2,8 +2,9 @@
* @name User-controlled data in arithmetic expression
* @description Arithmetic operations on user-controlled data that is
* not validated can cause overflows.
- * @kind problem
+ * @kind path-problem
* @problem.severity warning
+ * @security-severity 8.6
* @precision low
* @id cpp/tainted-arithmetic
* @tags security
@@ -15,22 +16,39 @@ import cpp
import semmle.code.cpp.security.Overflow
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking
+import TaintedWithPath
+import Bounded
-from Expr origin, Operation op, Expr e, string effect
+bindingset[op]
+predicate missingGuard(Operation op, Expr e, string effect) {
+ missingGuardAgainstUnderflow(op, e) and effect = "underflow"
+ or
+ missingGuardAgainstOverflow(op, e) and effect = "overflow"
+ or
+ not e instanceof VariableAccess and effect = "overflow"
+}
+
+class Configuration extends TaintTrackingConfiguration {
+ override predicate isSink(Element e) {
+ exists(Operation op |
+ missingGuard(op, e, _) and
+ op.getAnOperand() = e
+ |
+ op instanceof UnaryArithmeticOperation or
+ op instanceof BinaryArithmeticOperation
+ )
+ }
+
+ override predicate isBarrier(Expr e) {
+ super.isBarrier(e) or bounded(e) or e.getUnspecifiedType().(IntegralType).getSize() <= 1
+ }
+}
+
+from Expr origin, Expr e, string effect, PathNode sourceNode, PathNode sinkNode, Operation op
where
- isUserInput(origin, _) and
- tainted(origin, e) and
+ taintedWithPath(origin, e, sourceNode, sinkNode) and
op.getAnOperand() = e and
- (
- missingGuardAgainstUnderflow(op, e) and effect = "underflow"
- or
- missingGuardAgainstOverflow(op, e) and effect = "overflow"
- or
- not e instanceof VariableAccess and effect = "overflow"
- ) and
- (
- op instanceof UnaryArithmeticOperation or
- op instanceof BinaryArithmeticOperation
- )
-select e, "$@ flows to here and is used in arithmetic, potentially causing an " + effect + ".",
- origin, "User-provided value"
+ missingGuard(op, e, effect)
+select e, sourceNode, sinkNode,
+ "$@ flows to here and is used in arithmetic, potentially causing an " + effect + ".", origin,
+ "User-provided value"
diff --git a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql
index a4b0f131d144..158da53841f1 100644
--- a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql
+++ b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql
@@ -4,6 +4,7 @@
* validated can cause overflows.
* @kind path-problem
* @problem.severity warning
+ * @security-severity 8.6
* @precision medium
* @id cpp/uncontrolled-arithmetic
* @tags security
@@ -16,33 +17,38 @@ import semmle.code.cpp.security.Overflow
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking
import TaintedWithPath
+import Bounded
-predicate isRandCall(FunctionCall fc) { fc.getTarget().getName() = "rand" }
+predicate isUnboundedRandCall(FunctionCall fc) {
+ exists(Function func | func = fc.getTarget() |
+ func.hasGlobalOrStdOrBslName("rand") and
+ not bounded(fc) and
+ func.getNumberOfParameters() = 0
+ )
+}
-predicate isRandCallOrParent(Expr e) {
- isRandCall(e) or
- isRandCallOrParent(e.getAChild())
+predicate isUnboundedRandCallOrParent(Expr e) {
+ isUnboundedRandCall(e)
+ or
+ isUnboundedRandCallOrParent(e.getAChild())
}
-predicate isRandValue(Expr e) {
- isRandCall(e)
+predicate isUnboundedRandValue(Expr e) {
+ isUnboundedRandCall(e)
or
exists(MacroInvocation mi |
e = mi.getExpr() and
- isRandCallOrParent(e)
+ isUnboundedRandCallOrParent(e)
)
}
class SecurityOptionsArith extends SecurityOptions {
override predicate isUserInput(Expr expr, string cause) {
- isRandValue(expr) and
- cause = "rand" and
- not expr.getParent*() instanceof DivExpr
+ isUnboundedRandValue(expr) and
+ cause = "rand"
}
}
-predicate isDiv(VariableAccess va) { exists(AssignDivExpr div | div.getLValue() = va) }
-
predicate missingGuard(VariableAccess va, string effect) {
exists(Operation op | op.getAnOperand() = va |
missingGuardAgainstUnderflow(op, va) and effect = "underflow"
@@ -52,29 +58,15 @@ predicate missingGuard(VariableAccess va, string effect) {
}
class Configuration extends TaintTrackingConfiguration {
- override predicate isSink(Element e) {
- isDiv(e)
- or
- missingGuard(e, _)
- }
-}
+ override predicate isSink(Element e) { missingGuard(e, _) }
-/**
- * A value that undergoes division is likely to be bounded within a safe
- * range.
- */
-predicate guardedByAssignDiv(Expr origin) {
- exists(VariableAccess va |
- taintedWithPath(origin, va, _, _) and
- isDiv(va)
- )
+ override predicate isBarrier(Expr e) { super.isBarrier(e) or bounded(e) }
}
from Expr origin, VariableAccess va, string effect, PathNode sourceNode, PathNode sinkNode
where
taintedWithPath(origin, va, sourceNode, sinkNode) and
- missingGuard(va, effect) and
- not guardedByAssignDiv(origin)
+ missingGuard(va, effect)
select va, sourceNode, sinkNode,
"$@ flows to here and is used in arithmetic, potentially causing an " + effect + ".", origin,
"Uncontrolled value"
diff --git a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.ql b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.ql
index 668b07d72af5..35668953acc0 100644
--- a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.ql
+++ b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.ql
@@ -6,6 +6,7 @@
* @kind problem
* @id cpp/arithmetic-with-extreme-values
* @problem.severity warning
+ * @security-severity 8.6
* @precision low
* @tags security
* reliability
diff --git a/cpp/ql/src/Security/CWE/CWE-190/Bounded.qll b/cpp/ql/src/Security/CWE/CWE-190/Bounded.qll
new file mode 100644
index 000000000000..9f872304021d
--- /dev/null
+++ b/cpp/ql/src/Security/CWE/CWE-190/Bounded.qll
@@ -0,0 +1,83 @@
+/**
+ * This file provides the `bounded` predicate that is used in both `cpp/uncontrolled-arithmetic`
+ * and `cpp/tainted-arithmetic`.
+ */
+
+private import cpp
+private import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
+private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
+
+/**
+ * An operand `e` of a division expression (i.e., `e` is an operand of either a `DivExpr` or
+ * a `AssignDivExpr`) is bounded when `e` is the left-hand side of the division.
+ */
+pragma[inline]
+private predicate boundedDiv(Expr e, Expr left) { e = left }
+
+/**
+ * An operand `e` of a remainder expression `rem` (i.e., `rem` is either a `RemExpr` or
+ * an `AssignRemExpr`) with left-hand side `left` and right-ahnd side `right` is bounded
+ * when `e` is `left` and `right` is upper bounded by some number that is less than the maximum integer
+ * allowed by the result type of `rem`.
+ */
+pragma[inline]
+private predicate boundedRem(Expr e, Expr rem, Expr left, Expr right) {
+ e = left and
+ upperBound(right.getFullyConverted()) < exprMaxVal(rem.getFullyConverted())
+}
+
+/**
+ * An operand `e` of a bitwise and expression `andExpr` (i.e., `andExpr` is either an `BitwiseAndExpr`
+ * or an `AssignAndExpr`) with operands `operand1` and `operand2` is the operand that is not `e` is upper
+ * bounded by some number that is less than the maximum integer allowed by the result type of `andExpr`.
+ */
+pragma[inline]
+private predicate boundedBitwiseAnd(Expr e, Expr andExpr, Expr operand1, Expr operand2) {
+ operand1 != operand2 and
+ e = operand1 and
+ upperBound(operand2.getFullyConverted()) < exprMaxVal(andExpr.getFullyConverted())
+}
+
+/**
+ * Holds if `e` is an arithmetic expression that cannot overflow, or if `e` is an operand of an
+ * operation that may greatly reduce the range of possible values.
+ */
+predicate bounded(Expr e) {
+ (
+ e instanceof UnaryArithmeticOperation or
+ e instanceof BinaryArithmeticOperation or
+ e instanceof AssignArithmeticOperation
+ ) and
+ not convertedExprMightOverflow(e)
+ or
+ // For `%` and `&` we require that `e` is bounded by a value that is strictly smaller than the
+ // maximum possible value of the result type of the operation.
+ // For example, the function call `rand()` is considered bounded in the following program:
+ // ```
+ // int i = rand() % (UINT8_MAX + 1);
+ // ```
+ // but not in:
+ // ```
+ // unsigned char uc = rand() % (UINT8_MAX + 1);
+ // ```
+ exists(RemExpr rem | boundedRem(e, rem, rem.getLeftOperand(), rem.getRightOperand()))
+ or
+ exists(AssignRemExpr rem | boundedRem(e, rem, rem.getLValue(), rem.getRValue()))
+ or
+ exists(BitwiseAndExpr andExpr |
+ boundedBitwiseAnd(e, andExpr, andExpr.getAnOperand(), andExpr.getAnOperand())
+ )
+ or
+ exists(AssignAndExpr andExpr |
+ boundedBitwiseAnd(e, andExpr, andExpr.getAnOperand(), andExpr.getAnOperand())
+ )
+ or
+ // Optimitically assume that a division always yields a much smaller value.
+ boundedDiv(e, any(DivExpr div).getLeftOperand())
+ or
+ boundedDiv(e, any(AssignDivExpr div).getLValue())
+ or
+ boundedDiv(e, any(RShiftExpr shift).getLeftOperand())
+ or
+ boundedDiv(e, any(AssignRShiftExpr div).getLValue())
+}
diff --git a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql
index 3303316cede7..6636d100746c 100644
--- a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql
+++ b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql
@@ -5,6 +5,7 @@
* @id cpp/comparison-with-wider-type
* @kind problem
* @problem.severity warning
+ * @security-severity 7.8
* @precision high
* @tags reliability
* security
@@ -49,7 +50,9 @@ where
small = rel.getLesserOperand() and
large = rel.getGreaterOperand() and
rel = l.getCondition().getAChild*() and
- upperBound(large).log2() > getComparisonSize(small) * 8 and
+ forall(Expr conv | conv = large.getConversion*() |
+ upperBound(conv).log2() > getComparisonSize(small) * 8
+ ) and
// Ignore cases where the smaller type is int or larger
// These are still bugs, but you should need a very large string or array to
// trigger them. We will want to disable this for some applications, but it's
diff --git a/cpp/ql/src/Security/CWE/CWE-190/IntegerOverflowTainted.ql b/cpp/ql/src/Security/CWE/CWE-190/IntegerOverflowTainted.ql
index 0adb600dbda6..bc0dff582448 100644
--- a/cpp/ql/src/Security/CWE/CWE-190/IntegerOverflowTainted.ql
+++ b/cpp/ql/src/Security/CWE/CWE-190/IntegerOverflowTainted.ql
@@ -5,6 +5,7 @@
* @kind problem
* @id cpp/integer-overflow-tainted
* @problem.severity warning
+ * @security-severity 8.1
* @precision low
* @tags security
* external/cwe/cwe-190
@@ -28,6 +29,7 @@ predicate outOfBoundsExpr(Expr expr, string kind) {
from Expr use, Expr origin, string kind
where
+ not use.getUnspecifiedType() instanceof PointerType and
outOfBoundsExpr(use, kind) and
tainted(origin, use) and
origin != use and
diff --git a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql
index cc2d52385c77..585875798cc3 100644
--- a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql
+++ b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql
@@ -4,14 +4,17 @@
* user can result in integer overflow.
* @kind path-problem
* @problem.severity error
+ * @security-severity 8.1
* @precision medium
* @id cpp/uncontrolled-allocation-size
* @tags reliability
* security
* external/cwe/cwe-190
+ * external/cwe/cwe-789
*/
import cpp
+import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
import semmle.code.cpp.security.TaintTracking
import TaintedWithPath
@@ -27,6 +30,27 @@ predicate allocSink(Expr alloc, Expr tainted) {
class TaintedAllocationSizeConfiguration extends TaintTrackingConfiguration {
override predicate isSink(Element tainted) { allocSink(_, tainted) }
+
+ override predicate isBarrier(Expr e) {
+ super.isBarrier(e)
+ or
+ // There can be two separate reasons for `convertedExprMightOverflow` not holding:
+ // 1. `e` really cannot overflow.
+ // 2. `e` isn't analyzable.
+ // If we didn't rule out case 2 we would place barriers on anything that isn't analyzable.
+ (
+ e instanceof UnaryArithmeticOperation or
+ e instanceof BinaryArithmeticOperation or
+ e instanceof AssignArithmeticOperation
+ ) and
+ not convertedExprMightOverflow(e)
+ or
+ // Subtracting two pointers is either well-defined (and the result will likely be small), or
+ // terribly undefined and dangerous. Here, we assume that the programmer has ensured that the
+ // result is well-defined (i.e., the two pointers point to the same object), and thus the result
+ // will likely be small.
+ e = any(PointerDiffExpr diff).getAnOperand()
+ }
}
predicate taintedAllocSize(
diff --git a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql
index 007a4fd746d1..5be71472c920 100644
--- a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql
+++ b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql
@@ -4,6 +4,7 @@
* @kind problem
* @id cpp/unsigned-difference-expression-compared-zero
* @problem.severity warning
+ * @security-severity 9.8
* @precision medium
* @tags security
* correctness
@@ -12,29 +13,60 @@
import cpp
import semmle.code.cpp.commons.Exclusions
-import semmle.code.cpp.valuenumbering.GlobalValueNumbering
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
+import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
import semmle.code.cpp.controlflow.Guards
+import semmle.code.cpp.dataflow.DataFlow
-/** Holds if `sub` is guarded by a condition which ensures that `left >= right`. */
+/**
+ * Holds if `sub` is guarded by a condition which ensures that
+ * `left >= right`.
+ */
pragma[noinline]
predicate isGuarded(SubExpr sub, Expr left, Expr right) {
- exists(GuardCondition guard |
- guard.controls(sub.getBasicBlock(), true) and
- guard.ensuresLt(left, right, 0, sub.getBasicBlock(), false)
+ exists(GuardCondition guard, int k |
+ guard.controls(sub.getBasicBlock(), _) and
+ guard.ensuresLt(left, right, k, sub.getBasicBlock(), false) and
+ k >= 0
)
}
-/** Holds if `sub` will never be negative. */
-predicate nonNegative(SubExpr sub) {
- not exprMightOverflowNegatively(sub.getFullyConverted())
+/**
+ * Holds if `n` is known or suspected to be less than or equal to
+ * `sub.getLeftOperand()`.
+ */
+predicate exprIsSubLeftOrLess(SubExpr sub, DataFlow::Node n) {
+ n.asExpr() = sub.getLeftOperand()
+ or
+ exists(DataFlow::Node other |
+ // dataflow
+ exprIsSubLeftOrLess(sub, other) and
+ (
+ DataFlow::localFlowStep(n, other) or
+ DataFlow::localFlowStep(other, n)
+ )
+ )
+ or
+ exists(DataFlow::Node other |
+ // guard constraining `sub`
+ exprIsSubLeftOrLess(sub, other) and
+ isGuarded(sub, other.asExpr(), n.asExpr()) // other >= n
+ )
+ or
+ exists(DataFlow::Node other, float p, float q |
+ // linear access of `other`
+ exprIsSubLeftOrLess(sub, other) and
+ linearAccess(n.asExpr(), other.asExpr(), p, q) and // n = p * other + q
+ p <= 1 and
+ q <= 0
+ )
or
- // The subtraction is guarded by a check of the form `left >= right`.
- exists(GVN left, GVN right |
- // This is basically a poor man's version of a directional unbind operator.
- strictcount([left, globalValueNumber(sub.getLeftOperand())]) = 1 and
- strictcount([right, globalValueNumber(sub.getRightOperand())]) = 1 and
- isGuarded(sub, left.getAnExpr(), right.getAnExpr())
+ exists(DataFlow::Node other, float p, float q |
+ // linear access of `n`
+ exprIsSubLeftOrLess(sub, other) and
+ linearAccess(other.asExpr(), n.asExpr(), p, q) and // other = p * n + q
+ p >= 1 and
+ q >= 0
)
}
@@ -45,5 +77,6 @@ where
ro.getLesserOperand().getValue().toInt() = 0 and
ro.getGreaterOperand() = sub and
sub.getFullyConverted().getUnspecifiedType().(IntegralType).isUnsigned() and
- not nonNegative(sub)
+ exprMightOverflowNegatively(sub.getFullyConverted()) and // generally catches false positives involving constants
+ not exprIsSubLeftOrLess(sub, DataFlow::exprNode(sub.getRightOperand())) // generally catches false positives where there's a relation between the left and right operands
select ro, "Unsigned subtraction can never be negative."
diff --git a/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql b/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql
index 605321b51d0e..67ba5b0c45b3 100644
--- a/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql
+++ b/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql
@@ -4,6 +4,7 @@
* @kind problem
* @id cpp/hresult-boolean-conversion
* @problem.severity error
+ * @security-severity 7.5
* @precision high
* @tags security
* external/cwe/cwe-253
diff --git a/cpp/ql/src/Security/CWE/CWE-290/AuthenticationBypass.ql b/cpp/ql/src/Security/CWE/CWE-290/AuthenticationBypass.ql
index 80b5ee49e973..814c6aff21bb 100644
--- a/cpp/ql/src/Security/CWE/CWE-290/AuthenticationBypass.ql
+++ b/cpp/ql/src/Security/CWE/CWE-290/AuthenticationBypass.ql
@@ -5,6 +5,7 @@
* vulnerable to spoofing attacks.
* @kind path-problem
* @problem.severity warning
+ * @security-severity 8.1
* @precision medium
* @id cpp/user-controlled-bypass
* @tags security
diff --git a/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.ql b/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.ql
index 3e84c0a87d9d..696c5764fcd6 100644
--- a/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.ql
+++ b/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.ql
@@ -4,6 +4,7 @@
* to an attacker.
* @kind path-problem
* @problem.severity warning
+ * @security-severity 7.5
* @precision medium
* @id cpp/cleartext-storage-buffer
* @tags security
diff --git a/cpp/ql/src/Security/CWE/CWE-311/CleartextFileWrite.ql b/cpp/ql/src/Security/CWE/CWE-311/CleartextFileWrite.ql
index 8e447bafd900..aa90ff9567c8 100644
--- a/cpp/ql/src/Security/CWE/CWE-311/CleartextFileWrite.ql
+++ b/cpp/ql/src/Security/CWE/CWE-311/CleartextFileWrite.ql
@@ -4,6 +4,7 @@
* to an attacker.
* @kind problem
* @problem.severity warning
+ * @security-severity 7.5
* @precision medium
* @id cpp/cleartext-storage-file
* @tags security
diff --git a/cpp/ql/src/Security/CWE/CWE-313/CleartextSqliteDatabase.ql b/cpp/ql/src/Security/CWE/CWE-313/CleartextSqliteDatabase.ql
index fb5454d523e4..bb9135a92ff8 100644
--- a/cpp/ql/src/Security/CWE/CWE-313/CleartextSqliteDatabase.ql
+++ b/cpp/ql/src/Security/CWE/CWE-313/CleartextSqliteDatabase.ql
@@ -4,6 +4,7 @@
* database can expose it to an attacker.
* @kind path-problem
* @problem.severity warning
+ * @security-severity 7.5
* @precision medium
* @id cpp/cleartext-storage-database
* @tags security
diff --git a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql
index af64a1789c3b..e6c7b186ce2c 100644
--- a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql
+++ b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql
@@ -4,7 +4,8 @@
* an attacker to compromise security.
* @kind problem
* @problem.severity error
- * @precision medium
+ * @security-severity 7.5
+ * @precision high
* @id cpp/weak-cryptographic-algorithm
* @tags security
* external/cwe/cwe-327
@@ -13,39 +14,155 @@
import cpp
import semmle.code.cpp.security.Encryption
-abstract class InsecureCryptoSpec extends Locatable {
- abstract string description();
+/**
+ * A function which may relate to an insecure encryption algorithm.
+ */
+Function getAnInsecureEncryptionFunction() {
+ (
+ isInsecureEncryption(result.getName()) or
+ isInsecureEncryption(result.getAParameter().getName()) or
+ isInsecureEncryption(result.getDeclaringType().getName())
+ ) and
+ exists(result.getACallToThisFunction())
}
-Function getAnInsecureFunction() {
- result.getName().regexpMatch(getInsecureAlgorithmRegex()) and
+/**
+ * A function with additional evidence it is related to encryption.
+ */
+Function getAnAdditionalEvidenceFunction() {
+ (
+ isEncryptionAdditionalEvidence(result.getName()) or
+ isEncryptionAdditionalEvidence(result.getAParameter().getName())
+ ) and
exists(result.getACallToThisFunction())
}
-class InsecureFunctionCall extends InsecureCryptoSpec, FunctionCall {
- InsecureFunctionCall() { this.getTarget() = getAnInsecureFunction() }
+/**
+ * A macro which may relate to an insecure encryption algorithm.
+ */
+Macro getAnInsecureEncryptionMacro() {
+ isInsecureEncryption(result.getName()) and
+ exists(result.getAnInvocation())
+}
- override string description() { result = "function call" }
+/**
+ * A macro with additional evidence it is related to encryption.
+ */
+Macro getAnAdditionalEvidenceMacro() {
+ isEncryptionAdditionalEvidence(result.getName()) and
+ exists(result.getAnInvocation())
+}
- override string toString() { result = FunctionCall.super.toString() }
+/**
+ * An enum constant which may relate to an insecure encryption algorithm.
+ */
+EnumConstant getAnInsecureEncryptionEnumConst() { isInsecureEncryption(result.getName()) }
- override Location getLocation() { result = FunctionCall.super.getLocation() }
-}
+/**
+ * An enum constant with additional evidence it is related to encryption.
+ */
+EnumConstant getAdditionalEvidenceEnumConst() { isEncryptionAdditionalEvidence(result.getName()) }
-Macro getAnInsecureMacro() {
- result.getName().regexpMatch(getInsecureAlgorithmRegex()) and
- exists(result.getAnInvocation())
+/**
+ * A function call we have a high confidence is related to use of an insecure encryption algorithm, along
+ * with an associated `Element` which might be the best point to blame, and a description of that element.
+ */
+predicate getInsecureEncryptionEvidence(FunctionCall fc, Element blame, string description) {
+ // find use of an insecure algorithm name
+ (
+ exists(FunctionCall fc2 |
+ fc.getAChild*() = fc2 and
+ fc2.getTarget() = getAnInsecureEncryptionFunction() and
+ blame = fc2 and
+ description = "call to " + fc.getTarget().getName()
+ )
+ or
+ exists(MacroInvocation mi |
+ (
+ mi.getAnExpandedElement() = fc or
+ mi.getAnExpandedElement() = fc.getAnArgument()
+ ) and
+ mi.getMacro() = getAnInsecureEncryptionMacro() and
+ blame = mi and
+ description = "invocation of macro " + mi.getMacro().getName()
+ )
+ or
+ exists(EnumConstantAccess ec |
+ ec = fc.getAnArgument() and
+ ec.getTarget() = getAnInsecureEncryptionEnumConst() and
+ blame = ec and
+ description = "access of enum constant " + ec.getTarget().getName()
+ )
+ ) and
+ // find additional evidence that this function is related to encryption.
+ (
+ exists(FunctionCall fc2 |
+ fc.getAChild*() = fc2 and
+ fc2.getTarget() = getAnAdditionalEvidenceFunction()
+ )
+ or
+ exists(MacroInvocation mi |
+ (
+ mi.getAnExpandedElement() = fc or
+ mi.getAnExpandedElement() = fc.getAnArgument()
+ ) and
+ mi.getMacro() = getAnAdditionalEvidenceMacro()
+ )
+ or
+ exists(EnumConstantAccess ec |
+ ec = fc.getAnArgument() and
+ ec.getTarget() = getAdditionalEvidenceEnumConst()
+ )
+ ) and
+ // exclude calls from templates as this is rarely the right place to flag an
+ // issue
+ not fc.isFromTemplateInstantiation(_) and
+ (
+ // the function should have an input that looks like a non-constant buffer
+ exists(Expr e |
+ fc.getAnArgument() = e and
+ (
+ e.getUnspecifiedType() instanceof PointerType or
+ e.getUnspecifiedType() instanceof ReferenceType or
+ e.getUnspecifiedType() instanceof ArrayType
+ ) and
+ not e.getType().isDeeplyConstBelow() and
+ not e.isConstant()
+ )
+ or
+ // or be a non-const member function of an object
+ fc.getTarget() instanceof MemberFunction and
+ not fc.getTarget() instanceof ConstMemberFunction and
+ not fc.getTarget().isStatic()
+ )
}
-class InsecureMacroSpec extends InsecureCryptoSpec, MacroInvocation {
- InsecureMacroSpec() { this.getMacro() = getAnInsecureMacro() }
+/**
+ * An element that is the `blame` of an `InsecureFunctionCall`.
+ */
+class BlamedElement extends Element {
+ string description;
- override string description() { result = "macro invocation" }
+ BlamedElement() { getInsecureEncryptionEvidence(_, this, description) }
- override string toString() { result = MacroInvocation.super.toString() }
+ /**
+ * Holds if this is the `num`-th `BlamedElement` in `f`.
+ */
+ predicate hasFileRank(File f, int num) {
+ exists(int loc |
+ getLocation().charLoc(f, loc, _) and
+ loc =
+ rank[num](BlamedElement other, int loc2 | other.getLocation().charLoc(f, loc2, _) | loc2)
+ )
+ }
- override Location getLocation() { result = MacroInvocation.super.getLocation() }
+ string getDescription() { result = description }
}
-from InsecureCryptoSpec c
-select c, "This " + c.description() + " specifies a broken or weak cryptographic algorithm."
+from File f, BlamedElement firstResult, BlamedElement thisResult
+where
+ firstResult.hasFileRank(f, 1) and
+ thisResult.hasFileRank(f, _)
+select firstResult,
+ "This file makes use of a broken or weak cryptographic algorithm (specified by $@).", thisResult,
+ thisResult.getDescription()
diff --git a/cpp/ql/src/Security/CWE/CWE-327/OpenSslHeartbleed.ql b/cpp/ql/src/Security/CWE/CWE-327/OpenSslHeartbleed.ql
index a7ffadc07be4..38067ae200c2 100644
--- a/cpp/ql/src/Security/CWE/CWE-327/OpenSslHeartbleed.ql
+++ b/cpp/ql/src/Security/CWE/CWE-327/OpenSslHeartbleed.ql
@@ -4,6 +4,7 @@
* attackers to retrieve portions of memory.
* @kind problem
* @problem.severity error
+ * @security-severity 7.5
* @precision very-high
* @id cpp/openssl-heartbleed
* @tags security
diff --git a/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRace.ql b/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRace.ql
index 052cce561989..f5bed0bee642 100644
--- a/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRace.ql
+++ b/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRace.ql
@@ -5,6 +5,7 @@
* the two operations.
* @kind problem
* @problem.severity warning
+ * @security-severity 7.7
* @precision medium
* @id cpp/toctou-race-condition
* @tags security
diff --git a/cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql b/cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql
index c0f44a0c3d6a..7c540e9d3133 100644
--- a/cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql
+++ b/cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql
@@ -4,6 +4,7 @@
* @id cpp/unsafe-create-process-call
* @kind problem
* @problem.severity error
+ * @security-severity 7.8
* @precision medium
* @msrc.severity important
* @tags security
diff --git a/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariable.ql b/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariable.ql
index 3b76a5447dda..c6a797929bbc 100644
--- a/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariable.ql
+++ b/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariable.ql
@@ -5,6 +5,7 @@
* state, and reading the variable may result in undefined behavior.
* @kind problem
* @problem.severity warning
+ * @security-severity 7.8
* @opaque-id SM02313
* @id cpp/conditionally-uninitialized-variable
* @tags security
diff --git a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScaling.ql b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScaling.ql
index d49eef4202ad..ce99ce1ebce0 100644
--- a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScaling.ql
+++ b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScaling.ql
@@ -4,6 +4,7 @@
* can cause buffer overflow conditions.
* @kind problem
* @problem.severity warning
+ * @security-severity 8.8
* @precision medium
* @id cpp/suspicious-pointer-scaling
* @tags security
diff --git a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingChar.ql b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingChar.ql
index d333a2b37bc2..5b7e33799295 100644
--- a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingChar.ql
+++ b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingChar.ql
@@ -5,6 +5,7 @@
* @kind problem
* @id cpp/incorrect-pointer-scaling-char
* @problem.severity warning
+ * @security-severity 8.8
* @precision low
* @tags security
* external/cwe/cwe-468
diff --git a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingVoid.ql b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingVoid.ql
index 42bf03e8628a..460c98bf1e32 100644
--- a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingVoid.ql
+++ b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingVoid.ql
@@ -4,6 +4,7 @@
* can cause buffer overflow conditions.
* @kind problem
* @problem.severity warning
+ * @security-severity 8.8
* @precision medium
* @id cpp/suspicious-pointer-scaling-void
* @tags security
diff --git a/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql b/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql
index d356ba7bbc43..4ac00fc42c6d 100644
--- a/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql
+++ b/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql
@@ -5,6 +5,7 @@
* implicitly scaled.
* @kind problem
* @problem.severity warning
+ * @security-severity 8.8
* @precision high
* @id cpp/suspicious-add-sizeof
* @tags security
diff --git a/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql
index 63eca2922977..bbe3b0805e19 100644
--- a/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql
+++ b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql
@@ -5,6 +5,7 @@
* attack plan.
* @kind problem
* @problem.severity warning
+ * @security-severity 6.5
* @precision medium
* @id cpp/system-data-exposure
* @tags security
diff --git a/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.cpp b/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.cpp
new file mode 100644
index 000000000000..055aadcedb65
--- /dev/null
+++ b/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.cpp
@@ -0,0 +1,43 @@
+// BAD: the allocation will throw an unhandled exception
+// instead of returning a null pointer.
+void bad1(std::size_t length) noexcept {
+ int* dest = new int[length];
+ if(!dest) {
+ return;
+ }
+ std::memset(dest, 0, length);
+ // ...
+}
+
+// BAD: the allocation won't throw an exception, but
+// instead return a null pointer.
+void bad2(std::size_t length) noexcept {
+ try {
+ int* dest = new(std::nothrow) int[length];
+ std::memset(dest, 0, length);
+ // ...
+ } catch(std::bad_alloc&) {
+ // ...
+ }
+}
+
+// GOOD: the allocation failure is handled appropriately.
+void good1(std::size_t length) noexcept {
+ try {
+ int* dest = new int[length];
+ std::memset(dest, 0, length);
+ // ...
+ } catch(std::bad_alloc&) {
+ // ...
+ }
+}
+
+// GOOD: the allocation failure is handled appropriately.
+void good2(std::size_t length) noexcept {
+ int* dest = new int[length];
+ if(!dest) {
+ return;
+ }
+ std::memset(dest, 0, length);
+ // ...
+}
diff --git a/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.qhelp b/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.qhelp
new file mode 100644
index 000000000000..9e131e75d4e9
--- /dev/null
+++ b/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.qhelp
@@ -0,0 +1,31 @@
+
+
+
+Different overloads of the new
operator handle allocation failures in different ways.
+If new T
fails for some type T
, it throws a std::bad_alloc
exception,
+but new(std::nothrow) T
returns a null pointer. If the programmer does not use the corresponding
+method of error handling, allocation failure may go unhandled and could cause the program to behave in
+unexpected ways.
+
+
+
+
+Make sure that exceptions are handled appropriately if new T
is used. On the other hand,
+make sure to handle the possibility of null pointers if new(std::nothrow) T
is used.
+
+
+
+
+
+
+
+
+
+ CERT C++ Coding Standard:
+MEM52-CPP. Detect and handle memory allocation errors.
+
+
+
+
diff --git a/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql b/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql
new file mode 100644
index 000000000000..b06df90c8607
--- /dev/null
+++ b/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql
@@ -0,0 +1,251 @@
+/**
+ * @name Incorrect allocation-error handling
+ * @description Mixing up the failure conditions of 'operator new' and 'operator new(std::nothrow)' can result in unexpected behavior.
+ * @kind problem
+ * @id cpp/incorrect-allocation-error-handling
+ * @problem.severity warning
+ * @precision medium
+ * @tags correctness
+ * security
+ * external/cwe/cwe-570
+ */
+
+import cpp
+import semmle.code.cpp.valuenumbering.GlobalValueNumbering
+import semmle.code.cpp.controlflow.Guards
+
+/**
+ * A C++ `delete` or `delete[]` expression.
+ */
+class DeleteOrDeleteArrayExpr extends Expr {
+ DeleteOrDeleteArrayExpr() { this instanceof DeleteExpr or this instanceof DeleteArrayExpr }
+
+ DeallocationFunction getDeallocator() {
+ result = [this.(DeleteExpr).getDeallocator(), this.(DeleteArrayExpr).getDeallocator()]
+ }
+
+ Destructor getDestructor() {
+ result = [this.(DeleteExpr).getDestructor(), this.(DeleteArrayExpr).getDestructor()]
+ }
+}
+
+/** Gets the `Constructor` invoked when `newExpr` allocates memory. */
+Constructor getConstructorForAllocation(NewOrNewArrayExpr newExpr) {
+ result.getACallToThisFunction() = newExpr.getInitializer()
+}
+
+/** Gets the `Destructor` invoked when `deleteExpr` deallocates memory. */
+Destructor getDestructorForDeallocation(DeleteOrDeleteArrayExpr deleteExpr) {
+ result = deleteExpr.getDestructor()
+}
+
+/** Holds if the evaluation of `newExpr` may throw an exception. */
+predicate newMayThrow(NewOrNewArrayExpr newExpr) {
+ functionMayThrow(newExpr.getAllocator()) or
+ functionMayThrow(getConstructorForAllocation(newExpr))
+}
+
+/** Holds if the evaluation of `deleteExpr` may throw an exception. */
+predicate deleteMayThrow(DeleteOrDeleteArrayExpr deleteExpr) {
+ functionMayThrow(deleteExpr.getDeallocator()) or
+ functionMayThrow(getDestructorForDeallocation(deleteExpr))
+}
+
+/**
+ * Holds if the function may throw an exception when called. That is, if the body of the function looks
+ * like it might throw an exception, and the function does not have a `noexcept` or `throw()` specifier.
+ */
+predicate functionMayThrow(Function f) {
+ (not exists(f.getBlock()) or stmtMayThrow(f.getBlock())) and
+ not f.isNoExcept() and
+ not f.isNoThrow()
+}
+
+/** Holds if the evaluation of `stmt` may throw an exception. */
+predicate stmtMayThrow(Stmt stmt) {
+ stmtMayThrow(stmt.(BlockStmt).getAStmt())
+ or
+ convertedExprMayThrow(stmt.(ExprStmt).getExpr())
+ or
+ convertedExprMayThrow(stmt.(DeclStmt).getADeclaration().(Variable).getInitializer().getExpr())
+ or
+ exists(IfStmt ifStmt | ifStmt = stmt |
+ convertedExprMayThrow(ifStmt.getCondition()) or
+ stmtMayThrow([ifStmt.getThen(), ifStmt.getElse()])
+ )
+ or
+ exists(ConstexprIfStmt constIfStmt | constIfStmt = stmt |
+ stmtMayThrow([constIfStmt.getThen(), constIfStmt.getElse()])
+ )
+ or
+ exists(Loop loop | loop = stmt |
+ convertedExprMayThrow(loop.getCondition()) or
+ stmtMayThrow(loop.getStmt())
+ )
+ or
+ // The case for `Loop` already checked the condition and the statement.
+ convertedExprMayThrow(stmt.(RangeBasedForStmt).getUpdate())
+ or
+ // The case for `Loop` already checked the condition and the statement.
+ exists(ForStmt forStmt | forStmt = stmt |
+ stmtMayThrow(forStmt.getInitialization())
+ or
+ convertedExprMayThrow(forStmt.getUpdate())
+ )
+ or
+ exists(SwitchStmt switchStmt | switchStmt = stmt |
+ convertedExprMayThrow(switchStmt.getExpr()) or
+ stmtMayThrow(switchStmt.getStmt())
+ )
+ or
+ // NOTE: We don't include `TryStmt` as those exceptions are not "observable" outside the function.
+ stmtMayThrow(stmt.(Handler).getBlock())
+ or
+ convertedExprMayThrow(stmt.(CoReturnStmt).getExpr())
+ or
+ convertedExprMayThrow(stmt.(ReturnStmt).getExpr())
+}
+
+/** Holds if the evaluation of `e` (including conversions) may throw an exception. */
+predicate convertedExprMayThrow(Expr e) {
+ exprMayThrow(e)
+ or
+ convertedExprMayThrow(e.getConversion())
+}
+
+/** Holds if the evaluation of `e` may throw an exception. */
+predicate exprMayThrow(Expr e) {
+ e instanceof DynamicCast
+ or
+ e instanceof TypeidOperator
+ or
+ e instanceof ThrowExpr
+ or
+ newMayThrow(e)
+ or
+ deleteMayThrow(e)
+ or
+ convertedExprMayThrow(e.(UnaryOperation).getOperand())
+ or
+ exists(BinaryOperation binOp | binOp = e |
+ convertedExprMayThrow([binOp.getLeftOperand(), binOp.getRightOperand()])
+ )
+ or
+ exists(Assignment assign | assign = e |
+ convertedExprMayThrow([assign.getLValue(), assign.getRValue()])
+ )
+ or
+ exists(CommaExpr comma | comma = e |
+ convertedExprMayThrow([comma.getLeftOperand(), comma.getRightOperand()])
+ )
+ or
+ exists(StmtExpr stmtExpr | stmtExpr = e |
+ convertedExprMayThrow(stmtExpr.getResultExpr()) or
+ stmtMayThrow(stmtExpr.getStmt())
+ )
+ or
+ convertedExprMayThrow(e.(Conversion).getExpr())
+ or
+ exists(FunctionCall fc | fc = e |
+ not exists(fc.getTarget()) or
+ functionMayThrow(fc.getTarget()) or
+ convertedExprMayThrow(fc.getAnArgument())
+ )
+}
+
+/** The `std::nothrow_t` class and its `bsl` variant. */
+class NoThrowType extends Struct {
+ NoThrowType() { this.hasGlobalOrStdOrBslName("nothrow_t") }
+}
+
+/** An allocator that might throw an exception. */
+class ThrowingAllocator extends Function {
+ ThrowingAllocator() {
+ exists(NewOrNewArrayExpr newExpr |
+ newExpr.getAllocator() = this and
+ // Exclude custom overloads of `operator new`.
+ // What we really want here is to only include the functions that satisfy `functionMayThrow`, but
+ // there seems to be examples where `throw()` isn't extracted (which causes false positives).
+ //
+ // As noted in the QLDoc for `Function.getAllocatorCall`:
+ //
+ // "As a rule of thumb, there will be an allocator call precisely when the type
+ // being allocated has a custom `operator new`, or when an argument list appears
+ // after the `new` keyword and before the name of the type being allocated.
+ //
+ // In particular note that uses of placement-new and nothrow-new will have an
+ // allocator call."
+ //
+ // So we say an allocator might throw if:
+ // 1. It doesn't have a body
+ // 2. there isn't a parameter with type `nothrow_t`
+ // 3. the allocator isn't marked with `throw()` or `noexcept`.
+ not exists(this.getBlock()) and
+ not exists(Parameter p | p = this.getAParameter() |
+ p.getUnspecifiedType() instanceof NoThrowType
+ ) and
+ not this.isNoExcept() and
+ not this.isNoThrow()
+ )
+ }
+}
+
+/** The `std::bad_alloc` exception and its `bsl` variant. */
+class BadAllocType extends Class {
+ BadAllocType() { this.hasGlobalOrStdOrBslName("bad_alloc") }
+}
+
+/**
+ * A catch block that catches a `std::bad_alloc` (or any of its superclasses), or a catch
+ * block that catches every exception (i.e., `catch(...)`).
+ */
+class BadAllocCatchBlock extends CatchBlock {
+ BadAllocCatchBlock() {
+ this.getParameter().getUnspecifiedType().stripType() =
+ any(BadAllocType badAlloc).getABaseClass*()
+ or
+ not exists(this.getParameter())
+ }
+}
+
+/**
+ * Holds if `newExpr` is embedded in a `try` statement with a catch block `catchBlock` that
+ * catches a `std::bad_alloc` exception, but nothing in the `try` block (including the `newExpr`)
+ * will throw that exception.
+ */
+predicate noThrowInTryBlock(NewOrNewArrayExpr newExpr, BadAllocCatchBlock catchBlock) {
+ exists(TryStmt try |
+ not stmtMayThrow(try.getStmt()) and
+ try.getACatchClause() = catchBlock and
+ newExpr.getEnclosingBlock().getEnclosingBlock*() = try.getStmt()
+ )
+}
+
+/**
+ * Holds if `newExpr` is handles allocation failures by throwing an exception, yet
+ * the guard condition `guard` compares the result of `newExpr` to a null value.
+ */
+predicate nullCheckInThrowingNew(NewOrNewArrayExpr newExpr, GuardCondition guard) {
+ newExpr.getAllocator() instanceof ThrowingAllocator and
+ (
+ // Handles null comparisons.
+ guard.ensuresEq(globalValueNumber(newExpr).getAnExpr(), any(NullValue null), _, _, _)
+ or
+ // Handles `if(ptr)` and `if(!ptr)` cases.
+ guard = globalValueNumber(newExpr).getAnExpr()
+ )
+}
+
+from NewOrNewArrayExpr newExpr, Element element, string msg, string elementString
+where
+ not newExpr.isFromUninstantiatedTemplate(_) and
+ (
+ noThrowInTryBlock(newExpr, element) and
+ msg = "This allocation cannot throw. $@ is unnecessary." and
+ elementString = "This catch block"
+ or
+ nullCheckInThrowingNew(newExpr, element) and
+ msg = "This allocation cannot return null. $@ is unnecessary." and
+ elementString = "This check"
+ )
+select newExpr, msg, element, elementString
diff --git a/cpp/ql/src/Security/CWE/CWE-676/DangerousFunctionOverflow.ql b/cpp/ql/src/Security/CWE/CWE-676/DangerousFunctionOverflow.ql
index d883b2e73566..35955665a9e2 100644
--- a/cpp/ql/src/Security/CWE/CWE-676/DangerousFunctionOverflow.ql
+++ b/cpp/ql/src/Security/CWE/CWE-676/DangerousFunctionOverflow.ql
@@ -3,6 +3,7 @@
* @description Use of a standard library function that does not guard against buffer overflow.
* @kind problem
* @problem.severity error
+ * @security-severity 10.0
* @precision very-high
* @id cpp/dangerous-function-overflow
* @tags reliability
diff --git a/cpp/ql/src/Security/CWE/CWE-676/DangerousUseOfCin.ql b/cpp/ql/src/Security/CWE/CWE-676/DangerousUseOfCin.ql
index 32bdfbaf1007..07a7ef1de9b6 100644
--- a/cpp/ql/src/Security/CWE/CWE-676/DangerousUseOfCin.ql
+++ b/cpp/ql/src/Security/CWE/CWE-676/DangerousUseOfCin.ql
@@ -4,6 +4,7 @@
* may be dangerous.
* @kind problem
* @problem.severity error
+ * @security-severity 10.0
* @precision high
* @id cpp/dangerous-cin
* @tags reliability
diff --git a/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.ql b/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.ql
index 4316fe229b26..4e281b238bce 100644
--- a/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.ql
+++ b/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.ql
@@ -3,6 +3,7 @@
* @description Use of a standard library function that is not thread-safe.
* @kind problem
* @problem.severity warning
+ * @security-severity 10.0
* @precision high
* @id cpp/potentially-dangerous-function
* @tags reliability
diff --git a/cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.ql b/cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.ql
index 0d866ff1196f..65551a1f138b 100644
--- a/cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.ql
+++ b/cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.ql
@@ -6,6 +6,7 @@
* @kind problem
* @id cpp/incorrect-string-type-conversion
* @problem.severity error
+ * @security-severity 8.8
* @precision high
* @tags security
* external/cwe/cwe-704
diff --git a/cpp/ql/src/Security/CWE/CWE-732/DoNotCreateWorldWritable.ql b/cpp/ql/src/Security/CWE/CWE-732/DoNotCreateWorldWritable.ql
index 95790298347d..1fd55a02d016 100644
--- a/cpp/ql/src/Security/CWE/CWE-732/DoNotCreateWorldWritable.ql
+++ b/cpp/ql/src/Security/CWE/CWE-732/DoNotCreateWorldWritable.ql
@@ -3,6 +3,7 @@
* @description Creating a file that is world-writable can allow an attacker to write to the file.
* @kind problem
* @problem.severity warning
+ * @security-severity 7.8
* @precision medium
* @id cpp/world-writable-file-creation
* @tags security
diff --git a/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql
index 3e4ebfadac9a..bf6738263474 100644
--- a/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql
+++ b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql
@@ -7,6 +7,7 @@
* @id cpp/unsafe-dacl-security-descriptor
* @kind problem
* @problem.severity error
+ * @security-severity 7.8
* @precision high
* @tags security
* external/cwe/cwe-732
diff --git a/cpp/ql/src/Security/CWE/CWE-764/LockOrderCycle.ql b/cpp/ql/src/Security/CWE/CWE-764/LockOrderCycle.ql
index f53f84a4fb02..cd5d87718078 100644
--- a/cpp/ql/src/Security/CWE/CWE-764/LockOrderCycle.ql
+++ b/cpp/ql/src/Security/CWE/CWE-764/LockOrderCycle.ql
@@ -5,6 +5,7 @@
* @kind problem
* @id cpp/lock-order-cycle
* @problem.severity error
+ * @security-severity 5.0
* @tags security
* external/cwe/cwe-764
* external/cwe/cwe-833
diff --git a/cpp/ql/src/Security/CWE/CWE-764/TwiceLocked.ql b/cpp/ql/src/Security/CWE/CWE-764/TwiceLocked.ql
index 0eb7e8451e5e..051ad2eeeeab 100644
--- a/cpp/ql/src/Security/CWE/CWE-764/TwiceLocked.ql
+++ b/cpp/ql/src/Security/CWE/CWE-764/TwiceLocked.ql
@@ -5,6 +5,7 @@
* @kind problem
* @id cpp/twice-locked
* @problem.severity error
+ * @security-severity 5.0
* @precision low
* @tags security
* external/cwe/cwe-764
diff --git a/cpp/ql/src/Security/CWE/CWE-764/UnreleasedLock.ql b/cpp/ql/src/Security/CWE/CWE-764/UnreleasedLock.ql
index 1c0ee4a500fc..dd224352b12b 100644
--- a/cpp/ql/src/Security/CWE/CWE-764/UnreleasedLock.ql
+++ b/cpp/ql/src/Security/CWE/CWE-764/UnreleasedLock.ql
@@ -5,6 +5,7 @@
* @kind problem
* @id cpp/unreleased-lock
* @problem.severity error
+ * @security-severity 5.0
* @precision low
* @tags security
* external/cwe/cwe-764
diff --git a/cpp/ql/src/Security/CWE/CWE-807/TaintedCondition.ql b/cpp/ql/src/Security/CWE/CWE-807/TaintedCondition.ql
index e60f592b2af5..64505ee82836 100644
--- a/cpp/ql/src/Security/CWE/CWE-807/TaintedCondition.ql
+++ b/cpp/ql/src/Security/CWE/CWE-807/TaintedCondition.ql
@@ -5,6 +5,7 @@
* attack.
* @kind path-problem
* @problem.severity warning
+ * @security-severity 7.5
* @precision medium
* @id cpp/tainted-permissions-check
* @tags security
diff --git a/cpp/ql/src/Security/CWE/CWE-835/InfiniteLoopWithUnsatisfiableExitCondition.ql b/cpp/ql/src/Security/CWE/CWE-835/InfiniteLoopWithUnsatisfiableExitCondition.ql
index e088b9a666df..3db5e15874b5 100644
--- a/cpp/ql/src/Security/CWE/CWE-835/InfiniteLoopWithUnsatisfiableExitCondition.ql
+++ b/cpp/ql/src/Security/CWE/CWE-835/InfiniteLoopWithUnsatisfiableExitCondition.ql
@@ -6,6 +6,7 @@
* @kind problem
* @id cpp/infinite-loop-with-unsatisfiable-exit-condition
* @problem.severity warning
+ * @security-severity 7.5
* @tags security
* external/cwe/cwe-835
*/
diff --git a/cpp/ql/src/Summary/LinesOfCode.ql b/cpp/ql/src/Summary/LinesOfCode.ql
index 3b2aa2ac4c9c..2d816b349e8d 100644
--- a/cpp/ql/src/Summary/LinesOfCode.ql
+++ b/cpp/ql/src/Summary/LinesOfCode.ql
@@ -4,6 +4,7 @@
* @description The total number of lines of C/C++ code across all files, including system headers, libraries, and auto-generated files. This is a useful metric of the size of a database. For all files that were seen during the build, this query counts the lines of code, excluding whitespace or comments.
* @kind metric
* @tags summary
+ * lines-of-code
*/
import cpp
diff --git a/cpp/ql/src/Summary/LinesOfUserCode.ql b/cpp/ql/src/Summary/LinesOfUserCode.ql
new file mode 100644
index 000000000000..67d3aa6a8e05
--- /dev/null
+++ b/cpp/ql/src/Summary/LinesOfUserCode.ql
@@ -0,0 +1,17 @@
+/**
+ * @name Total lines of user written C/C++ code in the database
+ * @description The total number of lines of C/C++ code from the source code directory, excluding auto-generated files. This query counts the lines of code, excluding whitespace or comments. Note: If external libraries are included in the codebase either in a checked-in virtual environment or as vendored code, that will currently be counted as user written code.
+ * @kind metric
+ * @tags summary
+ * lines-of-code
+ * @id cpp/summary/lines-of-user-code
+ */
+
+import cpp
+import semmle.code.cpp.AutogeneratedFile
+
+select sum(File f |
+ f.fromSource() and exists(f.getRelativePath()) and not f instanceof AutogeneratedFile
+ |
+ f.getMetrics().getNumberOfLinesOfCode()
+ )
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.cpp
new file mode 100644
index 000000000000..3b85835fff98
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.cpp
@@ -0,0 +1,28 @@
+#include
+#include
+#include
+
+int main(int argc, char ** argv) {
+
+ if (argc != 2) {
+ throw std::runtime_error("Give me a string!");
+ }
+
+ pqxx::connection c;
+ pqxx::work w(c);
+
+ // BAD
+ char *userName = argv[1];
+ char query1[1000] = {0};
+ sprintf(query1, "SELECT UID FROM USERS where name = \"%s\"", userName);
+ pqxx::row r = w.exec1(query1);
+ w.commit();
+ std::cout << r[0].as() << std::endl;
+
+ // GOOD
+ pqxx::result r2 = w.exec("SELECT " + w.quote(argv[1]));
+ w.commit();
+ std::cout << r2[0][0].c_str() << std::endl;
+
+ return 0;
+}
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.qhelp
new file mode 100644
index 000000000000..1c01b3e4f3a6
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.qhelp
@@ -0,0 +1,31 @@
+
+
+
+The code passes user input as part of a SQL query without escaping special elements.
+It generates a SQL query to Postgres using sprintf
,
+with the user-supplied data directly passed as an argument
+to sprintf
. This leaves the code vulnerable to attack by SQL Injection.
+
+
+
+
+Use a library routine to escape characters in the user-supplied
+string before converting it to SQL. Use esc
and quote
pqxx library functions.
+
+
+
+
+
+
+
+
+MSDN Library: SQL Injection.
+
+
+
+
+
+
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql
new file mode 100644
index 000000000000..8de55953b158
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql
@@ -0,0 +1,113 @@
+/**
+ * @name Uncontrolled data in SQL query to Postgres
+ * @description Including user-supplied data in a SQL query to Postgres
+ * without neutralizing special elements can make code
+ * vulnerable to SQL Injection.
+ * @kind path-problem
+ * @problem.severity error
+ * @precision high
+ * @id cpp/sql-injection-via-pqxx
+ * @tags security
+ * external/cwe/cwe-089
+ */
+
+import cpp
+import semmle.code.cpp.security.Security
+import semmle.code.cpp.dataflow.TaintTracking
+import DataFlow::PathGraph
+
+predicate pqxxTransationClassNames(string className, string namespace) {
+ namespace = "pqxx" and
+ className in [
+ "dbtransaction", "nontransaction", "basic_robusttransaction", "robusttransaction",
+ "subtransaction", "transaction", "basic_transaction", "transaction_base", "work"
+ ]
+}
+
+predicate pqxxConnectionClassNames(string className, string namespace) {
+ namespace = "pqxx" and
+ className in ["connection_base", "basic_connection", "connection"]
+}
+
+predicate pqxxTransactionSqlArgument(string function, int arg) {
+ function = "exec" and arg = 0
+ or
+ function = "exec0" and arg = 0
+ or
+ function = "exec1" and arg = 0
+ or
+ function = "exec_n" and arg = 1
+ or
+ function = "exec_params" and arg = 0
+ or
+ function = "exec_params0" and arg = 0
+ or
+ function = "exec_params1" and arg = 0
+ or
+ function = "exec_params_n" and arg = 1
+ or
+ function = "query_value" and arg = 0
+ or
+ function = "stream" and arg = 0
+}
+
+predicate pqxxConnectionSqlArgument(string function, int arg) { function = "prepare" and arg = 1 }
+
+Expr getPqxxSqlArgument() {
+ exists(FunctionCall fc, Expr e, int argIndex, UserType t |
+ // examples: 'work' for 'work.exec(...)'; '->' for 'tx->exec()'.
+ e = fc.getQualifier() and
+ // to find ConnectionHandle/TransationHandle and similar classes which override '->' operator behavior
+ // and return pointer to a connection/transation object
+ e.getType().refersTo(t) and
+ // transaction exec and connection prepare variations
+ (
+ pqxxTransationClassNames(t.getName(), t.getNamespace().getName()) and
+ pqxxTransactionSqlArgument(fc.getTarget().getName(), argIndex)
+ or
+ pqxxConnectionClassNames(t.getName(), t.getNamespace().getName()) and
+ pqxxConnectionSqlArgument(fc.getTarget().getName(), argIndex)
+ ) and
+ result = fc.getArgument(argIndex)
+ )
+}
+
+predicate pqxxEscapeArgument(string function, int arg) {
+ arg = 0 and
+ function in ["esc", "esc_raw", "quote", "quote_raw", "quote_name", "quote_table", "esc_like"]
+}
+
+predicate isEscapedPqxxArgument(Expr argExpr) {
+ exists(FunctionCall fc, Expr e, int argIndex, UserType t |
+ // examples: 'work' for 'work.exec(...)'; '->' for 'tx->exec()'.
+ e = fc.getQualifier() and
+ // to find ConnectionHandle/TransationHandle and similar classes which override '->' operator behavior
+ // and return pointer to a connection/transation object
+ e.getType().refersTo(t) and
+ // transaction and connection escape functions
+ (
+ pqxxTransationClassNames(t.getName(), t.getNamespace().getName()) or
+ pqxxConnectionClassNames(t.getName(), t.getNamespace().getName())
+ ) and
+ pqxxEscapeArgument(fc.getTarget().getName(), argIndex) and
+ // is escaped arg == argExpr
+ argExpr = fc.getArgument(argIndex)
+ )
+}
+
+class Configuration extends TaintTracking::Configuration {
+ Configuration() { this = "SqlPqxxTainted" }
+
+ override predicate isSource(DataFlow::Node source) { isUserInput(source.asExpr(), _) }
+
+ override predicate isSink(DataFlow::Node sink) { sink.asExpr() = getPqxxSqlArgument() }
+
+ override predicate isSanitizer(DataFlow::Node node) { isEscapedPqxxArgument(node.asExpr()) }
+}
+
+from DataFlow::PathNode source, DataFlow::PathNode sink, Configuration config, string taintCause
+where
+ config.hasFlowPath(source, sink) and
+ isUserInput(source.getNode().asExpr(), taintCause)
+select sink, source, sink, "This argument to a SQL query function is derived from $@", source,
+ "user input (" + taintCause + ")"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c
new file mode 100644
index 000000000000..53a50841977a
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c
@@ -0,0 +1,14 @@
+while(intIndex > 2)
+{
+ ...
+ intIndex--;
+ ...
+} // GOOD: correct cycle
+...
+while(intIndex > 2)
+{
+ ...
+ int intIndex;
+ intIndex--;
+ ...
+} // BAD: the variable used in the condition does not change.
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp
new file mode 100644
index 000000000000..5234212f7ca7
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp
@@ -0,0 +1,26 @@
+
+
+
+Using variables with the same name is dangerous. However, such a situation inside the while loop can create an infinite loop exhausting resources. Requires the attention of developers.
+
+
+
+We recommend not to use local variables inside a loop if their names are the same as the variables in the condition of this loop.
+
+
+
+The following example demonstrates an erroneous and corrected use of a local variable within a loop.
+
+
+
+
+
+
+ CERT C Coding Standard:
+ DCL01-C. Do not reuse variable names in subscopes.
+
+
+
+
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql
new file mode 100644
index 000000000000..e73f36145c60
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql
@@ -0,0 +1,60 @@
+/**
+ * @name Errors When Using Variable Declaration Inside Loop
+ * @description Using variables with the same name is dangerous.
+ * However, such a situation inside the while loop can create an infinite loop exhausting resources.
+ * Requires the attention of developers.
+ * @kind problem
+ * @id cpp/errors-when-using-variable-declaration-inside-loop
+ * @problem.severity warning
+ * @precision medium
+ * @tags correctness
+ * security
+ * external/cwe/cwe-1126
+ */
+
+import cpp
+
+/**
+ * Errors when using a variable declaration inside a loop.
+ */
+class DangerousWhileLoop extends WhileStmt {
+ Expr exp;
+ Declaration dl;
+
+ DangerousWhileLoop() {
+ this = dl.getParentScope().(BlockStmt).getParent*() and
+ exp = this.getCondition().getAChild*() and
+ not exp instanceof PointerFieldAccess and
+ not exp instanceof ValueFieldAccess and
+ exp.(VariableAccess).getTarget().getName() = dl.getName() and
+ not exp.getParent*() instanceof FunctionCall
+ }
+
+ Declaration getDeclaration() { result = dl }
+
+ /** Holds when there are changes to the variables involved in the condition. */
+ predicate isUseThisVariable() {
+ exists(Variable v |
+ this.getCondition().getAChild*().(VariableAccess).getTarget() = v and
+ (
+ exists(Assignment aexp |
+ this = aexp.getEnclosingStmt().getParentStmt*() and
+ (
+ aexp.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() = v
+ or
+ aexp.getLValue().(VariableAccess).getTarget() = v
+ )
+ )
+ or
+ exists(CrementOperation crm |
+ this = crm.getEnclosingStmt().getParentStmt*() and
+ crm.getOperand().(VariableAccess).getTarget() = v
+ )
+ )
+ )
+ }
+}
+
+from DangerousWhileLoop lp
+where not lp.isUseThisVariable()
+select lp.getDeclaration(), "A variable with this name is used in the $@ condition.", lp, "loop"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.c b/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.c
new file mode 100644
index 000000000000..ce7fcc6846e6
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.c
@@ -0,0 +1,14 @@
+...
+ buf = malloc(intSize);
+...
+ free(buf);
+ buf = NULL;
+ if(buf) free(buf); // GOOD
+...
+
+...
+ buf = malloc(intSize);
+...
+ free(buf);
+ if(buf) free(buf); // BAD: the cleanup function does not zero out the pointer
+...
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.qhelp
new file mode 100644
index 000000000000..69ef348be348
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.qhelp
@@ -0,0 +1,26 @@
+
+
+
+Freeing a previously allocated resource twice can lead to various vulnerabilities in the program.
+
+
+
+We recommend that you exclude situations of possible double release. For example, use the assignment NULL to a freed variable.
+
+
+
+The following example demonstrates an erroneous and corrected use of freeing a pointer.
+
+
+
+
+
+
+ CERT C Coding Standard:
+ MEM30-C. Do not access freed memory.
+
+
+
+
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.ql b/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.ql
new file mode 100644
index 000000000000..0544c2aefd57
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.ql
@@ -0,0 +1,43 @@
+/**
+ * @name Errors When Double Free
+ * @description Freeing a previously allocated resource twice can lead to various vulnerabilities in the program.
+ * @kind problem
+ * @id cpp/double-free
+ * @problem.severity warning
+ * @precision medium
+ * @tags security
+ * external/cwe/cwe-415
+ */
+
+import cpp
+
+from FunctionCall fc, FunctionCall fc2, LocalScopeVariable v
+where
+ freeCall(fc, v.getAnAccess()) and
+ freeCall(fc2, v.getAnAccess()) and
+ fc != fc2 and
+ fc.getASuccessor*() = fc2 and
+ not exists(Expr exptmp |
+ (exptmp = v.getAnAssignedValue() or exptmp.(AddressOfExpr).getOperand() = v.getAnAccess()) and
+ exptmp = fc.getASuccessor*() and
+ exptmp = fc2.getAPredecessor*()
+ ) and
+ not exists(FunctionCall fctmp |
+ not fctmp instanceof DeallocationExpr and
+ fctmp = fc.getASuccessor*() and
+ fctmp = fc2.getAPredecessor*() and
+ fctmp.getAnArgument().(VariableAccess).getTarget() = v
+ ) and
+ (
+ fc.getTarget().hasGlobalOrStdName("realloc") and
+ (
+ not fc.getParent*() instanceof IfStmt and
+ not exists(IfStmt iftmp |
+ iftmp.getCondition().getAChild*().(VariableAccess).getTarget().getAnAssignedValue() = fc
+ )
+ )
+ or
+ not fc.getTarget().hasGlobalOrStdName("realloc")
+ )
+select fc2.getArgument(0),
+ "This pointer may have already been cleared in the line " + fc.getLocation().getStartLine() + "."
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.cpp
deleted file mode 100644
index df69886e97bd..000000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-// BAD: on memory allocation error, the program terminates.
-void badFunction(const int *source, std::size_t length) noexcept {
- int * dest = new int[length];
- std::memset(dest, 0, length);
-// ..
-}
-// GOOD: memory allocation error will be handled.
-void goodFunction(const int *source, std::size_t length) noexcept {
- try {
- int * dest = new int[length];
- } catch(std::bad_alloc) {
- // ...
- }
- std::memset(dest, 0, length);
-// ..
-}
-// BAD: memory allocation error will not be handled.
-void badFunction(const int *source, std::size_t length) noexcept {
- try {
- int * dest = new (std::nothrow) int[length];
- } catch(std::bad_alloc) {
- // ...
- }
- std::memset(dest, 0, length);
-// ..
-}
-// GOOD: memory allocation error will be handled.
-void goodFunction(const int *source, std::size_t length) noexcept {
- int * dest = new (std::nothrow) int[length];
- if (!dest) {
- return;
- }
- std::memset(dest, 0, length);
-// ..
-}
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.qhelp
deleted file mode 100644
index 9e6cb2d89cec..000000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.qhelp
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-When using the new
operator to allocate memory, you need to pay attention to the different ways of detecting errors. ::operator new(std::size_t)
throws an exception on error, whereas ::operator new(std::size_t, const std::nothrow_t &)
returns zero on error. The programmer can get confused and check the error that occurs when allocating memory incorrectly. That can lead to an unhandled program termination or to a violation of the program logic.
-
-
-
-
-Use the correct error detection method corresponding with the memory allocation.
-
-
-
-The following example demonstrates various approaches to detecting memory allocation errors using the new
operator.
-
-
-
-
-
-
- CERT C++ Coding Standard:
-MEM52-CPP. Detect and handle memory allocation errors.
-
-
-
-
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.ql b/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.ql
deleted file mode 100644
index 4869da7e6f30..000000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.ql
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * @name Detect And Handle Memory Allocation Errors
- * @description --::operator new(std::size_t) throws an exception on error, and ::operator new(std::size_t, const std::nothrow_t &) returns zero on error.
- * --the programmer can get confused when check the error that occurs when allocating memory incorrectly.
- * @kind problem
- * @id cpp/detect-and-handle-memory-allocation-errors
- * @problem.severity warning
- * @precision medium
- * @tags correctness
- * security
- * external/cwe/cwe-570
- */
-
-import cpp
-
-/**
- * Lookup if condition compare with 0
- */
-class IfCompareWithZero extends IfStmt {
- IfCompareWithZero() {
- this.getCondition().(EQExpr).getAChild().getValue() = "0"
- or
- this.getCondition().(NEExpr).getAChild().getValue() = "0" and
- this.hasElse()
- or
- this.getCondition().(NEExpr).getAChild().getValue() = "0" and
- this.getThen().getAChild*() instanceof ReturnStmt
- }
-}
-
-/**
- * lookup for calls to `operator new`, with incorrect error handling.
- */
-class WrongCheckErrorOperatorNew extends FunctionCall {
- Expr exp;
-
- WrongCheckErrorOperatorNew() {
- this = exp.(NewOrNewArrayExpr).getAChild().(FunctionCall) and
- (
- this.getTarget().hasGlobalOrStdName("operator new")
- or
- this.getTarget().hasGlobalOrStdName("operator new[]")
- )
- }
-
- /**
- * Holds if handler `try ... catch` exists.
- */
- predicate isExistsTryCatchBlock() {
- exists(TryStmt ts | this.getEnclosingStmt() = ts.getStmt().getAChild*())
- }
-
- /**
- * Holds if results call `operator new` check in `operator if`.
- */
- predicate isExistsIfCondition() {
- exists(IfCompareWithZero ifc, AssignExpr aex, Initializer it |
- // call `operator new` directly from the condition of `operator if`.
- this = ifc.getCondition().getAChild*()
- or
- // check results call `operator new` with variable appropriation
- postDominates(ifc, this) and
- aex.getAChild() = exp and
- ifc.getCondition().getAChild().(VariableAccess).getTarget() =
- aex.getLValue().(VariableAccess).getTarget()
- or
- // check results call `operator new` with declaration variable
- postDominates(ifc, this) and
- exp = it.getExpr() and
- it.getDeclaration() = ifc.getCondition().getAChild().(VariableAccess).getTarget()
- )
- }
-
- /**
- * Holds if `(std::nothrow)` or `(std::noexcept)` exists in call `operator new`.
- */
- predicate isExistsNothrow() { getTarget().isNoExcept() or getTarget().isNoThrow() }
-}
-
-from WrongCheckErrorOperatorNew op
-where
- // use call `operator new` with `(std::nothrow)` and checking error using `try ... catch` block and not `operator if`
- op.isExistsNothrow() and not op.isExistsIfCondition() and op.isExistsTryCatchBlock()
- or
- // use call `operator new` without `(std::nothrow)` and checking error using `operator if` and not `try ... catch` block
- not op.isExistsNothrow() and not op.isExistsTryCatchBlock() and op.isExistsIfCondition()
-select op, "memory allocation error check is incorrect or missing"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.c b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.c
new file mode 100644
index 000000000000..1f1f10b89cc5
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.c
@@ -0,0 +1,17 @@
+while(flagsLoop)
+{
+ ...
+ if(flagsIf) break;
+ ...
+}while(flagsLoop); // BAD: when exiting through `break`, it is possible to get into an eternal loop.
+...
+while(flagsLoop)
+{
+ ...
+ if(flagsIf) break;
+ ...
+} // GOOD: correct cycle
+...
+if(intA+intB) return 1; // BAD: possibly no comparison
+...
+if(intA+intB>intC) return 1; // GOOD: correct comparison
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.qhelp
new file mode 100644
index 000000000000..4167ce57d65e
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.qhelp
@@ -0,0 +1,28 @@
+
+
+
+In some situations, after code refactoring, parts of the old constructs may remain. They are correctly accepted by the compiler, but can critically affect program execution. For example, if you switch from `do {...} while ();` to `while () {...}` forgetting to remove the old construct completely, you get `while(){...}while();` which may be vulnerable. These code snippets look suspicious and require the developer's attention.
+
+
+
+
+
+We recommend that you use more explicit code transformations.
+
+
+
+The following example demonstrates the erroneous and corrected sections of the code.
+
+
+
+
+
+
+ CWE Common Weakness Enumeration:
+ CWE-691: Insufficient Control Flow Management.
+
+
+
+
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.ql b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.ql
new file mode 100644
index 000000000000..163305dd0399
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.ql
@@ -0,0 +1,119 @@
+/**
+ * @name Errors After Refactoring
+ * @description --In some situations, after code refactoring, parts of the old constructs may remain.
+ * --They are correctly accepted by the compiler, but can critically affect program execution.
+ * --For example, if you switch from `do {...} while ();` to `while () {...}` with errors, you run the risk of running out of resources.
+ * --These code snippets look suspicious and require the developer's attention.
+ * @kind problem
+ * @id cpp/errors-after-refactoring
+ * @problem.severity warning
+ * @precision medium
+ * @tags correctness
+ * security
+ * external/cwe/cwe-691
+ */
+
+import cpp
+import semmle.code.cpp.valuenumbering.HashCons
+import semmle.code.cpp.valuenumbering.GlobalValueNumbering
+
+/**
+ * Using `while` directly after the body of another` while`.
+ */
+class UsingWhileAfterWhile extends WhileStmt {
+ /**
+ * Using a loop call after another loop has finished running can result in an eternal loop.
+ * For example, perhaps as a result of refactoring, the `do ... while ()` loop was incorrectly corrected.
+ * Even in the case of deliberate use of such an expression, it is better to correct it.
+ */
+ UsingWhileAfterWhile() {
+ exists(WhileStmt wh1 |
+ wh1.getStmt().getAChild*().(BreakStmt).(ControlFlowNode).getASuccessor().getASuccessor() =
+ this and
+ hashCons(wh1.getCondition()) = hashCons(this.getCondition()) and
+ this.getStmt() instanceof EmptyStmt
+ )
+ or
+ exists(ForStmt fr1 |
+ fr1.getStmt().getAChild*().(BreakStmt).(ControlFlowNode).getASuccessor().getASuccessor() =
+ this and
+ hashCons(fr1.getCondition()) = hashCons(this.getCondition()) and
+ this.getStmt() instanceof EmptyStmt
+ )
+ }
+}
+
+/**
+ * Using arithmetic in a condition.
+ */
+class UsingArithmeticInComparison extends BinaryArithmeticOperation {
+ /**
+ * Using arithmetic operations in a comparison operation can be dangerous.
+ * For example, part of the comparison may have been lost as a result of refactoring.
+ * Even if you deliberately use such an expression, it is better to add an explicit comparison.
+ */
+ UsingArithmeticInComparison() {
+ this.getParent*() instanceof IfStmt and
+ not this.getAChild*().isConstant() and
+ not this.getParent*() instanceof Call and
+ not this.getParent*() instanceof AssignExpr and
+ not this.getParent*() instanceof ArrayExpr and
+ not this.getParent*() instanceof RemExpr and
+ not this.getParent*() instanceof AssignBitwiseOperation and
+ not this.getParent*() instanceof AssignArithmeticOperation and
+ not this.getParent*() instanceof EqualityOperation and
+ not this.getParent*() instanceof RelationalOperation
+ }
+
+ /** Holds when the expression is inside the loop body. */
+ predicate insideTheLoop() { exists(Loop lp | lp.getStmt().getAChild*() = this.getParent*()) }
+
+ /** Holds when the expression is used in binary operations. */
+ predicate workingWithValue() {
+ this.getParent*() instanceof BinaryBitwiseOperation or
+ this.getParent*() instanceof NotExpr
+ }
+
+ /** Holds when the expression contains a pointer. */
+ predicate workingWithPointer() {
+ this.getAChild*().getFullyConverted().getType() instanceof DerivedType
+ }
+
+ /** Holds when a null comparison expression exists. */
+ predicate compareWithZero() {
+ exists(Expr exp |
+ exp instanceof ComparisonOperation and
+ (
+ globalValueNumber(exp.getAChild*()) = globalValueNumber(this) or
+ hashCons(exp.getAChild*()) = hashCons(this)
+ ) and
+ (
+ exp.(ComparisonOperation).getLeftOperand().getValue() = "0" or
+ exp.(ComparisonOperation).getRightOperand().getValue() = "0"
+ )
+ )
+ }
+
+ /** Holds when a comparison expression exists. */
+ predicate compareWithOutZero() {
+ exists(Expr exp |
+ exp instanceof ComparisonOperation and
+ (
+ globalValueNumber(exp.getAChild*()) = globalValueNumber(this) or
+ hashCons(exp.getAChild*()) = hashCons(this)
+ )
+ )
+ }
+}
+
+from Expr exp
+where
+ exp instanceof UsingArithmeticInComparison and
+ not exp.(UsingArithmeticInComparison).workingWithValue() and
+ not exp.(UsingArithmeticInComparison).workingWithPointer() and
+ not exp.(UsingArithmeticInComparison).insideTheLoop() and
+ not exp.(UsingArithmeticInComparison).compareWithZero() and
+ exp.(UsingArithmeticInComparison).compareWithOutZero()
+ or
+ exists(WhileStmt wst | wst instanceof UsingWhileAfterWhile and exp = wst.getCondition())
+select exp, "this expression needs your attention"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.c b/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.c
deleted file mode 100644
index 060a22b5c186..000000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.c
+++ /dev/null
@@ -1,4 +0,0 @@
-
-strncat(dest, source, sizeof(dest) - strlen(dest)); // BAD: writes a zero byte past the `dest` buffer.
-
-strncat(dest, source, sizeof(dest) - strlen(dest) -1); // GOOD: Reserves space for the zero byte.
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.qhelp
deleted file mode 100644
index 5c2154097ec9..000000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.qhelp
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-The standard library function strncat(dest, source, count)
appends the source
string to the dest
string. count
specifies the maximum number of characters to append and must be less than the remaining space in the target buffer. Calls of the form strncat (dest, source, sizeof (dest) - strlen (dest))
set the third argument to one more than possible. So when the dest
is full, the expression sizeof (dest) - strlen (dest)
will be equal to one, and not zero as the programmer might think. Making a call of this type may result in a zero byte being written just outside the dest
buffer.
-
-
-
-
-
-We recommend subtracting one from the third argument. For example, replace strncat(dest, source, sizeof(dest)-strlen(dest))
with strncat(dest, source, sizeof(dest)-strlen(dest)-1)
.
-
-
-
-The following example demonstrates an erroneous and corrected use of the strncat
function.
-
-
-
-
-
-
- CERT C Coding Standard:
-STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator.
-
-
- CERT C Coding Standard:
- ARR30-C. Do not form or use out-of-bounds pointers or array subscripts.
-
-
-
-
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.ql b/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.ql
deleted file mode 100644
index e7021bda1ceb..000000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.ql
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
- * @name Access Of Memory Location After The End Of A Buffer Using Strncat
- * @description Calls of the form `strncat(dest, source, sizeof (dest) - strlen (dest))` set the third argument to one more than possible. So when `dest` is full, the expression `sizeof(dest) - strlen (dest)` will be equal to one, and not zero as the programmer might think. Making a call of this type may result in a zero byte being written just outside the `dest` buffer.
- * @kind problem
- * @id cpp/access-memory-location-after-end-buffer-strncat
- * @problem.severity warning
- * @precision medium
- * @tags correctness
- * security
- * external/cwe/cwe-788
- */
-
-import cpp
-import semmle.code.cpp.valuenumbering.GlobalValueNumbering
-
-/**
- * A call to `strncat` of the form `strncat(buff, str, someExpr - strlen(buf))`, for some expression `someExpr` equal to `sizeof(buff)`.
- */
-class WrongCallStrncat extends FunctionCall {
- Expr leftsomeExpr;
-
- WrongCallStrncat() {
- this.getTarget().hasGlobalOrStdName("strncat") and
- // the expression of the first argument in `strncat` and `strnlen` is identical
- globalValueNumber(this.getArgument(0)) =
- globalValueNumber(this.getArgument(2).(SubExpr).getRightOperand().(StrlenCall).getStringExpr()) and
- // using a string constant often speaks of manually calculating the length of the required buffer.
- (
- not this.getArgument(1) instanceof StringLiteral and
- not this.getArgument(1) instanceof CharLiteral
- ) and
- // for use in predicates
- leftsomeExpr = this.getArgument(2).(SubExpr).getLeftOperand()
- }
-
- /**
- * Holds if the left side of the expression `someExpr` equal to `sizeof(buf)`.
- */
- predicate isExpressionEqualSizeof() {
- // the left side of the expression `someExpr` is `sizeof(buf)`.
- globalValueNumber(this.getArgument(0)) =
- globalValueNumber(leftsomeExpr.(SizeofExprOperator).getExprOperand())
- or
- // value of the left side of the expression `someExpr` equal `sizeof(buf)` value, and `buf` is array.
- leftsomeExpr.getValue().toInt() = this.getArgument(0).getType().getSize()
- }
-
- /**
- * Holds if the left side of the expression `someExpr` equal to variable containing the length of the memory allocated for the buffer.
- */
- predicate isVariableEqualValueSizegBuffer() {
- // the left side of expression `someExpr` is the variable that was used in the function of allocating memory for the buffer`.
- exists(AllocationExpr alc |
- leftsomeExpr.(VariableAccess).getTarget() =
- alc.(FunctionCall).getArgument(0).(VariableAccess).getTarget()
- )
- }
-}
-
-from WrongCallStrncat sc
-where
- sc.isExpressionEqualSizeof() or
- sc.isVariableEqualValueSizegBuffer()
-select sc, "if the used buffer is full, writing out of the buffer is possible"
diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp
index d4edec95db93..fde62870954f 100644
--- a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp
+++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp
@@ -33,7 +33,7 @@ the break statement only exits from one level of the loop.
Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997).
- Chapter 4: Control Flow, Rule 4.6 (PDF).
+ Chapter 4: Control Flow, Rule 4.6 (PDF).
www.cplusplus.com Control Structures
diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 201.qhelp b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 201.qhelp
index c165fe59ba8c..19d01d570cd2 100644
--- a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 201.qhelp
+++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 201.qhelp
@@ -39,7 +39,7 @@ loop if the loop requires more complicated variable iteration.
Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997).
- Chapter 4: Control Flow, Rule 4.1 (PDF).
+ Chapter 4: Control Flow, Rule 4.1 (PDF).
diff --git a/cpp/ql/src/semmle/code/cpp/Location.qll b/cpp/ql/src/semmle/code/cpp/Location.qll
index 8d3653ea1c00..2a730ea57688 100644
--- a/cpp/ql/src/semmle/code/cpp/Location.qll
+++ b/cpp/ql/src/semmle/code/cpp/Location.qll
@@ -128,7 +128,9 @@ deprecated library class LocationExpr extends Location, @location_expr { }
* Gets the length of the longest line in file `f`.
*/
pragma[nomagic]
-private int maxCols(File f) { result = max(Location l | l.getFile() = f | l.getEndColumn()) }
+private int maxCols(File f) {
+ result = max(Location l | l.getFile() = f | l.getStartColumn().maximum(l.getEndColumn()))
+}
/**
* A C/C++ element that has a location in a file
diff --git a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll
index 9dc1bbc7346d..63c1406d8a5a 100644
--- a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll
+++ b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll
@@ -48,6 +48,15 @@ class MemberFunction extends Function {
/** Holds if this member is public. */
predicate isPublic() { this.hasSpecifier("public") }
+ /** Holds if this declaration has the lvalue ref-qualifier */
+ predicate isLValueRefQualified() { hasSpecifier("&") }
+
+ /** Holds if this declaration has the rvalue ref-qualifier */
+ predicate isRValueRefQualified() { hasSpecifier("&&") }
+
+ /** Holds if this declaration has a ref-qualifier */
+ predicate isRefQualified() { isLValueRefQualified() or isRValueRefQualified() }
+
/** Holds if this function overrides that function. */
predicate overrides(MemberFunction that) {
overrides(underlyingElement(this), unresolveElement(that))
diff --git a/cpp/ql/src/semmle/code/cpp/Specifier.qll b/cpp/ql/src/semmle/code/cpp/Specifier.qll
index 1c1eb0c090a7..4a425b690f4f 100644
--- a/cpp/ql/src/semmle/code/cpp/Specifier.qll
+++ b/cpp/ql/src/semmle/code/cpp/Specifier.qll
@@ -37,7 +37,7 @@ class FunctionSpecifier extends Specifier {
this.hasName("explicit")
}
- override string getAPrimaryQlClass() { result = "FunctionSpecifier)" }
+ override string getAPrimaryQlClass() { result = "FunctionSpecifier" }
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/Type.qll b/cpp/ql/src/semmle/code/cpp/Type.qll
index a0d8c42df32d..fd9ae4c87378 100644
--- a/cpp/ql/src/semmle/code/cpp/Type.qll
+++ b/cpp/ql/src/semmle/code/cpp/Type.qll
@@ -101,6 +101,7 @@ class Type extends Locatable, @type {
*
* For example, starting with `const i64* const` in the context of `typedef long long i64;`, this predicate will return `long long*`.
*/
+ pragma[nomagic]
Type getUnspecifiedType() { unspecifiedtype(underlyingElement(this), unresolveElement(result)) }
/**
diff --git a/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll b/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll
index 253a27670774..5d6c64630a6d 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll
@@ -63,14 +63,278 @@ class Ptrdiff_t extends Type {
override string getAPrimaryQlClass() { result = "Ptrdiff_t" }
}
+/**
+ * A parent class representing C/C++ a typedef'd `UserType` such as `int8_t`.
+ */
+abstract private class IntegralUnderlyingUserType extends UserType {
+ IntegralUnderlyingUserType() { this.getUnderlyingType() instanceof IntegralType }
+}
+
+abstract private class TFixedWidthIntegralType extends IntegralUnderlyingUserType { }
+
+/**
+ * A C/C++ fixed-width numeric type, such as `int8_t`.
+ */
+class FixedWidthIntegralType extends TFixedWidthIntegralType {
+ FixedWidthIntegralType() { this instanceof TFixedWidthIntegralType }
+}
+
+abstract private class TMinimumWidthIntegralType extends IntegralUnderlyingUserType { }
+
+/**
+ * A C/C++ minimum-width numeric type, such as `int_least8_t`.
+ */
+class MinimumWidthIntegralType extends TMinimumWidthIntegralType {
+ MinimumWidthIntegralType() { this instanceof TMinimumWidthIntegralType }
+}
+
+abstract private class TFastestMinimumWidthIntegralType extends IntegralUnderlyingUserType { }
+
+/**
+ * A C/C++ minimum-width numeric type, representing the fastest integer type with a
+ * width of at least `N` such as `int_fast8_t`.
+ */
+class FastestMinimumWidthIntegralType extends TFastestMinimumWidthIntegralType {
+ FastestMinimumWidthIntegralType() { this instanceof TFastestMinimumWidthIntegralType }
+}
+
+abstract private class TMaximumWidthIntegralType extends IntegralUnderlyingUserType { }
+
+/**
+ * A C/C++ maximum-width numeric type, either `intmax_t` or `uintmax_t`.
+ */
+class MaximumWidthIntegralType extends TMaximumWidthIntegralType {
+ MaximumWidthIntegralType() { this instanceof TMaximumWidthIntegralType }
+}
+
+/**
+ * An enum type based on a fixed-width integer type. For instance, `enum e: uint8_t = { a, b };`
+ */
+class FixedWidthEnumType extends UserType {
+ FixedWidthEnumType() { this.(Enum).getExplicitUnderlyingType() instanceof FixedWidthIntegralType }
+}
+
+/**
+ * The C/C++ `int8_t` type.
+ */
+class Int8_t extends TFixedWidthIntegralType {
+ Int8_t() { this.hasGlobalOrStdName("int8_t") }
+
+ override string getAPrimaryQlClass() { result = "Int8_t" }
+}
+
+/**
+ * The C/C++ `int16_t` type.
+ */
+class Int16_t extends TFixedWidthIntegralType {
+ Int16_t() { this.hasGlobalOrStdName("int16_t") }
+
+ override string getAPrimaryQlClass() { result = "Int16_t" }
+}
+
+/**
+ * The C/C++ `int32_t` type.
+ */
+class Int32_t extends TFixedWidthIntegralType {
+ Int32_t() { this.hasGlobalOrStdName("int32_t") }
+
+ override string getAPrimaryQlClass() { result = "Int32_t" }
+}
+
+/**
+ * The C/C++ `int64_t` type.
+ */
+class Int64_t extends TFixedWidthIntegralType {
+ Int64_t() { this.hasGlobalOrStdName("int64_t") }
+
+ override string getAPrimaryQlClass() { result = "Int64_t" }
+}
+
+/**
+ * The C/C++ `uint8_t` type.
+ */
+class UInt8_t extends TFixedWidthIntegralType {
+ UInt8_t() { this.hasGlobalOrStdName("uint8_t") }
+
+ override string getAPrimaryQlClass() { result = "UInt8_t" }
+}
+
+/**
+ * The C/C++ `uint16_t` type.
+ */
+class UInt16_t extends TFixedWidthIntegralType {
+ UInt16_t() { this.hasGlobalOrStdName("uint16_t") }
+
+ override string getAPrimaryQlClass() { result = "UInt16_t" }
+}
+
+/**
+ * The C/C++ `uint32_t` type.
+ */
+class UInt32_t extends TFixedWidthIntegralType {
+ UInt32_t() { this.hasGlobalOrStdName("uint32_t") }
+
+ override string getAPrimaryQlClass() { result = "UInt32_t" }
+}
+
+/**
+ * The C/C++ `uint64_t` type.
+ */
+class UInt64_t extends TFixedWidthIntegralType {
+ UInt64_t() { this.hasGlobalOrStdName("uint64_t") }
+
+ override string getAPrimaryQlClass() { result = "UInt64_t" }
+}
+
+/**
+ * The C/C++ `int_least8_t` type.
+ */
+class Int_least8_t extends TMinimumWidthIntegralType {
+ Int_least8_t() { this.hasGlobalOrStdName("int_least8_t") }
+
+ override string getAPrimaryQlClass() { result = "Int_least8_t" }
+}
+
+/**
+ * The C/C++ `int_least16_t` type.
+ */
+class Int_least16_t extends TMinimumWidthIntegralType {
+ Int_least16_t() { this.hasGlobalOrStdName("int_least16_t") }
+
+ override string getAPrimaryQlClass() { result = "Int_least16_t" }
+}
+
+/**
+ * The C/C++ `int_least32_t` type.
+ */
+class Int_least32_t extends TMinimumWidthIntegralType {
+ Int_least32_t() { this.hasGlobalOrStdName("int_least32_t") }
+
+ override string getAPrimaryQlClass() { result = "Int_least32_t" }
+}
+
+/**
+ * The C/C++ `int_least64_t` type.
+ */
+class Int_least64_t extends TMinimumWidthIntegralType {
+ Int_least64_t() { this.hasGlobalOrStdName("int_least64_t") }
+
+ override string getAPrimaryQlClass() { result = "Int_least64_t" }
+}
+
+/**
+ * The C/C++ `uint_least8_t` type.
+ */
+class UInt_least8_t extends TMinimumWidthIntegralType {
+ UInt_least8_t() { this.hasGlobalOrStdName("uint_least8_t") }
+
+ override string getAPrimaryQlClass() { result = "UInt_least8_t" }
+}
+
+/**
+ * The C/C++ `uint_least16_t` type.
+ */
+class UInt_least16_t extends TMinimumWidthIntegralType {
+ UInt_least16_t() { this.hasGlobalOrStdName("uint_least16_t") }
+
+ override string getAPrimaryQlClass() { result = "UInt_least16_t" }
+}
+
+/**
+ * The C/C++ `uint_least32_t` type.
+ */
+class UInt_least32_t extends TMinimumWidthIntegralType {
+ UInt_least32_t() { this.hasGlobalOrStdName("uint_least32_t") }
+
+ override string getAPrimaryQlClass() { result = "UInt_least32_t" }
+}
+
+/**
+ * The C/C++ `uint_least64_t` type.
+ */
+class UInt_least64_t extends TMinimumWidthIntegralType {
+ UInt_least64_t() { this.hasGlobalOrStdName("uint_least64_t") }
+
+ override string getAPrimaryQlClass() { result = "UInt_least64_t" }
+}
+
+/**
+ * The C/C++ `int_fast8_t` type.
+ */
+class Int_fast8_t extends TFastestMinimumWidthIntegralType {
+ Int_fast8_t() { this.hasGlobalOrStdName("int_fast8_t") }
+
+ override string getAPrimaryQlClass() { result = "Int_fast8_t" }
+}
+
+/**
+ * The C/C++ `int_fast16_t` type.
+ */
+class Int_fast16_t extends TFastestMinimumWidthIntegralType {
+ Int_fast16_t() { this.hasGlobalOrStdName("int_fast16_t") }
+
+ override string getAPrimaryQlClass() { result = "Int_fast16_t" }
+}
+
+/**
+ * The C/C++ `int_fast32_t` type.
+ */
+class Int_fast32_t extends TFastestMinimumWidthIntegralType {
+ Int_fast32_t() { this.hasGlobalOrStdName("int_fast32_t") }
+
+ override string getAPrimaryQlClass() { result = "Int_fast32_t" }
+}
+
+/**
+ * The C/C++ `int_fast64_t` type.
+ */
+class Int_fast64_t extends TFastestMinimumWidthIntegralType {
+ Int_fast64_t() { this.hasGlobalOrStdName("int_fast64_t") }
+
+ override string getAPrimaryQlClass() { result = "Int_fast64_t" }
+}
+
+/**
+ * The C/C++ `uint_fast8_t` type.
+ */
+class UInt_fast8_t extends TFastestMinimumWidthIntegralType {
+ UInt_fast8_t() { this.hasGlobalOrStdName("uint_fast8_t") }
+
+ override string getAPrimaryQlClass() { result = "UInt_fast8_t" }
+}
+
+/**
+ * The C/C++ `uint_fast16_t` type.
+ */
+class UInt_fast16_t extends TFastestMinimumWidthIntegralType {
+ UInt_fast16_t() { this.hasGlobalOrStdName("uint_fast16_t") }
+
+ override string getAPrimaryQlClass() { result = "UInt_fast16_t" }
+}
+
+/**
+ * The C/C++ `uint_fast32_t` type.
+ */
+class UInt_fast32_t extends TFastestMinimumWidthIntegralType {
+ UInt_fast32_t() { this.hasGlobalOrStdName("uint_fast32_t") }
+
+ override string getAPrimaryQlClass() { result = "UInt_fast32_t" }
+}
+
+/**
+ * The C/C++ `uint_fast64_t` type.
+ */
+class UInt_fast64_t extends TFastestMinimumWidthIntegralType {
+ UInt_fast64_t() { this.hasGlobalOrStdName("uint_fast64_t") }
+
+ override string getAPrimaryQlClass() { result = "UInt_fast64_t" }
+}
+
/**
* The C/C++ `intmax_t` type.
*/
-class Intmax_t extends Type {
- Intmax_t() {
- this.getUnderlyingType() instanceof IntegralType and
- this.hasName("intmax_t")
- }
+class Intmax_t extends TMaximumWidthIntegralType {
+ Intmax_t() { this.hasGlobalOrStdName("intmax_t") }
override string getAPrimaryQlClass() { result = "Intmax_t" }
}
@@ -78,11 +342,8 @@ class Intmax_t extends Type {
/**
* The C/C++ `uintmax_t` type.
*/
-class Uintmax_t extends Type {
- Uintmax_t() {
- this.getUnderlyingType() instanceof IntegralType and
- this.hasName("uintmax_t")
- }
+class Uintmax_t extends TMaximumWidthIntegralType {
+ Uintmax_t() { this.hasGlobalOrStdName("uintmax_t") }
override string getAPrimaryQlClass() { result = "Uintmax_t" }
}
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/Dominance.qll b/cpp/ql/src/semmle/code/cpp/controlflow/Dominance.qll
index 3abe68e04427..424e615f3ea8 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/Dominance.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/Dominance.qll
@@ -14,6 +14,7 @@ import cpp
* In rare cases, the same node is used in multiple control-flow scopes. This
* confuses the dominance analysis, so this predicate is used to exclude them.
*/
+pragma[noinline]
private predicate hasMultiScopeNode(Function f) {
exists(ControlFlowNode node |
node.getControlFlowScope() = f and
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll b/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll
index 656496325af2..d96fc34259c3 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll
@@ -125,17 +125,17 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
)
}
- override predicate comparesEq(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) {
+ override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
exists(boolean partIsTrue, GuardCondition part |
this.(BinaryLogicalOperation).impliesValue(part, partIsTrue, testIsTrue)
|
- part.comparesEq(left, right, k, isLessThan, partIsTrue)
+ part.comparesEq(left, right, k, areEqual, partIsTrue)
)
}
- override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
+ override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) {
exists(boolean testIsTrue |
- comparesEq(left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue)
+ comparesEq(left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue)
)
}
}
@@ -154,20 +154,20 @@ private class GuardConditionFromShortCircuitNot extends GuardCondition, NotExpr
getOperand().(GuardCondition).controls(controlled, testIsTrue.booleanNot())
}
- override predicate comparesLt(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
- getOperand().(GuardCondition).comparesLt(left, right, k, areEqual, testIsTrue.booleanNot())
+ override predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) {
+ getOperand().(GuardCondition).comparesLt(left, right, k, isLessThan, testIsTrue.booleanNot())
}
- override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean testIsTrue) {
- getOperand().(GuardCondition).ensuresLt(left, right, k, block, testIsTrue.booleanNot())
+ override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
+ getOperand().(GuardCondition).ensuresLt(left, right, k, block, isLessThan.booleanNot())
}
override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
getOperand().(GuardCondition).comparesEq(left, right, k, areEqual, testIsTrue.booleanNot())
}
- override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean testIsTrue) {
- getOperand().(GuardCondition).ensuresEq(left, right, k, block, testIsTrue.booleanNot())
+ override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) {
+ getOperand().(GuardCondition).ensuresEq(left, right, k, block, areEqual.booleanNot())
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll b/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll
index b47618de2e92..31ef5570451b 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll
@@ -1307,7 +1307,8 @@ private predicate conditionJumps(Expr test, boolean truth, Node n2, Pos p2) {
)
}
-// Factored out for performance. See QL-796.
+// Pulled out for performance. See
+// https://github.com/github/codeql-coreql-team/issues/1044.
private predicate normalGroupMemberBaseCase(Node memberNode, Pos memberPos, Node atNode) {
memberNode = atNode and
memberPos.isAt() and
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll b/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll
index 77e0f05ed020..476f626e8749 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll
@@ -104,9 +104,43 @@ private predicate loopConditionAlwaysUponEntry(ControlFlowNode loop, Expr condit
)
}
+/**
+ * This relation is the same as the `el instanceof Function`, only obfuscated
+ * so the optimizer will not understand that any `FunctionCall.getTarget()`
+ * should be in this relation.
+ */
+pragma[noinline]
+private predicate isFunction(Element el) {
+ el instanceof Function
+ or
+ el.(Expr).getParent() = el
+}
+
+/**
+ * Holds if `fc` is a `FunctionCall` with no return value for `getTarget`. This
+ * can happen in case of rare database inconsistencies.
+ */
+pragma[noopt]
+private predicate callHasNoTarget(@funbindexpr fc) {
+ exists(Function f |
+ funbind(fc, f) and
+ not isFunction(f)
+ )
+}
+
+// Pulled out for performance. See
+// https://github.com/github/codeql-coreql-team/issues/1044.
+private predicate potentiallyReturningFunctionCall_base(FunctionCall fc) {
+ fc.isVirtual()
+ or
+ callHasNoTarget(fc)
+}
+
/** A function call that *may* return; if in doubt, we assume it may. */
private predicate potentiallyReturningFunctionCall(FunctionCall fc) {
- potentiallyReturningFunction(fc.getTarget()) or fc.isVirtual()
+ potentiallyReturningFunctionCall_base(fc)
+ or
+ potentiallyReturningFunction(fc.getTarget())
}
/** A function that *may* return; if in doubt, we assume it may. */
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
index 9498e51e7e61..df59c128ad39 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
@@ -81,6 +81,12 @@ abstract class Configuration extends string {
*/
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
+ /**
+ * Holds if an arbitrary number of implicit read steps of content `c` may be
+ * taken at `node`.
+ */
+ predicate allowImplicitRead(Node node, Content c) { none() }
+
/**
* Gets the virtual dispatch branching limit when calculating field flow.
* This can be overridden to a smaller value to improve performance (a
@@ -182,78 +188,210 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
}
}
-private predicate inBarrier(Node node, Configuration config) {
- config.isBarrierIn(node) and
- config.isSource(node)
+private newtype TNodeEx =
+ TNodeNormal(Node n) or
+ TNodeImplicitRead(Node n, boolean hasRead) {
+ any(Configuration c).allowImplicitRead(n, _) and hasRead = [false, true]
+ }
+
+private class NodeEx extends TNodeEx {
+ string toString() {
+ result = this.asNode().toString()
+ or
+ exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]")
+ }
+
+ Node asNode() { this = TNodeNormal(result) }
+
+ predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) }
+
+ Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) }
+
+ pragma[nomagic]
+ private DataFlowCallable getEnclosingCallable0() {
+ nodeEnclosingCallable(this.projectToNode(), result)
+ }
+
+ pragma[inline]
+ DataFlowCallable getEnclosingCallable() {
+ pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result)
+ }
+
+ pragma[nomagic]
+ private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) }
+
+ pragma[inline]
+ DataFlowType getDataFlowType() {
+ pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result)
+ }
+
+ predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ }
}
-private predicate outBarrier(Node node, Configuration config) {
- config.isBarrierOut(node) and
- config.isSink(node)
+private class ArgNodeEx extends NodeEx {
+ ArgNodeEx() { this.asNode() instanceof ArgNode }
}
-private predicate fullBarrier(Node node, Configuration config) {
- config.isBarrier(node)
- or
- config.isBarrierIn(node) and
- not config.isSource(node)
- or
- config.isBarrierOut(node) and
- not config.isSink(node)
- or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- node = g.getAGuardedNode()
+private class ParamNodeEx extends NodeEx {
+ ParamNodeEx() { this.asNode() instanceof ParamNode }
+
+ predicate isParameterOf(DataFlowCallable c, int i) {
+ this.asNode().(ParamNode).isParameterOf(c, i)
+ }
+
+ int getPosition() { this.isParameterOf(_, result) }
+}
+
+private class RetNodeEx extends NodeEx {
+ RetNodeEx() { this.asNode() instanceof ReturnNodeExt }
+
+ ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) }
+
+ ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() }
+}
+
+private predicate inBarrier(NodeEx node, Configuration config) {
+ exists(Node n |
+ node.asNode() = n and
+ config.isBarrierIn(n) and
+ config.isSource(n)
+ )
+}
+
+private predicate outBarrier(NodeEx node, Configuration config) {
+ exists(Node n |
+ node.asNode() = n and
+ config.isBarrierOut(n) and
+ config.isSink(n)
+ )
+}
+
+private predicate fullBarrier(NodeEx node, Configuration config) {
+ exists(Node n | node.asNode() = n |
+ config.isBarrier(n)
+ or
+ config.isBarrierIn(n) and
+ not config.isSource(n)
+ or
+ config.isBarrierOut(n) and
+ not config.isSink(n)
+ or
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
)
}
+pragma[nomagic]
+private predicate sourceNode(NodeEx node, Configuration config) { config.isSource(node.asNode()) }
+
+pragma[nomagic]
+private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) }
+
/**
* Holds if data can flow in one local step from `node1` to `node2`.
*/
-private predicate localFlowStep(Node node1, Node node2, Configuration config) {
- (
- simpleLocalFlowStep(node1, node2) or
- reverseStepThroughInputOutputAlias(node1, node2)
- ) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ simpleLocalFlowStepExt(n1, n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+ or
+ exists(Node n |
+ config.allowImplicitRead(n, _) and
+ node1.asNode() = n and
+ node2.isImplicitReadNode(n, false)
+ )
}
/**
* Holds if the additional step from `node1` to `node2` does not jump between callables.
*/
-private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
- config.isAdditionalFlowStep(node1, node2) and
- getNodeEnclosingCallable(node1) = getNodeEnclosingCallable(node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ config.isAdditionalFlowStep(n1, n2) and
+ getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+ or
+ exists(Node n |
+ config.allowImplicitRead(n, _) and
+ node1.isImplicitReadNode(n, true) and
+ node2.asNode() = n
+ )
}
/**
* Holds if data can flow from `node1` to `node2` in a way that discards call contexts.
*/
-private predicate jumpStep(Node node1, Node node2, Configuration config) {
- jumpStep(node1, node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ jumpStepCached(n1, n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
}
/**
* Holds if the additional step from `node1` to `node2` jumps between callables.
*/
-private predicate additionalJumpStep(Node node1, Node node2, Configuration config) {
- config.isAdditionalFlowStep(node1, node2) and
- getNodeEnclosingCallable(node1) != getNodeEnclosingCallable(node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ config.isAdditionalFlowStep(n1, n2) and
+ getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+}
+
+private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ read(node1.asNode(), c, node2.asNode())
+ or
+ exists(Node n |
+ node2.isImplicitReadNode(n, true) and
+ node1.isImplicitReadNode(n, _) and
+ config.allowImplicitRead(n, c)
+ )
+}
+
+private predicate store(
+ NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
+) {
+ store(node1.asNode(), tc, node2.asNode(), contentType) and
+ read(_, tc.getContent(), _, config)
+}
+
+pragma[nomagic]
+private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) {
+ viableReturnPosOut(call, pos, out.asNode())
+}
+
+pragma[nomagic]
+private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) {
+ viableParamArg(call, p.asNode(), arg.asNode())
}
/**
@@ -277,39 +415,39 @@ private module Stage1 {
* The Boolean `cc` records whether the node is reached through an
* argument in a call.
*/
- predicate fwdFlow(Node node, Cc cc, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
not fullBarrier(node, config) and
(
- config.isSource(node) and
+ sourceNode(node, config) and
cc = false
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
localFlowStep(mid, node, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
additionalLocalFlowStep(mid, node, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, config) and
jumpStep(mid, node, config) and
cc = false
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, config) and
additionalJumpStep(mid, node, config) and
cc = false
)
or
// store
- exists(Node mid |
+ exists(NodeEx mid |
useFieldFlow(config) and
fwdFlow(mid, cc, config) and
- store(mid, _, node, _) and
+ store(mid, _, node, _, config) and
not outBarrier(mid, config)
)
or
@@ -321,9 +459,9 @@ private module Stage1 {
)
or
// flow into a callable
- exists(Node arg |
+ exists(NodeEx arg |
fwdFlow(arg, _, config) and
- viableParamArg(_, node, arg) and
+ viableParamArgEx(_, node, arg) and
cc = true
)
or
@@ -338,13 +476,13 @@ private module Stage1 {
)
}
- private predicate fwdFlow(Node node, Configuration config) { fwdFlow(node, _, config) }
+ private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
pragma[nomagic]
- private predicate fwdFlowRead(Content c, Node node, Cc cc, Configuration config) {
- exists(Node mid |
+ private predicate fwdFlowRead(Content c, NodeEx node, Cc cc, Configuration config) {
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
- read(mid, c, node)
+ read(mid, c, node, config)
)
}
@@ -353,33 +491,33 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate fwdFlowConsCand(Content c, Configuration config) {
- exists(Node mid, Node node, TypedContent tc |
+ exists(NodeEx mid, NodeEx node, TypedContent tc |
not fullBarrier(node, config) and
useFieldFlow(config) and
fwdFlow(mid, _, config) and
- store(mid, tc, node, _) and
+ store(mid, tc, node, _, config) and
c = tc.getContent()
)
}
pragma[nomagic]
private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc, Configuration config) {
- exists(ReturnNodeExt ret |
+ exists(RetNodeEx ret |
fwdFlow(ret, cc, config) and
- getReturnPosition(ret) = pos
+ ret.getReturnPosition() = pos
)
}
pragma[nomagic]
- private predicate fwdFlowOut(DataFlowCall call, Node out, Cc cc, Configuration config) {
+ private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
exists(ReturnPosition pos |
fwdFlowReturnPosition(pos, cc, config) and
- viableReturnPosOut(call, pos, out)
+ viableReturnPosOutEx(call, pos, out)
)
}
pragma[nomagic]
- private predicate fwdFlowOutFromArg(DataFlowCall call, Node out, Configuration config) {
+ private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out, Configuration config) {
fwdFlowOut(call, out, true, config)
}
@@ -388,9 +526,9 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
- exists(ArgumentNode arg |
+ exists(ArgNodeEx arg |
fwdFlow(arg, cc, config) and
- viableParamArg(call, _, arg)
+ viableParamArgEx(call, _, arg)
)
}
@@ -402,34 +540,34 @@ private module Stage1 {
* the enclosing callable in order to reach a sink.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and
fwdFlow(node, config)
}
pragma[nomagic]
- private predicate revFlow0(Node node, boolean toReturn, Configuration config) {
+ private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStep(node, mid, config) and
revFlow(mid, toReturn, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
additionalLocalFlowStep(node, mid, config) and
revFlow(mid, toReturn, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, config) and
toReturn = false
)
or
- exists(Node mid |
+ exists(NodeEx mid |
additionalJumpStep(node, mid, config) and
revFlow(mid, _, config) and
toReturn = false
@@ -442,8 +580,8 @@ private module Stage1 {
)
or
// read
- exists(Node mid, Content c |
- read(node, c, mid) and
+ exists(NodeEx mid, Content c |
+ read(node, c, mid, config) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
revFlow(mid, toReturn, pragma[only_bind_into](config))
)
@@ -460,7 +598,7 @@ private module Stage1 {
// flow out of a callable
exists(ReturnPosition pos |
revFlowOut(pos, config) and
- getReturnPosition(node) = pos and
+ node.(RetNodeEx).getReturnPosition() = pos and
toReturn = true
)
}
@@ -470,20 +608,20 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Content c, Configuration config) {
- exists(Node mid, Node node |
+ exists(NodeEx mid, NodeEx node |
fwdFlow(node, pragma[only_bind_into](config)) and
- read(node, c, mid) and
+ read(node, c, mid, config) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
revFlow(pragma[only_bind_into](mid), _, pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate revFlowStore(Content c, Node node, boolean toReturn, Configuration config) {
- exists(Node mid, TypedContent tc |
+ private predicate revFlowStore(Content c, NodeEx node, boolean toReturn, Configuration config) {
+ exists(NodeEx mid, TypedContent tc |
revFlow(mid, toReturn, pragma[only_bind_into](config)) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
- store(node, tc, mid, _) and
+ store(node, tc, mid, _, config) and
c = tc.getContent()
)
}
@@ -499,15 +637,15 @@ private module Stage1 {
pragma[nomagic]
predicate viableReturnPosOutNodeCandFwd1(
- DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+ DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
fwdFlowReturnPosition(pos, _, config) and
- viableReturnPosOut(call, pos, out)
+ viableReturnPosOutEx(call, pos, out)
}
pragma[nomagic]
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
- exists(DataFlowCall call, Node out |
+ exists(DataFlowCall call, NodeEx out |
revFlow(out, _, config) and
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
)
@@ -515,33 +653,35 @@ private module Stage1 {
pragma[nomagic]
predicate viableParamArgNodeCandFwd1(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+ DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
- viableParamArg(call, p, arg) and
+ viableParamArgEx(call, p, arg) and
fwdFlow(arg, config)
}
pragma[nomagic]
private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config
+ DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
revFlow(p, toReturn, config) and
viableParamArgNodeCandFwd1(call, p, arg, config)
)
}
pragma[nomagic]
- private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
+ private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg, Configuration config) {
revFlowIn(call, arg, true, config)
}
/**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`.
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
*/
pragma[nomagic]
private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) {
- exists(Node out |
+ exists(NodeEx out |
revFlow(out, toReturn, config) and
fwdFlowOutFromArg(call, out, config)
)
@@ -549,32 +689,33 @@ private module Stage1 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Content c |
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
revFlow(node2, pragma[only_bind_into](config)) and
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
c = tc.getContent() and
exists(ap1)
)
}
pragma[nomagic]
- predicate readStepCand(Node n1, Content c, Node n2, Configuration config) {
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) and
- read(n1, c, n2)
+ read(n1, c, n2, pragma[only_bind_into](config))
}
pragma[nomagic]
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow(node, toReturn, config) and exists(returnAp) and exists(ap)
}
- private predicate throughFlowNodeCand(Node node, Configuration config) {
+ private predicate throughFlowNodeCand(NodeEx node, Configuration config) {
revFlow(node, true, config) and
fwdFlow(node, true, config) and
not inBarrier(node, config) and
@@ -586,9 +727,9 @@ private module Stage1 {
private predicate returnFlowCallableNodeCand(
DataFlowCallable callable, ReturnKindExt kind, Configuration config
) {
- exists(ReturnNodeExt ret |
+ exists(RetNodeEx ret |
throughFlowNodeCand(ret, config) and
- callable = getNodeEnclosingCallable(ret) and
+ callable = ret.getEnclosingCallable() and
kind = ret.getKind()
)
}
@@ -597,50 +738,57 @@ private module Stage1 {
* Holds if flow may enter through `p` and reach a return node making `p` a
* candidate for the origin of a summary.
*/
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
- getNodeEnclosingCallable(p) = c and
+ p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself
- not exists(int pos |
- kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos)
- )
+ not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
+ )
+ }
+
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(ArgNodeEx arg, boolean toReturn |
+ revFlow(arg, toReturn, config) and
+ revFlowInToReturn(call, arg, config) and
+ revFlowIsReturned(call, toReturn, config)
)
}
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, config)) and
fields = count(Content f0 | fwdFlowConsCand(f0, config)) and
conscand = -1 and
- tuples = count(Node n, boolean b | fwdFlow(n, b, config))
+ tuples = count(NodeEx n, boolean b | fwdFlow(n, b, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, config)) and
fields = count(Content f0 | revFlowConsCand(f0, config)) and
conscand = -1 and
- tuples = count(Node n, boolean b | revFlow(n, b, config))
+ tuples = count(NodeEx n, boolean b | revFlow(n, b, config))
}
/* End: Stage 1 logic. */
}
pragma[noinline]
-private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) {
Stage1::revFlow(node2, config) and
localFlowStep(node1, node2, config)
}
pragma[noinline]
-private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) {
Stage1::revFlow(node2, config) and
additionalLocalFlowStep(node1, node2, config)
}
pragma[nomagic]
private predicate viableReturnPosOutNodeCand1(
- DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+ DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
Stage1::revFlow(out, config) and
Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out, config)
@@ -653,9 +801,9 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, ReturnNodeExt ret, Node out, Configuration config
+ DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
) {
- viableReturnPosOutNodeCand1(call, getReturnPosition(ret), out, config) and
+ viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
@@ -663,7 +811,7 @@ private predicate flowOutOfCallNodeCand1(
pragma[nomagic]
private predicate viableParamArgNodeCand1(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+ DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and
Stage1::revFlow(arg, config)
@@ -675,7 +823,7 @@ private predicate viableParamArgNodeCand1(
*/
pragma[nomagic]
private predicate flowIntoCallNodeCand1(
- DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, Configuration config
) {
viableParamArgNodeCand1(call, p, arg, config) and
Stage1::revFlow(p, config) and
@@ -688,9 +836,9 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
-private int branch(Node n1, Configuration conf) {
+private int branch(NodeEx n1, Configuration conf) {
result =
- strictcount(Node n |
+ strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -700,9 +848,9 @@ private int branch(Node n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
-private int join(Node n2, Configuration conf) {
+private int join(NodeEx n2, Configuration conf) {
result =
- strictcount(Node n |
+ strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -716,7 +864,7 @@ private int join(Node n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
exists(int b, int j |
@@ -735,8 +883,7 @@ private predicate flowOutOfCallNodeCand1(
*/
pragma[nomagic]
private predicate flowIntoCallNodeCand1(
- DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
exists(int b, int j |
@@ -762,7 +909,7 @@ private module Stage2 {
bindingset[result, ap]
private ApApprox getApprox(Ap ap) { any() }
- private ApNil getApNil(Node node) { PrevStage::revFlow(node, _) and exists(result) }
+ private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
@@ -805,10 +952,10 @@ private module Stage2 {
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { any() }
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
(
preservesValue = true and
@@ -829,10 +976,20 @@ private module Stage2 {
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
/* Begin: Stage 2 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -842,14 +999,14 @@ private module Stage2 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -860,7 +1017,7 @@ private module Stage2 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -868,7 +1025,7 @@ private module Stage2 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -899,19 +1056,17 @@ private module Stage2 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -935,7 +1090,7 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -944,32 +1099,29 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -979,9 +1131,15 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -992,7 +1150,7 @@ private module Stage2 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
)
@@ -1000,18 +1158,41 @@ private module Stage2 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1021,41 +1202,41 @@ private module Stage2 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -1071,20 +1252,18 @@ private module Stage2 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -1097,7 +1276,7 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -1111,7 +1290,7 @@ private module Stage2 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -1120,10 +1299,10 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -1132,13 +1311,12 @@ private module Stage2 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1146,9 +1324,14 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1160,7 +1343,7 @@ private module Stage2 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -1169,16 +1352,17 @@ private module Stage2 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -1187,7 +1371,7 @@ private module Stage2 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -1199,44 +1383,54 @@ private module Stage2 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 2 logic. */
}
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
- DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
@@ -1245,7 +1439,7 @@ private predicate flowOutOfCallNodeCand2(
pragma[nomagic]
private predicate flowIntoCallNodeCand2(
- DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
@@ -1258,10 +1452,10 @@ private module LocalFlowBigStep {
* A node where some checking is required, and hence the big-step relation
* is not allowed to step over.
*/
- private class FlowCheckNode extends Node {
+ private class FlowCheckNode extends NodeEx {
FlowCheckNode() {
- this instanceof CastNode or
- clearsContent(this, _)
+ castNode(this.asNode()) or
+ clearsContentCached(this.asNode(), _)
}
}
@@ -1269,16 +1463,16 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- predicate localFlowEntry(Node node, Configuration config) {
+ predicate localFlowEntry(NodeEx node, Configuration config) {
Stage2::revFlow(node, config) and
(
- config.isSource(node) or
+ sourceNode(node, config) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
- node instanceof ParameterNode or
- node instanceof OutNodeExt or
- store(_, _, node, _) or
- read(_, _, node) or
+ node instanceof ParamNodeEx or
+ node.asNode() instanceof OutNodeExt or
+ store(_, _, node, _, config) or
+ read(_, _, node, config) or
node instanceof FlowCheckNode
)
}
@@ -1287,23 +1481,25 @@ private module LocalFlowBigStep {
* Holds if `node` can be the last node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- private predicate localFlowExit(Node node, Configuration config) {
- exists(Node next | Stage2::revFlow(next, config) |
+ private predicate localFlowExit(NodeEx node, Configuration config) {
+ exists(NodeEx next | Stage2::revFlow(next, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
- store(node, _, next, _) or
- read(node, _, next)
+ store(node, _, next, _, config) or
+ read(node, _, next, config)
)
or
node instanceof FlowCheckNode
or
- config.isSink(node)
+ sinkNode(node, config)
}
pragma[noinline]
- private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
+ private predicate additionalLocalFlowStepNodeCand2(
+ NodeEx node1, NodeEx node2, Configuration config
+ ) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and
Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config))
@@ -1318,39 +1514,39 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
- Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config,
+ NodeEx node1, NodeEx node2, boolean preservesValue, DataFlowType t, Configuration config,
LocalCallContext cc
) {
- not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
+ not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
(
localFlowEntry(node1, pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
- t = getNodeType(node1)
+ t = node1.getDataFlowType() // irrelevant dummy value
or
additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
- t = getNodeType(node2)
+ t = node2.getDataFlowType()
) and
node1 != node2 and
- cc.relevantFor(getNodeEnclosingCallable(node1)) and
- not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
+ cc.relevantFor(node1.getEnclosingCallable()) and
+ not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
Stage2::revFlow(node2, pragma[only_bind_into](config))
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStepPlus(node1, mid, preservesValue, t, pragma[only_bind_into](config), cc) and
localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof FlowCheckNode and
Stage2::revFlow(node2, pragma[only_bind_into](config))
)
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStepPlus(node1, mid, _, _, pragma[only_bind_into](config), cc) and
additionalLocalFlowStepNodeCand2(mid, node2, config) and
not mid instanceof FlowCheckNode and
preservesValue = false and
- t = getNodeType(node2) and
+ t = node2.getDataFlowType() and
Stage2::revFlow(node2, pragma[only_bind_into](config))
)
)
@@ -1362,8 +1558,8 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, AccessPathFrontNil apf, Configuration config,
- LocalCallContext callContext
+ NodeEx node1, NodeEx node2, boolean preservesValue, AccessPathFrontNil apf,
+ Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, config)
@@ -1383,8 +1579,8 @@ private module Stage3 {
private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(Node node) {
- PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node))
+ private ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
@@ -1428,10 +1624,10 @@ private module Stage3 {
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { any() }
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, node2, preservesValue, ap, config, _) and exists(lcc)
}
@@ -1440,10 +1636,16 @@ private module Stage3 {
private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ pragma[nomagic]
+ private predicate clear(NodeEx node, Ap ap) { ap.isClearedAt(node.asNode()) }
+
+ pragma[nomagic]
+ private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
+
bindingset[node, ap]
- private predicate filter(Node node, Ap ap) {
- not ap.isClearedAt(node) and
- if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any()
+ private predicate filter(NodeEx node, Ap ap) {
+ not clear(node, ap) and
+ if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()
}
bindingset[ap, contentType]
@@ -1454,7 +1656,7 @@ private module Stage3 {
}
/* Begin: Stage 3 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
@@ -1465,6 +1667,16 @@ private module Stage3 {
)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -1474,21 +1686,21 @@ private module Stage3 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
pragma[nomagic]
- private predicate fwdFlow0(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -1499,7 +1711,7 @@ private module Stage3 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -1507,7 +1719,7 @@ private module Stage3 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -1538,19 +1750,17 @@ private module Stage3 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -1574,7 +1784,7 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -1583,32 +1793,29 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1618,9 +1825,15 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1631,7 +1844,7 @@ private module Stage3 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
@@ -1639,18 +1852,41 @@ private module Stage3 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1660,41 +1896,41 @@ private module Stage3 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -1710,20 +1946,18 @@ private module Stage3 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -1736,7 +1970,7 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -1750,7 +1984,7 @@ private module Stage3 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -1759,10 +1993,10 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -1771,13 +2005,12 @@ private module Stage3 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1785,9 +2018,14 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1799,7 +2037,7 @@ private module Stage3 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -1808,16 +2046,17 @@ private module Stage3 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -1826,7 +2065,7 @@ private module Stage3 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -1838,37 +2077,47 @@ private module Stage3 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 3 logic. */
}
@@ -1877,7 +2126,7 @@ private module Stage3 {
* Holds if `argApf` is recorded as the summary context for flow reaching `node`
* and remains relevant for the following pruning stage.
*/
-private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) {
+private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
@@ -1892,7 +2141,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
exists(int tails, int nodes, int apLimit, int tupleLimit |
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
- strictcount(Node n |
+ strictcount(NodeEx n |
Stage3::revFlow(n, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
@@ -2087,8 +2336,8 @@ private module Stage4 {
private ApApprox getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(Node node) {
- PrevStage::revFlow(node, _) and result = TNil(getNodeType(node))
+ private ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
@@ -2127,26 +2376,25 @@ private module Stage4 {
bindingset[innercc, inner, call]
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
resolveReturn(innercc, inner, call)
- or
- innercc.(CallContextCall).matchesCall(call)
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
localFlowEntry(node, config) and
- result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node))
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
}
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, node2, preservesValue, ap.getFront(), config, lcc)
}
pragma[nomagic]
private predicate flowOutOfCall(
- DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
@@ -2155,7 +2403,7 @@ private module Stage4 {
pragma[nomagic]
private predicate flowIntoCall(
- DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
@@ -2164,14 +2412,14 @@ private module Stage4 {
}
bindingset[node, ap]
- private predicate filter(Node node, Ap ap) { any() }
+ private predicate filter(NodeEx node, Ap ap) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
/* Begin: Stage 4 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
@@ -2182,6 +2430,16 @@ private module Stage4 {
)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -2191,21 +2449,21 @@ private module Stage4 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
pragma[nomagic]
- private predicate fwdFlow0(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -2216,7 +2474,7 @@ private module Stage4 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -2224,7 +2482,7 @@ private module Stage4 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -2255,19 +2513,17 @@ private module Stage4 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -2291,7 +2547,7 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -2300,32 +2556,29 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -2335,9 +2588,15 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -2348,7 +2607,7 @@ private module Stage4 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
@@ -2356,18 +2615,41 @@ private module Stage4 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -2377,41 +2659,41 @@ private module Stage4 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -2427,20 +2709,18 @@ private module Stage4 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -2453,7 +2733,7 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -2467,7 +2747,7 @@ private module Stage4 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -2476,10 +2756,10 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -2488,13 +2768,12 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -2502,9 +2781,14 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -2516,7 +2800,7 @@ private module Stage4 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -2525,16 +2809,17 @@ private module Stage4 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -2543,7 +2828,7 @@ private module Stage4 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -2555,37 +2840,47 @@ private module Stage4 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 4 logic. */
}
@@ -2595,18 +2890,18 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
}
-private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) {
+private predicate nodeMayUseSummary(NodeEx n, AccessPathApprox apa, Configuration config) {
exists(DataFlowCallable c, AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and
Stage4::revFlow(n, true, _, apa0, config) and
Stage4::fwdFlow(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
- getNodeEnclosingCallable(n) = c
+ n.getEnclosingCallable() = c
)
}
private newtype TSummaryCtx =
TSummaryCtxNone() or
- TSummaryCtxSome(ParameterNode p, AccessPath ap) {
+ TSummaryCtxSome(ParamNodeEx p, AccessPath ap) {
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _)
}
@@ -2627,7 +2922,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone {
/** A summary context from which a flow summary can be generated. */
private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
- private ParameterNode p;
+ private ParamNodeEx p;
private AccessPath ap;
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
@@ -2660,7 +2955,9 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
- strictcount(Node n | Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config))
+ strictcount(NodeEx n |
+ Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)
+ )
}
/**
@@ -2752,13 +3049,13 @@ private newtype TAccessPath =
}
private newtype TPathNode =
- TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
+ TPathNodeMid(NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
- ap = TAccessPathNil(getNodeType(node))
+ ap = TAccessPathNil(node.getDataFlowType())
or
// ... or a step from an existing PathNode to another node.
exists(PathNodeMid mid |
@@ -2767,12 +3064,12 @@ private newtype TPathNode =
Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config))
)
} or
- TPathNodeSink(Node node, Configuration config) {
- pragma[only_bind_into](config).isSink(node) and
+ TPathNodeSink(NodeEx node, Configuration config) {
+ sinkNode(node, pragma[only_bind_into](config)) and
Stage4::revFlow(node, pragma[only_bind_into](config)) and
(
// A sink that is also a source ...
- config.isSource(node)
+ sourceNode(node, config)
or
// ... or a sink that can be reached from a source
exists(PathNodeMid mid |
@@ -2973,15 +3270,17 @@ class PathNode extends TPathNode {
}
/** Gets the underlying `Node`. */
- Node getNode() { none() }
+ final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
private predicate isHidden() {
- nodeIsHidden(this.getNode()) and
+ hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
+ or
+ this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
}
private PathNode getASuccessorIfHidden() {
@@ -3003,6 +3302,8 @@ class PathNode extends TPathNode {
abstract private class PathNodeImpl extends PathNode {
abstract PathNode getASuccessorImpl();
+ abstract NodeEx getNodeEx();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3017,14 +3318,14 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
}
- override string toString() { result = this.getNode().toString() + ppAp() }
+ override string toString() { result = this.getNodeEx().toString() + ppAp() }
- override string toStringWithContext() { result = this.getNode().toString() + ppAp() + ppCtx() }
+ override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
@@ -3054,7 +3355,7 @@ module PathGraph {
* a `CallContext`, and a `Configuration`.
*/
private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
- Node node;
+ NodeEx node;
CallContext cc;
SummaryCtx sc;
AccessPath ap;
@@ -3062,7 +3363,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) }
- override Node getNode() { result = node }
+ override NodeEx getNodeEx() { result = node }
CallContext getCallContext() { result = cc }
@@ -3073,7 +3374,8 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
override Configuration getConfiguration() { result = config }
private PathNodeMid getSuccMid() {
- pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and
+ pathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx(),
+ result.getAp()) and
result.getConfiguration() = unbindConf(this.getConfiguration())
}
@@ -3084,7 +3386,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid, PathNodeSink sink |
mid = getSuccMid() and
- mid.getNode() = sink.getNode() and
+ mid.getNodeEx() = sink.getNodeEx() and
mid.getAp() instanceof AccessPathNil and
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
result = sink
@@ -3092,7 +3394,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
}
override predicate isSource() {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
@@ -3105,31 +3407,35 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
* excluding the `CallContext`.
*/
private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
- Node node;
+ NodeEx node;
Configuration config;
PathNodeSink() { this = TPathNodeSink(node, config) }
- override Node getNode() { result = node }
+ override NodeEx getNodeEx() { result = node }
override Configuration getConfiguration() { result = config }
override PathNode getASuccessorImpl() { none() }
- override predicate isSource() { config.isSource(node) }
+ override predicate isSource() { sourceNode(node, config) }
}
/**
* Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`.
*/
-private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
- exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
- midnode = mid.getNode() and
+private predicate pathStep(
+ PathNodeMid mid, NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap
+) {
+ exists(AccessPath ap0, NodeEx midnode, Configuration conf, LocalCallContext localCC |
+ midnode = mid.getNodeEx() and
conf = mid.getConfiguration() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
- localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and
+ localCC =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ midnode.getEnclosingCallable()) and
ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
@@ -3139,16 +3445,16 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
ap0 instanceof AccessPathNil
)
or
- jumpStep(mid.getNode(), node, mid.getConfiguration()) and
+ jumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
ap = mid.getAp()
or
- additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and
+ additionalJumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
mid.getAp() instanceof AccessPathNil and
- ap = TAccessPathNil(getNodeType(node))
+ ap = TAccessPathNil(node.getDataFlowType())
or
exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and
sc = mid.getSummaryCtx()
@@ -3165,20 +3471,20 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
pragma[nomagic]
private predicate pathReadStep(
- PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+ PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
- Stage4::readStepCand(mid.getNode(), tc.getContent(), node, mid.getConfiguration()) and
+ Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+ PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- Stage4::storeStepCand(mid.getNode(), _, tc, node, _, mid.getConfiguration()) and
+ Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
cc = mid.getCallContext()
}
@@ -3186,7 +3492,7 @@ private predicate pathOutOfCallable0(
PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa,
Configuration config
) {
- pos = getReturnPosition(mid.getNode()) and
+ pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and
innercc = mid.getCallContext() and
innercc instanceof CallContextNoCall and
apa = mid.getAp().getApprox() and
@@ -3209,10 +3515,10 @@ private predicate pathOutOfCallable1(
}
pragma[noinline]
-private Node getAnOutNodeFlow(
+private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
- result = kind.getAnOutNode(call) and
+ result.asNode() = kind.getAnOutNode(call) and
Stage4::revFlow(result, _, _, apa, config)
}
@@ -3221,7 +3527,7 @@ private Node getAnOutNodeFlow(
* is a return from a callable and is recorded by `cc`, if needed.
*/
pragma[noinline]
-private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
+private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) {
exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config |
pathOutOfCallable1(mid, call, kind, cc, apa, config) and
out = getAnOutNodeFlow(kind, call, apa, config)
@@ -3235,8 +3541,8 @@ pragma[noinline]
private predicate pathIntoArg(
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa
) {
- exists(ArgumentNode arg |
- arg = mid.getNode() and
+ exists(ArgNode arg |
+ arg = mid.getNodeEx().asNode() and
cc = mid.getCallContext() and
arg.argumentOf(call, i) and
ap = mid.getAp() and
@@ -3248,7 +3554,7 @@ pragma[noinline]
private predicate parameterCand(
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
Stage4::revFlow(p, _, _, apa, config) and
p.isParameterOf(callable, i)
)
@@ -3272,7 +3578,7 @@ private predicate pathIntoCallable0(
* respectively.
*/
private predicate pathIntoCallable(
- PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
+ PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
DataFlowCall call
) {
exists(int i, DataFlowCallable callable, AccessPath ap |
@@ -3297,8 +3603,8 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa,
Configuration config
) {
- exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
- mid.getNode() = ret and
+ exists(PathNodeMid mid, RetNodeEx ret, int pos |
+ mid.getNodeEx() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
@@ -3326,7 +3632,7 @@ private predicate pathThroughCallable0(
* The context `cc` is restored to its value prior to entering the callable.
*/
pragma[noinline]
-private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
+private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) {
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
pathThroughCallable0(call, mid, kind, cc, ap, apa) and
out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
@@ -3344,9 +3650,9 @@ private predicate flowsTo(
) {
flowsource.isSource() and
flowsource.getConfiguration() = configuration and
- flowsource.getNode() = source and
+ flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
- flowsink.getNode() = sink
+ flowsink.getNodeEx().asNode() = sink
}
/**
@@ -3361,13 +3667,13 @@ predicate flowsTo(Node source, Node sink, Configuration configuration) {
private predicate finalStats(boolean fwd, int nodes, int fields, int conscand, int tuples) {
fwd = true and
- nodes = count(Node n0 | exists(PathNode pn | pn.getNode() = n0)) and
+ nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
tuples = count(PathNode pn)
or
fwd = false and
- nodes = count(Node n0 | exists(PathNode pn | pn.getNode() = n0 and reach(pn))) and
+ nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
tuples = count(PathNode pn | reach(pn))
@@ -3404,19 +3710,19 @@ predicate stageStats(
private module FlowExploration {
private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2, Configuration config) {
- exists(Node node1, Node node2 |
+ exists(NodeEx node1, NodeEx node2 |
jumpStep(node1, node2, config)
or
additionalJumpStep(node1, node2, config)
or
// flow into callable
- viableParamArg(_, node2, node1)
+ viableParamArgEx(_, node2, node1)
or
// flow out of a callable
- viableReturnPosOut(_, getReturnPosition(node1), node2)
+ viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2)
|
- c1 = getNodeEnclosingCallable(node1) and
- c2 = getNodeEnclosingCallable(node2) and
+ c1 = node1.getEnclosingCallable() and
+ c2 = node2.getEnclosingCallable() and
c1 != c2
)
}
@@ -3568,7 +3874,7 @@ private module FlowExploration {
private newtype TSummaryCtx1 =
TSummaryCtx1None() or
- TSummaryCtx1Param(ParameterNode p)
+ TSummaryCtx1Param(ParamNodeEx p)
private newtype TSummaryCtx2 =
TSummaryCtx2None() or
@@ -3584,25 +3890,25 @@ private module FlowExploration {
private newtype TPartialPathNode =
TPartialPathNodeFwd(
- Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
+ NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
- distSrc(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
+ distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
- Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
+ NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
Configuration config
) {
- config.isSink(node) and
+ sinkNode(node, config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil() and
@@ -3611,23 +3917,23 @@ private module FlowExploration {
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
- not clearsContent(node, ap.getHead()) and
+ not clearsContentCached(node.asNode(), ap.getHead()) and
not fullBarrier(node, config) and
- distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
+ distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
}
pragma[nomagic]
private predicate partialPathNodeMk0(
- Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
+ NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
- not clearsContent(node, ap.getHead().getContent()) and
- if node instanceof CastingNode
- then compatibleTypes(getNodeType(node), ap.getType())
+ not clearsContentCached(node.asNode(), ap.getHead().getContent()) and
+ if node.asNode() instanceof CastingNode
+ then compatibleTypes(node.getDataFlowType(), ap.getType())
else any()
)
}
@@ -3637,13 +3943,15 @@ private module FlowExploration {
*/
class PartialPathNode extends TPartialPathNode {
/** Gets a textual representation of this element. */
- string toString() { result = this.getNode().toString() + this.ppAp() }
+ string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/**
* Gets a textual representation of this element, including a textual
* representation of the call context.
*/
- string toStringWithContext() { result = this.getNode().toString() + this.ppAp() + this.ppCtx() }
+ string toStringWithContext() {
+ result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
+ }
/**
* Holds if this element is at the specified location.
@@ -3655,11 +3963,16 @@ private module FlowExploration {
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the underlying `Node`. */
- Node getNode() { none() }
+ final Node getNode() { this.getNodeEx().projectToNode() = result }
+
+ private NodeEx getNodeEx() {
+ result = this.(PartialPathNodeFwd).getNodeEx() or
+ result = this.(PartialPathNodeRev).getNodeEx()
+ }
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
@@ -3672,7 +3985,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSourceDistance() {
- result = distSrc(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
+ result = distSrc(this.getNodeEx().getEnclosingCallable(), this.getConfiguration())
}
/**
@@ -3680,7 +3993,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSinkDistance() {
- result = distSink(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
+ result = distSink(this.getNodeEx().getEnclosingCallable(), this.getConfiguration())
}
private string ppAp() {
@@ -3712,7 +4025,7 @@ private module FlowExploration {
}
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
- Node node;
+ NodeEx node;
CallContext cc;
TSummaryCtx1 sc1;
TSummaryCtx2 sc2;
@@ -3721,7 +4034,7 @@ private module FlowExploration {
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
- override Node getNode() { result = node }
+ NodeEx getNodeEx() { result = node }
CallContext getCallContext() { result = cc }
@@ -3734,12 +4047,12 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodeFwd getASuccessor() {
- partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
+ partialPathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx1(),
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
}
predicate isSource() {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
@@ -3748,7 +4061,7 @@ private module FlowExploration {
}
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
- Node node;
+ NodeEx node;
TRevSummaryCtx1 sc1;
TRevSummaryCtx2 sc2;
RevPartialAccessPath ap;
@@ -3756,7 +4069,7 @@ private module FlowExploration {
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
- override Node getNode() { result = node }
+ NodeEx getNodeEx() { result = node }
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
@@ -3767,12 +4080,12 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodeRev getASuccessor() {
- revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
+ revPartialPathStep(result, this.getNodeEx(), this.getSummaryCtx1(), this.getSummaryCtx2(),
this.getAp(), this.getConfiguration())
}
predicate isSink() {
- config.isSink(node) and
+ sinkNode(node, config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil()
@@ -3780,40 +4093,40 @@ private module FlowExploration {
}
private predicate partialPathStep(
- PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
+ PartialPathNodeFwd mid, NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
- not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
+ not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and
(
- localFlowStep(mid.getNode(), node, config) and
+ localFlowStep(mid.getNodeEx(), node, config) and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalLocalFlowStep(mid.getNode(), node, config) and
+ additionalLocalFlowStep(mid.getNodeEx(), node, config) and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
config = mid.getConfiguration()
)
or
- jumpStep(mid.getNode(), node, config) and
+ jumpStep(mid.getNodeEx(), node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalJumpStep(mid.getNode(), node, config) and
+ additionalJumpStep(mid.getNodeEx(), node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
config = mid.getConfiguration()
or
partialPathStoreStep(mid, _, _, node, ap) and
@@ -3826,8 +4139,7 @@ private module FlowExploration {
partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, tc, ap0, config) and
- compatibleTypes(ap.getType(), getNodeType(node))
+ apConsFwd(ap, tc, ap0, config)
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -3846,12 +4158,13 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
+ PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, NodeEx node,
+ PartialAccessPath ap2
) {
- exists(Node midNode, DataFlowType contentType |
- midNode = mid.getNode() and
+ exists(NodeEx midNode, DataFlowType contentType |
+ midNode = mid.getNodeEx() and
ap1 = mid.getAp() and
- store(midNode, tc, node, contentType) and
+ store(midNode, tc, node, contentType, mid.getConfiguration()) and
ap2.getHead() = tc and
ap2.len() = unbindInt(ap1.len() + 1) and
compatibleTypes(ap1.getType(), contentType)
@@ -3870,15 +4183,15 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
+ PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, NodeEx node, CallContext cc,
Configuration config
) {
- exists(Node midNode |
- midNode = mid.getNode() and
+ exists(NodeEx midNode |
+ midNode = mid.getNodeEx() and
ap = mid.getAp() and
- read(midNode, tc.getContent(), node) and
+ read(midNode, tc.getContent(), node, pragma[only_bind_into](config)) and
ap.getHead() = tc and
- config = mid.getConfiguration() and
+ pragma[only_bind_into](config) = mid.getConfiguration() and
cc = mid.getCallContext()
)
}
@@ -3887,7 +4200,7 @@ private module FlowExploration {
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
Configuration config
) {
- pos = getReturnPosition(mid.getNode()) and
+ pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and
innercc = mid.getCallContext() and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
@@ -3910,12 +4223,12 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
- PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = kind.getAnOutNode(call)
+ out.asNode() = kind.getAnOutNode(call)
)
}
@@ -3924,8 +4237,8 @@ private module FlowExploration {
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
- exists(ArgumentNode arg |
- arg = mid.getNode() and
+ exists(ArgNode arg |
+ arg = mid.getNodeEx().asNode() and
cc = mid.getCallContext() and
arg.argumentOf(call, i) and
ap = mid.getAp() and
@@ -3943,7 +4256,7 @@ private module FlowExploration {
}
private predicate partialPathIntoCallable(
- PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
+ PartialPathNodeFwd mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc,
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
@@ -3964,8 +4277,8 @@ private module FlowExploration {
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
- exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
- mid.getNode() = ret and
+ exists(PartialPathNodeFwd mid, RetNodeEx ret |
+ mid.getNodeEx() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
@@ -3980,45 +4293,45 @@ private module FlowExploration {
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
- exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
- partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and
+ exists(CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
+ partialPathIntoCallable(mid, _, cc, innercc, sc1, sc2, call, _, config) and
paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config)
)
}
private predicate partialPathThroughCallable(
- PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
- out = kind.getAnOutNode(call)
+ out.asNode() = kind.getAnOutNode(call)
)
}
private predicate revPartialPathStep(
- PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
+ PartialPathNodeRev mid, NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
RevPartialAccessPath ap, Configuration config
) {
- localFlowStep(node, mid.getNode(), config) and
+ localFlowStep(node, mid.getNodeEx(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalLocalFlowStep(node, mid.getNode(), config) and
+ additionalLocalFlowStep(node, mid.getNodeEx(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
- jumpStep(node, mid.getNode(), config) and
+ jumpStep(node, mid.getNodeEx(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalJumpStep(node, mid.getNode(), config) and
+ additionalJumpStep(node, mid.getNodeEx(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
mid.getAp() instanceof RevPartialAccessPathNil and
@@ -4037,9 +4350,9 @@ private module FlowExploration {
apConsRev(ap, c, ap0, config)
)
or
- exists(ParameterNode p |
- mid.getNode() = p and
- viableParamArg(_, p, node) and
+ exists(ParamNodeEx p |
+ mid.getNodeEx() = p and
+ viableParamArgEx(_, p, node) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
sc1 = TRevSummaryCtx1None() and
@@ -4050,7 +4363,7 @@ private module FlowExploration {
or
exists(ReturnPosition pos |
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
- pos = getReturnPosition(node)
+ pos = getReturnPosition(node.asNode())
)
or
revPartialPathThroughCallable(mid, node, ap, config) and
@@ -4060,12 +4373,13 @@ private module FlowExploration {
pragma[inline]
private predicate revPartialPathReadStep(
- PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
+ PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, NodeEx node,
+ RevPartialAccessPath ap2
) {
- exists(Node midNode |
- midNode = mid.getNode() and
+ exists(NodeEx midNode |
+ midNode = mid.getNodeEx() and
ap1 = mid.getAp() and
- read(node, c, midNode) and
+ read(node, c, midNode, mid.getConfiguration()) and
ap2.getHead() = c and
ap2.len() = unbindInt(ap1.len() + 1)
)
@@ -4083,12 +4397,12 @@ private module FlowExploration {
pragma[nomagic]
private predicate revPartialPathStoreStep(
- PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
+ PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, NodeEx node, Configuration config
) {
- exists(Node midNode, TypedContent tc |
- midNode = mid.getNode() and
+ exists(NodeEx midNode, TypedContent tc |
+ midNode = mid.getNodeEx() and
ap = mid.getAp() and
- store(node, tc, midNode, _) and
+ store(node, tc, midNode, _, config) and
ap.getHead() = c and
config = mid.getConfiguration() and
tc.getContent() = c
@@ -4100,9 +4414,9 @@ private module FlowExploration {
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
DataFlowCall call, RevPartialAccessPath ap, Configuration config
) {
- exists(Node out |
- mid.getNode() = out and
- viableReturnPosOut(call, pos, out) and
+ exists(NodeEx out |
+ mid.getNodeEx() = out and
+ viableReturnPosOutEx(call, pos, out) and
sc1 = TRevSummaryCtx1Some(pos) and
sc2 = TRevSummaryCtx2Some(ap) and
ap = mid.getAp() and
@@ -4115,9 +4429,9 @@ private module FlowExploration {
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
Configuration config
) {
- exists(PartialPathNodeRev mid, ParameterNode p |
- mid.getNode() = p and
- p.isParameterOf(_, pos) and
+ exists(PartialPathNodeRev mid, ParamNodeEx p |
+ mid.getNodeEx() = p and
+ p.getPosition() = pos and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
@@ -4138,11 +4452,11 @@ private module FlowExploration {
pragma[nomagic]
private predicate revPartialPathThroughCallable(
- PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
+ PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, int pos |
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
- node.argumentOf(call, pos)
+ node.asNode().(ArgNode).argumentOf(call, pos)
)
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
index 9498e51e7e61..df59c128ad39 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
@@ -81,6 +81,12 @@ abstract class Configuration extends string {
*/
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
+ /**
+ * Holds if an arbitrary number of implicit read steps of content `c` may be
+ * taken at `node`.
+ */
+ predicate allowImplicitRead(Node node, Content c) { none() }
+
/**
* Gets the virtual dispatch branching limit when calculating field flow.
* This can be overridden to a smaller value to improve performance (a
@@ -182,78 +188,210 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
}
}
-private predicate inBarrier(Node node, Configuration config) {
- config.isBarrierIn(node) and
- config.isSource(node)
+private newtype TNodeEx =
+ TNodeNormal(Node n) or
+ TNodeImplicitRead(Node n, boolean hasRead) {
+ any(Configuration c).allowImplicitRead(n, _) and hasRead = [false, true]
+ }
+
+private class NodeEx extends TNodeEx {
+ string toString() {
+ result = this.asNode().toString()
+ or
+ exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]")
+ }
+
+ Node asNode() { this = TNodeNormal(result) }
+
+ predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) }
+
+ Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) }
+
+ pragma[nomagic]
+ private DataFlowCallable getEnclosingCallable0() {
+ nodeEnclosingCallable(this.projectToNode(), result)
+ }
+
+ pragma[inline]
+ DataFlowCallable getEnclosingCallable() {
+ pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result)
+ }
+
+ pragma[nomagic]
+ private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) }
+
+ pragma[inline]
+ DataFlowType getDataFlowType() {
+ pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result)
+ }
+
+ predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ }
}
-private predicate outBarrier(Node node, Configuration config) {
- config.isBarrierOut(node) and
- config.isSink(node)
+private class ArgNodeEx extends NodeEx {
+ ArgNodeEx() { this.asNode() instanceof ArgNode }
}
-private predicate fullBarrier(Node node, Configuration config) {
- config.isBarrier(node)
- or
- config.isBarrierIn(node) and
- not config.isSource(node)
- or
- config.isBarrierOut(node) and
- not config.isSink(node)
- or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- node = g.getAGuardedNode()
+private class ParamNodeEx extends NodeEx {
+ ParamNodeEx() { this.asNode() instanceof ParamNode }
+
+ predicate isParameterOf(DataFlowCallable c, int i) {
+ this.asNode().(ParamNode).isParameterOf(c, i)
+ }
+
+ int getPosition() { this.isParameterOf(_, result) }
+}
+
+private class RetNodeEx extends NodeEx {
+ RetNodeEx() { this.asNode() instanceof ReturnNodeExt }
+
+ ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) }
+
+ ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() }
+}
+
+private predicate inBarrier(NodeEx node, Configuration config) {
+ exists(Node n |
+ node.asNode() = n and
+ config.isBarrierIn(n) and
+ config.isSource(n)
+ )
+}
+
+private predicate outBarrier(NodeEx node, Configuration config) {
+ exists(Node n |
+ node.asNode() = n and
+ config.isBarrierOut(n) and
+ config.isSink(n)
+ )
+}
+
+private predicate fullBarrier(NodeEx node, Configuration config) {
+ exists(Node n | node.asNode() = n |
+ config.isBarrier(n)
+ or
+ config.isBarrierIn(n) and
+ not config.isSource(n)
+ or
+ config.isBarrierOut(n) and
+ not config.isSink(n)
+ or
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
)
}
+pragma[nomagic]
+private predicate sourceNode(NodeEx node, Configuration config) { config.isSource(node.asNode()) }
+
+pragma[nomagic]
+private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) }
+
/**
* Holds if data can flow in one local step from `node1` to `node2`.
*/
-private predicate localFlowStep(Node node1, Node node2, Configuration config) {
- (
- simpleLocalFlowStep(node1, node2) or
- reverseStepThroughInputOutputAlias(node1, node2)
- ) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ simpleLocalFlowStepExt(n1, n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+ or
+ exists(Node n |
+ config.allowImplicitRead(n, _) and
+ node1.asNode() = n and
+ node2.isImplicitReadNode(n, false)
+ )
}
/**
* Holds if the additional step from `node1` to `node2` does not jump between callables.
*/
-private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
- config.isAdditionalFlowStep(node1, node2) and
- getNodeEnclosingCallable(node1) = getNodeEnclosingCallable(node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ config.isAdditionalFlowStep(n1, n2) and
+ getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+ or
+ exists(Node n |
+ config.allowImplicitRead(n, _) and
+ node1.isImplicitReadNode(n, true) and
+ node2.asNode() = n
+ )
}
/**
* Holds if data can flow from `node1` to `node2` in a way that discards call contexts.
*/
-private predicate jumpStep(Node node1, Node node2, Configuration config) {
- jumpStep(node1, node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ jumpStepCached(n1, n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
}
/**
* Holds if the additional step from `node1` to `node2` jumps between callables.
*/
-private predicate additionalJumpStep(Node node1, Node node2, Configuration config) {
- config.isAdditionalFlowStep(node1, node2) and
- getNodeEnclosingCallable(node1) != getNodeEnclosingCallable(node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ config.isAdditionalFlowStep(n1, n2) and
+ getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+}
+
+private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ read(node1.asNode(), c, node2.asNode())
+ or
+ exists(Node n |
+ node2.isImplicitReadNode(n, true) and
+ node1.isImplicitReadNode(n, _) and
+ config.allowImplicitRead(n, c)
+ )
+}
+
+private predicate store(
+ NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
+) {
+ store(node1.asNode(), tc, node2.asNode(), contentType) and
+ read(_, tc.getContent(), _, config)
+}
+
+pragma[nomagic]
+private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) {
+ viableReturnPosOut(call, pos, out.asNode())
+}
+
+pragma[nomagic]
+private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) {
+ viableParamArg(call, p.asNode(), arg.asNode())
}
/**
@@ -277,39 +415,39 @@ private module Stage1 {
* The Boolean `cc` records whether the node is reached through an
* argument in a call.
*/
- predicate fwdFlow(Node node, Cc cc, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
not fullBarrier(node, config) and
(
- config.isSource(node) and
+ sourceNode(node, config) and
cc = false
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
localFlowStep(mid, node, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
additionalLocalFlowStep(mid, node, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, config) and
jumpStep(mid, node, config) and
cc = false
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, config) and
additionalJumpStep(mid, node, config) and
cc = false
)
or
// store
- exists(Node mid |
+ exists(NodeEx mid |
useFieldFlow(config) and
fwdFlow(mid, cc, config) and
- store(mid, _, node, _) and
+ store(mid, _, node, _, config) and
not outBarrier(mid, config)
)
or
@@ -321,9 +459,9 @@ private module Stage1 {
)
or
// flow into a callable
- exists(Node arg |
+ exists(NodeEx arg |
fwdFlow(arg, _, config) and
- viableParamArg(_, node, arg) and
+ viableParamArgEx(_, node, arg) and
cc = true
)
or
@@ -338,13 +476,13 @@ private module Stage1 {
)
}
- private predicate fwdFlow(Node node, Configuration config) { fwdFlow(node, _, config) }
+ private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
pragma[nomagic]
- private predicate fwdFlowRead(Content c, Node node, Cc cc, Configuration config) {
- exists(Node mid |
+ private predicate fwdFlowRead(Content c, NodeEx node, Cc cc, Configuration config) {
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
- read(mid, c, node)
+ read(mid, c, node, config)
)
}
@@ -353,33 +491,33 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate fwdFlowConsCand(Content c, Configuration config) {
- exists(Node mid, Node node, TypedContent tc |
+ exists(NodeEx mid, NodeEx node, TypedContent tc |
not fullBarrier(node, config) and
useFieldFlow(config) and
fwdFlow(mid, _, config) and
- store(mid, tc, node, _) and
+ store(mid, tc, node, _, config) and
c = tc.getContent()
)
}
pragma[nomagic]
private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc, Configuration config) {
- exists(ReturnNodeExt ret |
+ exists(RetNodeEx ret |
fwdFlow(ret, cc, config) and
- getReturnPosition(ret) = pos
+ ret.getReturnPosition() = pos
)
}
pragma[nomagic]
- private predicate fwdFlowOut(DataFlowCall call, Node out, Cc cc, Configuration config) {
+ private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
exists(ReturnPosition pos |
fwdFlowReturnPosition(pos, cc, config) and
- viableReturnPosOut(call, pos, out)
+ viableReturnPosOutEx(call, pos, out)
)
}
pragma[nomagic]
- private predicate fwdFlowOutFromArg(DataFlowCall call, Node out, Configuration config) {
+ private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out, Configuration config) {
fwdFlowOut(call, out, true, config)
}
@@ -388,9 +526,9 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
- exists(ArgumentNode arg |
+ exists(ArgNodeEx arg |
fwdFlow(arg, cc, config) and
- viableParamArg(call, _, arg)
+ viableParamArgEx(call, _, arg)
)
}
@@ -402,34 +540,34 @@ private module Stage1 {
* the enclosing callable in order to reach a sink.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and
fwdFlow(node, config)
}
pragma[nomagic]
- private predicate revFlow0(Node node, boolean toReturn, Configuration config) {
+ private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStep(node, mid, config) and
revFlow(mid, toReturn, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
additionalLocalFlowStep(node, mid, config) and
revFlow(mid, toReturn, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, config) and
toReturn = false
)
or
- exists(Node mid |
+ exists(NodeEx mid |
additionalJumpStep(node, mid, config) and
revFlow(mid, _, config) and
toReturn = false
@@ -442,8 +580,8 @@ private module Stage1 {
)
or
// read
- exists(Node mid, Content c |
- read(node, c, mid) and
+ exists(NodeEx mid, Content c |
+ read(node, c, mid, config) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
revFlow(mid, toReturn, pragma[only_bind_into](config))
)
@@ -460,7 +598,7 @@ private module Stage1 {
// flow out of a callable
exists(ReturnPosition pos |
revFlowOut(pos, config) and
- getReturnPosition(node) = pos and
+ node.(RetNodeEx).getReturnPosition() = pos and
toReturn = true
)
}
@@ -470,20 +608,20 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Content c, Configuration config) {
- exists(Node mid, Node node |
+ exists(NodeEx mid, NodeEx node |
fwdFlow(node, pragma[only_bind_into](config)) and
- read(node, c, mid) and
+ read(node, c, mid, config) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
revFlow(pragma[only_bind_into](mid), _, pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate revFlowStore(Content c, Node node, boolean toReturn, Configuration config) {
- exists(Node mid, TypedContent tc |
+ private predicate revFlowStore(Content c, NodeEx node, boolean toReturn, Configuration config) {
+ exists(NodeEx mid, TypedContent tc |
revFlow(mid, toReturn, pragma[only_bind_into](config)) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
- store(node, tc, mid, _) and
+ store(node, tc, mid, _, config) and
c = tc.getContent()
)
}
@@ -499,15 +637,15 @@ private module Stage1 {
pragma[nomagic]
predicate viableReturnPosOutNodeCandFwd1(
- DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+ DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
fwdFlowReturnPosition(pos, _, config) and
- viableReturnPosOut(call, pos, out)
+ viableReturnPosOutEx(call, pos, out)
}
pragma[nomagic]
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
- exists(DataFlowCall call, Node out |
+ exists(DataFlowCall call, NodeEx out |
revFlow(out, _, config) and
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
)
@@ -515,33 +653,35 @@ private module Stage1 {
pragma[nomagic]
predicate viableParamArgNodeCandFwd1(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+ DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
- viableParamArg(call, p, arg) and
+ viableParamArgEx(call, p, arg) and
fwdFlow(arg, config)
}
pragma[nomagic]
private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config
+ DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
revFlow(p, toReturn, config) and
viableParamArgNodeCandFwd1(call, p, arg, config)
)
}
pragma[nomagic]
- private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
+ private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg, Configuration config) {
revFlowIn(call, arg, true, config)
}
/**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`.
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
*/
pragma[nomagic]
private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) {
- exists(Node out |
+ exists(NodeEx out |
revFlow(out, toReturn, config) and
fwdFlowOutFromArg(call, out, config)
)
@@ -549,32 +689,33 @@ private module Stage1 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Content c |
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
revFlow(node2, pragma[only_bind_into](config)) and
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
c = tc.getContent() and
exists(ap1)
)
}
pragma[nomagic]
- predicate readStepCand(Node n1, Content c, Node n2, Configuration config) {
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) and
- read(n1, c, n2)
+ read(n1, c, n2, pragma[only_bind_into](config))
}
pragma[nomagic]
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow(node, toReturn, config) and exists(returnAp) and exists(ap)
}
- private predicate throughFlowNodeCand(Node node, Configuration config) {
+ private predicate throughFlowNodeCand(NodeEx node, Configuration config) {
revFlow(node, true, config) and
fwdFlow(node, true, config) and
not inBarrier(node, config) and
@@ -586,9 +727,9 @@ private module Stage1 {
private predicate returnFlowCallableNodeCand(
DataFlowCallable callable, ReturnKindExt kind, Configuration config
) {
- exists(ReturnNodeExt ret |
+ exists(RetNodeEx ret |
throughFlowNodeCand(ret, config) and
- callable = getNodeEnclosingCallable(ret) and
+ callable = ret.getEnclosingCallable() and
kind = ret.getKind()
)
}
@@ -597,50 +738,57 @@ private module Stage1 {
* Holds if flow may enter through `p` and reach a return node making `p` a
* candidate for the origin of a summary.
*/
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
- getNodeEnclosingCallable(p) = c and
+ p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself
- not exists(int pos |
- kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos)
- )
+ not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
+ )
+ }
+
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(ArgNodeEx arg, boolean toReturn |
+ revFlow(arg, toReturn, config) and
+ revFlowInToReturn(call, arg, config) and
+ revFlowIsReturned(call, toReturn, config)
)
}
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, config)) and
fields = count(Content f0 | fwdFlowConsCand(f0, config)) and
conscand = -1 and
- tuples = count(Node n, boolean b | fwdFlow(n, b, config))
+ tuples = count(NodeEx n, boolean b | fwdFlow(n, b, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, config)) and
fields = count(Content f0 | revFlowConsCand(f0, config)) and
conscand = -1 and
- tuples = count(Node n, boolean b | revFlow(n, b, config))
+ tuples = count(NodeEx n, boolean b | revFlow(n, b, config))
}
/* End: Stage 1 logic. */
}
pragma[noinline]
-private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) {
Stage1::revFlow(node2, config) and
localFlowStep(node1, node2, config)
}
pragma[noinline]
-private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) {
Stage1::revFlow(node2, config) and
additionalLocalFlowStep(node1, node2, config)
}
pragma[nomagic]
private predicate viableReturnPosOutNodeCand1(
- DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+ DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
Stage1::revFlow(out, config) and
Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out, config)
@@ -653,9 +801,9 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, ReturnNodeExt ret, Node out, Configuration config
+ DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
) {
- viableReturnPosOutNodeCand1(call, getReturnPosition(ret), out, config) and
+ viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
@@ -663,7 +811,7 @@ private predicate flowOutOfCallNodeCand1(
pragma[nomagic]
private predicate viableParamArgNodeCand1(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+ DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and
Stage1::revFlow(arg, config)
@@ -675,7 +823,7 @@ private predicate viableParamArgNodeCand1(
*/
pragma[nomagic]
private predicate flowIntoCallNodeCand1(
- DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, Configuration config
) {
viableParamArgNodeCand1(call, p, arg, config) and
Stage1::revFlow(p, config) and
@@ -688,9 +836,9 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
-private int branch(Node n1, Configuration conf) {
+private int branch(NodeEx n1, Configuration conf) {
result =
- strictcount(Node n |
+ strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -700,9 +848,9 @@ private int branch(Node n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
-private int join(Node n2, Configuration conf) {
+private int join(NodeEx n2, Configuration conf) {
result =
- strictcount(Node n |
+ strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -716,7 +864,7 @@ private int join(Node n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
exists(int b, int j |
@@ -735,8 +883,7 @@ private predicate flowOutOfCallNodeCand1(
*/
pragma[nomagic]
private predicate flowIntoCallNodeCand1(
- DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
exists(int b, int j |
@@ -762,7 +909,7 @@ private module Stage2 {
bindingset[result, ap]
private ApApprox getApprox(Ap ap) { any() }
- private ApNil getApNil(Node node) { PrevStage::revFlow(node, _) and exists(result) }
+ private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
@@ -805,10 +952,10 @@ private module Stage2 {
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { any() }
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
(
preservesValue = true and
@@ -829,10 +976,20 @@ private module Stage2 {
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
/* Begin: Stage 2 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -842,14 +999,14 @@ private module Stage2 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -860,7 +1017,7 @@ private module Stage2 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -868,7 +1025,7 @@ private module Stage2 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -899,19 +1056,17 @@ private module Stage2 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -935,7 +1090,7 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -944,32 +1099,29 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -979,9 +1131,15 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -992,7 +1150,7 @@ private module Stage2 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
)
@@ -1000,18 +1158,41 @@ private module Stage2 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1021,41 +1202,41 @@ private module Stage2 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -1071,20 +1252,18 @@ private module Stage2 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -1097,7 +1276,7 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -1111,7 +1290,7 @@ private module Stage2 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -1120,10 +1299,10 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -1132,13 +1311,12 @@ private module Stage2 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1146,9 +1324,14 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1160,7 +1343,7 @@ private module Stage2 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -1169,16 +1352,17 @@ private module Stage2 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -1187,7 +1371,7 @@ private module Stage2 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -1199,44 +1383,54 @@ private module Stage2 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 2 logic. */
}
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
- DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
@@ -1245,7 +1439,7 @@ private predicate flowOutOfCallNodeCand2(
pragma[nomagic]
private predicate flowIntoCallNodeCand2(
- DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
@@ -1258,10 +1452,10 @@ private module LocalFlowBigStep {
* A node where some checking is required, and hence the big-step relation
* is not allowed to step over.
*/
- private class FlowCheckNode extends Node {
+ private class FlowCheckNode extends NodeEx {
FlowCheckNode() {
- this instanceof CastNode or
- clearsContent(this, _)
+ castNode(this.asNode()) or
+ clearsContentCached(this.asNode(), _)
}
}
@@ -1269,16 +1463,16 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- predicate localFlowEntry(Node node, Configuration config) {
+ predicate localFlowEntry(NodeEx node, Configuration config) {
Stage2::revFlow(node, config) and
(
- config.isSource(node) or
+ sourceNode(node, config) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
- node instanceof ParameterNode or
- node instanceof OutNodeExt or
- store(_, _, node, _) or
- read(_, _, node) or
+ node instanceof ParamNodeEx or
+ node.asNode() instanceof OutNodeExt or
+ store(_, _, node, _, config) or
+ read(_, _, node, config) or
node instanceof FlowCheckNode
)
}
@@ -1287,23 +1481,25 @@ private module LocalFlowBigStep {
* Holds if `node` can be the last node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- private predicate localFlowExit(Node node, Configuration config) {
- exists(Node next | Stage2::revFlow(next, config) |
+ private predicate localFlowExit(NodeEx node, Configuration config) {
+ exists(NodeEx next | Stage2::revFlow(next, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
- store(node, _, next, _) or
- read(node, _, next)
+ store(node, _, next, _, config) or
+ read(node, _, next, config)
)
or
node instanceof FlowCheckNode
or
- config.isSink(node)
+ sinkNode(node, config)
}
pragma[noinline]
- private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
+ private predicate additionalLocalFlowStepNodeCand2(
+ NodeEx node1, NodeEx node2, Configuration config
+ ) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and
Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config))
@@ -1318,39 +1514,39 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
- Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config,
+ NodeEx node1, NodeEx node2, boolean preservesValue, DataFlowType t, Configuration config,
LocalCallContext cc
) {
- not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
+ not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
(
localFlowEntry(node1, pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
- t = getNodeType(node1)
+ t = node1.getDataFlowType() // irrelevant dummy value
or
additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
- t = getNodeType(node2)
+ t = node2.getDataFlowType()
) and
node1 != node2 and
- cc.relevantFor(getNodeEnclosingCallable(node1)) and
- not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
+ cc.relevantFor(node1.getEnclosingCallable()) and
+ not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
Stage2::revFlow(node2, pragma[only_bind_into](config))
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStepPlus(node1, mid, preservesValue, t, pragma[only_bind_into](config), cc) and
localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof FlowCheckNode and
Stage2::revFlow(node2, pragma[only_bind_into](config))
)
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStepPlus(node1, mid, _, _, pragma[only_bind_into](config), cc) and
additionalLocalFlowStepNodeCand2(mid, node2, config) and
not mid instanceof FlowCheckNode and
preservesValue = false and
- t = getNodeType(node2) and
+ t = node2.getDataFlowType() and
Stage2::revFlow(node2, pragma[only_bind_into](config))
)
)
@@ -1362,8 +1558,8 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, AccessPathFrontNil apf, Configuration config,
- LocalCallContext callContext
+ NodeEx node1, NodeEx node2, boolean preservesValue, AccessPathFrontNil apf,
+ Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, config)
@@ -1383,8 +1579,8 @@ private module Stage3 {
private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(Node node) {
- PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node))
+ private ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
@@ -1428,10 +1624,10 @@ private module Stage3 {
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { any() }
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, node2, preservesValue, ap, config, _) and exists(lcc)
}
@@ -1440,10 +1636,16 @@ private module Stage3 {
private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ pragma[nomagic]
+ private predicate clear(NodeEx node, Ap ap) { ap.isClearedAt(node.asNode()) }
+
+ pragma[nomagic]
+ private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
+
bindingset[node, ap]
- private predicate filter(Node node, Ap ap) {
- not ap.isClearedAt(node) and
- if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any()
+ private predicate filter(NodeEx node, Ap ap) {
+ not clear(node, ap) and
+ if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()
}
bindingset[ap, contentType]
@@ -1454,7 +1656,7 @@ private module Stage3 {
}
/* Begin: Stage 3 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
@@ -1465,6 +1667,16 @@ private module Stage3 {
)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -1474,21 +1686,21 @@ private module Stage3 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
pragma[nomagic]
- private predicate fwdFlow0(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -1499,7 +1711,7 @@ private module Stage3 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -1507,7 +1719,7 @@ private module Stage3 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -1538,19 +1750,17 @@ private module Stage3 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -1574,7 +1784,7 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -1583,32 +1793,29 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1618,9 +1825,15 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1631,7 +1844,7 @@ private module Stage3 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
@@ -1639,18 +1852,41 @@ private module Stage3 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1660,41 +1896,41 @@ private module Stage3 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -1710,20 +1946,18 @@ private module Stage3 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -1736,7 +1970,7 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -1750,7 +1984,7 @@ private module Stage3 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -1759,10 +1993,10 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -1771,13 +2005,12 @@ private module Stage3 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1785,9 +2018,14 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1799,7 +2037,7 @@ private module Stage3 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -1808,16 +2046,17 @@ private module Stage3 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -1826,7 +2065,7 @@ private module Stage3 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -1838,37 +2077,47 @@ private module Stage3 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 3 logic. */
}
@@ -1877,7 +2126,7 @@ private module Stage3 {
* Holds if `argApf` is recorded as the summary context for flow reaching `node`
* and remains relevant for the following pruning stage.
*/
-private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) {
+private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
@@ -1892,7 +2141,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
exists(int tails, int nodes, int apLimit, int tupleLimit |
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
- strictcount(Node n |
+ strictcount(NodeEx n |
Stage3::revFlow(n, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
@@ -2087,8 +2336,8 @@ private module Stage4 {
private ApApprox getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(Node node) {
- PrevStage::revFlow(node, _) and result = TNil(getNodeType(node))
+ private ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
@@ -2127,26 +2376,25 @@ private module Stage4 {
bindingset[innercc, inner, call]
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
resolveReturn(innercc, inner, call)
- or
- innercc.(CallContextCall).matchesCall(call)
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
localFlowEntry(node, config) and
- result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node))
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
}
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, node2, preservesValue, ap.getFront(), config, lcc)
}
pragma[nomagic]
private predicate flowOutOfCall(
- DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
@@ -2155,7 +2403,7 @@ private module Stage4 {
pragma[nomagic]
private predicate flowIntoCall(
- DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
@@ -2164,14 +2412,14 @@ private module Stage4 {
}
bindingset[node, ap]
- private predicate filter(Node node, Ap ap) { any() }
+ private predicate filter(NodeEx node, Ap ap) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
/* Begin: Stage 4 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
@@ -2182,6 +2430,16 @@ private module Stage4 {
)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -2191,21 +2449,21 @@ private module Stage4 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
pragma[nomagic]
- private predicate fwdFlow0(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -2216,7 +2474,7 @@ private module Stage4 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -2224,7 +2482,7 @@ private module Stage4 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -2255,19 +2513,17 @@ private module Stage4 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -2291,7 +2547,7 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -2300,32 +2556,29 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -2335,9 +2588,15 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -2348,7 +2607,7 @@ private module Stage4 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
@@ -2356,18 +2615,41 @@ private module Stage4 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -2377,41 +2659,41 @@ private module Stage4 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -2427,20 +2709,18 @@ private module Stage4 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -2453,7 +2733,7 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -2467,7 +2747,7 @@ private module Stage4 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -2476,10 +2756,10 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -2488,13 +2768,12 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -2502,9 +2781,14 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -2516,7 +2800,7 @@ private module Stage4 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -2525,16 +2809,17 @@ private module Stage4 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -2543,7 +2828,7 @@ private module Stage4 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -2555,37 +2840,47 @@ private module Stage4 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 4 logic. */
}
@@ -2595,18 +2890,18 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
}
-private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) {
+private predicate nodeMayUseSummary(NodeEx n, AccessPathApprox apa, Configuration config) {
exists(DataFlowCallable c, AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and
Stage4::revFlow(n, true, _, apa0, config) and
Stage4::fwdFlow(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
- getNodeEnclosingCallable(n) = c
+ n.getEnclosingCallable() = c
)
}
private newtype TSummaryCtx =
TSummaryCtxNone() or
- TSummaryCtxSome(ParameterNode p, AccessPath ap) {
+ TSummaryCtxSome(ParamNodeEx p, AccessPath ap) {
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _)
}
@@ -2627,7 +2922,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone {
/** A summary context from which a flow summary can be generated. */
private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
- private ParameterNode p;
+ private ParamNodeEx p;
private AccessPath ap;
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
@@ -2660,7 +2955,9 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
- strictcount(Node n | Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config))
+ strictcount(NodeEx n |
+ Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)
+ )
}
/**
@@ -2752,13 +3049,13 @@ private newtype TAccessPath =
}
private newtype TPathNode =
- TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
+ TPathNodeMid(NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
- ap = TAccessPathNil(getNodeType(node))
+ ap = TAccessPathNil(node.getDataFlowType())
or
// ... or a step from an existing PathNode to another node.
exists(PathNodeMid mid |
@@ -2767,12 +3064,12 @@ private newtype TPathNode =
Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config))
)
} or
- TPathNodeSink(Node node, Configuration config) {
- pragma[only_bind_into](config).isSink(node) and
+ TPathNodeSink(NodeEx node, Configuration config) {
+ sinkNode(node, pragma[only_bind_into](config)) and
Stage4::revFlow(node, pragma[only_bind_into](config)) and
(
// A sink that is also a source ...
- config.isSource(node)
+ sourceNode(node, config)
or
// ... or a sink that can be reached from a source
exists(PathNodeMid mid |
@@ -2973,15 +3270,17 @@ class PathNode extends TPathNode {
}
/** Gets the underlying `Node`. */
- Node getNode() { none() }
+ final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
private predicate isHidden() {
- nodeIsHidden(this.getNode()) and
+ hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
+ or
+ this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
}
private PathNode getASuccessorIfHidden() {
@@ -3003,6 +3302,8 @@ class PathNode extends TPathNode {
abstract private class PathNodeImpl extends PathNode {
abstract PathNode getASuccessorImpl();
+ abstract NodeEx getNodeEx();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3017,14 +3318,14 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
}
- override string toString() { result = this.getNode().toString() + ppAp() }
+ override string toString() { result = this.getNodeEx().toString() + ppAp() }
- override string toStringWithContext() { result = this.getNode().toString() + ppAp() + ppCtx() }
+ override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
@@ -3054,7 +3355,7 @@ module PathGraph {
* a `CallContext`, and a `Configuration`.
*/
private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
- Node node;
+ NodeEx node;
CallContext cc;
SummaryCtx sc;
AccessPath ap;
@@ -3062,7 +3363,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) }
- override Node getNode() { result = node }
+ override NodeEx getNodeEx() { result = node }
CallContext getCallContext() { result = cc }
@@ -3073,7 +3374,8 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
override Configuration getConfiguration() { result = config }
private PathNodeMid getSuccMid() {
- pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and
+ pathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx(),
+ result.getAp()) and
result.getConfiguration() = unbindConf(this.getConfiguration())
}
@@ -3084,7 +3386,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid, PathNodeSink sink |
mid = getSuccMid() and
- mid.getNode() = sink.getNode() and
+ mid.getNodeEx() = sink.getNodeEx() and
mid.getAp() instanceof AccessPathNil and
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
result = sink
@@ -3092,7 +3394,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
}
override predicate isSource() {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
@@ -3105,31 +3407,35 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
* excluding the `CallContext`.
*/
private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
- Node node;
+ NodeEx node;
Configuration config;
PathNodeSink() { this = TPathNodeSink(node, config) }
- override Node getNode() { result = node }
+ override NodeEx getNodeEx() { result = node }
override Configuration getConfiguration() { result = config }
override PathNode getASuccessorImpl() { none() }
- override predicate isSource() { config.isSource(node) }
+ override predicate isSource() { sourceNode(node, config) }
}
/**
* Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`.
*/
-private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
- exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
- midnode = mid.getNode() and
+private predicate pathStep(
+ PathNodeMid mid, NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap
+) {
+ exists(AccessPath ap0, NodeEx midnode, Configuration conf, LocalCallContext localCC |
+ midnode = mid.getNodeEx() and
conf = mid.getConfiguration() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
- localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and
+ localCC =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ midnode.getEnclosingCallable()) and
ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
@@ -3139,16 +3445,16 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
ap0 instanceof AccessPathNil
)
or
- jumpStep(mid.getNode(), node, mid.getConfiguration()) and
+ jumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
ap = mid.getAp()
or
- additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and
+ additionalJumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
mid.getAp() instanceof AccessPathNil and
- ap = TAccessPathNil(getNodeType(node))
+ ap = TAccessPathNil(node.getDataFlowType())
or
exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and
sc = mid.getSummaryCtx()
@@ -3165,20 +3471,20 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
pragma[nomagic]
private predicate pathReadStep(
- PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+ PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
- Stage4::readStepCand(mid.getNode(), tc.getContent(), node, mid.getConfiguration()) and
+ Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+ PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- Stage4::storeStepCand(mid.getNode(), _, tc, node, _, mid.getConfiguration()) and
+ Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
cc = mid.getCallContext()
}
@@ -3186,7 +3492,7 @@ private predicate pathOutOfCallable0(
PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa,
Configuration config
) {
- pos = getReturnPosition(mid.getNode()) and
+ pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and
innercc = mid.getCallContext() and
innercc instanceof CallContextNoCall and
apa = mid.getAp().getApprox() and
@@ -3209,10 +3515,10 @@ private predicate pathOutOfCallable1(
}
pragma[noinline]
-private Node getAnOutNodeFlow(
+private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
- result = kind.getAnOutNode(call) and
+ result.asNode() = kind.getAnOutNode(call) and
Stage4::revFlow(result, _, _, apa, config)
}
@@ -3221,7 +3527,7 @@ private Node getAnOutNodeFlow(
* is a return from a callable and is recorded by `cc`, if needed.
*/
pragma[noinline]
-private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
+private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) {
exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config |
pathOutOfCallable1(mid, call, kind, cc, apa, config) and
out = getAnOutNodeFlow(kind, call, apa, config)
@@ -3235,8 +3541,8 @@ pragma[noinline]
private predicate pathIntoArg(
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa
) {
- exists(ArgumentNode arg |
- arg = mid.getNode() and
+ exists(ArgNode arg |
+ arg = mid.getNodeEx().asNode() and
cc = mid.getCallContext() and
arg.argumentOf(call, i) and
ap = mid.getAp() and
@@ -3248,7 +3554,7 @@ pragma[noinline]
private predicate parameterCand(
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
Stage4::revFlow(p, _, _, apa, config) and
p.isParameterOf(callable, i)
)
@@ -3272,7 +3578,7 @@ private predicate pathIntoCallable0(
* respectively.
*/
private predicate pathIntoCallable(
- PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
+ PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
DataFlowCall call
) {
exists(int i, DataFlowCallable callable, AccessPath ap |
@@ -3297,8 +3603,8 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa,
Configuration config
) {
- exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
- mid.getNode() = ret and
+ exists(PathNodeMid mid, RetNodeEx ret, int pos |
+ mid.getNodeEx() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
@@ -3326,7 +3632,7 @@ private predicate pathThroughCallable0(
* The context `cc` is restored to its value prior to entering the callable.
*/
pragma[noinline]
-private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
+private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) {
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
pathThroughCallable0(call, mid, kind, cc, ap, apa) and
out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
@@ -3344,9 +3650,9 @@ private predicate flowsTo(
) {
flowsource.isSource() and
flowsource.getConfiguration() = configuration and
- flowsource.getNode() = source and
+ flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
- flowsink.getNode() = sink
+ flowsink.getNodeEx().asNode() = sink
}
/**
@@ -3361,13 +3667,13 @@ predicate flowsTo(Node source, Node sink, Configuration configuration) {
private predicate finalStats(boolean fwd, int nodes, int fields, int conscand, int tuples) {
fwd = true and
- nodes = count(Node n0 | exists(PathNode pn | pn.getNode() = n0)) and
+ nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
tuples = count(PathNode pn)
or
fwd = false and
- nodes = count(Node n0 | exists(PathNode pn | pn.getNode() = n0 and reach(pn))) and
+ nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
tuples = count(PathNode pn | reach(pn))
@@ -3404,19 +3710,19 @@ predicate stageStats(
private module FlowExploration {
private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2, Configuration config) {
- exists(Node node1, Node node2 |
+ exists(NodeEx node1, NodeEx node2 |
jumpStep(node1, node2, config)
or
additionalJumpStep(node1, node2, config)
or
// flow into callable
- viableParamArg(_, node2, node1)
+ viableParamArgEx(_, node2, node1)
or
// flow out of a callable
- viableReturnPosOut(_, getReturnPosition(node1), node2)
+ viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2)
|
- c1 = getNodeEnclosingCallable(node1) and
- c2 = getNodeEnclosingCallable(node2) and
+ c1 = node1.getEnclosingCallable() and
+ c2 = node2.getEnclosingCallable() and
c1 != c2
)
}
@@ -3568,7 +3874,7 @@ private module FlowExploration {
private newtype TSummaryCtx1 =
TSummaryCtx1None() or
- TSummaryCtx1Param(ParameterNode p)
+ TSummaryCtx1Param(ParamNodeEx p)
private newtype TSummaryCtx2 =
TSummaryCtx2None() or
@@ -3584,25 +3890,25 @@ private module FlowExploration {
private newtype TPartialPathNode =
TPartialPathNodeFwd(
- Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
+ NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
- distSrc(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
+ distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
- Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
+ NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
Configuration config
) {
- config.isSink(node) and
+ sinkNode(node, config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil() and
@@ -3611,23 +3917,23 @@ private module FlowExploration {
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
- not clearsContent(node, ap.getHead()) and
+ not clearsContentCached(node.asNode(), ap.getHead()) and
not fullBarrier(node, config) and
- distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
+ distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
}
pragma[nomagic]
private predicate partialPathNodeMk0(
- Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
+ NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
- not clearsContent(node, ap.getHead().getContent()) and
- if node instanceof CastingNode
- then compatibleTypes(getNodeType(node), ap.getType())
+ not clearsContentCached(node.asNode(), ap.getHead().getContent()) and
+ if node.asNode() instanceof CastingNode
+ then compatibleTypes(node.getDataFlowType(), ap.getType())
else any()
)
}
@@ -3637,13 +3943,15 @@ private module FlowExploration {
*/
class PartialPathNode extends TPartialPathNode {
/** Gets a textual representation of this element. */
- string toString() { result = this.getNode().toString() + this.ppAp() }
+ string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/**
* Gets a textual representation of this element, including a textual
* representation of the call context.
*/
- string toStringWithContext() { result = this.getNode().toString() + this.ppAp() + this.ppCtx() }
+ string toStringWithContext() {
+ result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
+ }
/**
* Holds if this element is at the specified location.
@@ -3655,11 +3963,16 @@ private module FlowExploration {
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the underlying `Node`. */
- Node getNode() { none() }
+ final Node getNode() { this.getNodeEx().projectToNode() = result }
+
+ private NodeEx getNodeEx() {
+ result = this.(PartialPathNodeFwd).getNodeEx() or
+ result = this.(PartialPathNodeRev).getNodeEx()
+ }
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
@@ -3672,7 +3985,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSourceDistance() {
- result = distSrc(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
+ result = distSrc(this.getNodeEx().getEnclosingCallable(), this.getConfiguration())
}
/**
@@ -3680,7 +3993,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSinkDistance() {
- result = distSink(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
+ result = distSink(this.getNodeEx().getEnclosingCallable(), this.getConfiguration())
}
private string ppAp() {
@@ -3712,7 +4025,7 @@ private module FlowExploration {
}
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
- Node node;
+ NodeEx node;
CallContext cc;
TSummaryCtx1 sc1;
TSummaryCtx2 sc2;
@@ -3721,7 +4034,7 @@ private module FlowExploration {
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
- override Node getNode() { result = node }
+ NodeEx getNodeEx() { result = node }
CallContext getCallContext() { result = cc }
@@ -3734,12 +4047,12 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodeFwd getASuccessor() {
- partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
+ partialPathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx1(),
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
}
predicate isSource() {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
@@ -3748,7 +4061,7 @@ private module FlowExploration {
}
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
- Node node;
+ NodeEx node;
TRevSummaryCtx1 sc1;
TRevSummaryCtx2 sc2;
RevPartialAccessPath ap;
@@ -3756,7 +4069,7 @@ private module FlowExploration {
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
- override Node getNode() { result = node }
+ NodeEx getNodeEx() { result = node }
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
@@ -3767,12 +4080,12 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodeRev getASuccessor() {
- revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
+ revPartialPathStep(result, this.getNodeEx(), this.getSummaryCtx1(), this.getSummaryCtx2(),
this.getAp(), this.getConfiguration())
}
predicate isSink() {
- config.isSink(node) and
+ sinkNode(node, config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil()
@@ -3780,40 +4093,40 @@ private module FlowExploration {
}
private predicate partialPathStep(
- PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
+ PartialPathNodeFwd mid, NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
- not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
+ not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and
(
- localFlowStep(mid.getNode(), node, config) and
+ localFlowStep(mid.getNodeEx(), node, config) and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalLocalFlowStep(mid.getNode(), node, config) and
+ additionalLocalFlowStep(mid.getNodeEx(), node, config) and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
config = mid.getConfiguration()
)
or
- jumpStep(mid.getNode(), node, config) and
+ jumpStep(mid.getNodeEx(), node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalJumpStep(mid.getNode(), node, config) and
+ additionalJumpStep(mid.getNodeEx(), node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
config = mid.getConfiguration()
or
partialPathStoreStep(mid, _, _, node, ap) and
@@ -3826,8 +4139,7 @@ private module FlowExploration {
partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, tc, ap0, config) and
- compatibleTypes(ap.getType(), getNodeType(node))
+ apConsFwd(ap, tc, ap0, config)
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -3846,12 +4158,13 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
+ PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, NodeEx node,
+ PartialAccessPath ap2
) {
- exists(Node midNode, DataFlowType contentType |
- midNode = mid.getNode() and
+ exists(NodeEx midNode, DataFlowType contentType |
+ midNode = mid.getNodeEx() and
ap1 = mid.getAp() and
- store(midNode, tc, node, contentType) and
+ store(midNode, tc, node, contentType, mid.getConfiguration()) and
ap2.getHead() = tc and
ap2.len() = unbindInt(ap1.len() + 1) and
compatibleTypes(ap1.getType(), contentType)
@@ -3870,15 +4183,15 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
+ PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, NodeEx node, CallContext cc,
Configuration config
) {
- exists(Node midNode |
- midNode = mid.getNode() and
+ exists(NodeEx midNode |
+ midNode = mid.getNodeEx() and
ap = mid.getAp() and
- read(midNode, tc.getContent(), node) and
+ read(midNode, tc.getContent(), node, pragma[only_bind_into](config)) and
ap.getHead() = tc and
- config = mid.getConfiguration() and
+ pragma[only_bind_into](config) = mid.getConfiguration() and
cc = mid.getCallContext()
)
}
@@ -3887,7 +4200,7 @@ private module FlowExploration {
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
Configuration config
) {
- pos = getReturnPosition(mid.getNode()) and
+ pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and
innercc = mid.getCallContext() and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
@@ -3910,12 +4223,12 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
- PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = kind.getAnOutNode(call)
+ out.asNode() = kind.getAnOutNode(call)
)
}
@@ -3924,8 +4237,8 @@ private module FlowExploration {
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
- exists(ArgumentNode arg |
- arg = mid.getNode() and
+ exists(ArgNode arg |
+ arg = mid.getNodeEx().asNode() and
cc = mid.getCallContext() and
arg.argumentOf(call, i) and
ap = mid.getAp() and
@@ -3943,7 +4256,7 @@ private module FlowExploration {
}
private predicate partialPathIntoCallable(
- PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
+ PartialPathNodeFwd mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc,
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
@@ -3964,8 +4277,8 @@ private module FlowExploration {
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
- exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
- mid.getNode() = ret and
+ exists(PartialPathNodeFwd mid, RetNodeEx ret |
+ mid.getNodeEx() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
@@ -3980,45 +4293,45 @@ private module FlowExploration {
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
- exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
- partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and
+ exists(CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
+ partialPathIntoCallable(mid, _, cc, innercc, sc1, sc2, call, _, config) and
paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config)
)
}
private predicate partialPathThroughCallable(
- PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
- out = kind.getAnOutNode(call)
+ out.asNode() = kind.getAnOutNode(call)
)
}
private predicate revPartialPathStep(
- PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
+ PartialPathNodeRev mid, NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
RevPartialAccessPath ap, Configuration config
) {
- localFlowStep(node, mid.getNode(), config) and
+ localFlowStep(node, mid.getNodeEx(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalLocalFlowStep(node, mid.getNode(), config) and
+ additionalLocalFlowStep(node, mid.getNodeEx(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
- jumpStep(node, mid.getNode(), config) and
+ jumpStep(node, mid.getNodeEx(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalJumpStep(node, mid.getNode(), config) and
+ additionalJumpStep(node, mid.getNodeEx(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
mid.getAp() instanceof RevPartialAccessPathNil and
@@ -4037,9 +4350,9 @@ private module FlowExploration {
apConsRev(ap, c, ap0, config)
)
or
- exists(ParameterNode p |
- mid.getNode() = p and
- viableParamArg(_, p, node) and
+ exists(ParamNodeEx p |
+ mid.getNodeEx() = p and
+ viableParamArgEx(_, p, node) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
sc1 = TRevSummaryCtx1None() and
@@ -4050,7 +4363,7 @@ private module FlowExploration {
or
exists(ReturnPosition pos |
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
- pos = getReturnPosition(node)
+ pos = getReturnPosition(node.asNode())
)
or
revPartialPathThroughCallable(mid, node, ap, config) and
@@ -4060,12 +4373,13 @@ private module FlowExploration {
pragma[inline]
private predicate revPartialPathReadStep(
- PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
+ PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, NodeEx node,
+ RevPartialAccessPath ap2
) {
- exists(Node midNode |
- midNode = mid.getNode() and
+ exists(NodeEx midNode |
+ midNode = mid.getNodeEx() and
ap1 = mid.getAp() and
- read(node, c, midNode) and
+ read(node, c, midNode, mid.getConfiguration()) and
ap2.getHead() = c and
ap2.len() = unbindInt(ap1.len() + 1)
)
@@ -4083,12 +4397,12 @@ private module FlowExploration {
pragma[nomagic]
private predicate revPartialPathStoreStep(
- PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
+ PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, NodeEx node, Configuration config
) {
- exists(Node midNode, TypedContent tc |
- midNode = mid.getNode() and
+ exists(NodeEx midNode, TypedContent tc |
+ midNode = mid.getNodeEx() and
ap = mid.getAp() and
- store(node, tc, midNode, _) and
+ store(node, tc, midNode, _, config) and
ap.getHead() = c and
config = mid.getConfiguration() and
tc.getContent() = c
@@ -4100,9 +4414,9 @@ private module FlowExploration {
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
DataFlowCall call, RevPartialAccessPath ap, Configuration config
) {
- exists(Node out |
- mid.getNode() = out and
- viableReturnPosOut(call, pos, out) and
+ exists(NodeEx out |
+ mid.getNodeEx() = out and
+ viableReturnPosOutEx(call, pos, out) and
sc1 = TRevSummaryCtx1Some(pos) and
sc2 = TRevSummaryCtx2Some(ap) and
ap = mid.getAp() and
@@ -4115,9 +4429,9 @@ private module FlowExploration {
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
Configuration config
) {
- exists(PartialPathNodeRev mid, ParameterNode p |
- mid.getNode() = p and
- p.isParameterOf(_, pos) and
+ exists(PartialPathNodeRev mid, ParamNodeEx p |
+ mid.getNodeEx() = p and
+ p.getPosition() = pos and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
@@ -4138,11 +4452,11 @@ private module FlowExploration {
pragma[nomagic]
private predicate revPartialPathThroughCallable(
- PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
+ PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, int pos |
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
- node.argumentOf(call, pos)
+ node.asNode().(ArgNode).argumentOf(call, pos)
)
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
index 9498e51e7e61..df59c128ad39 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
@@ -81,6 +81,12 @@ abstract class Configuration extends string {
*/
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
+ /**
+ * Holds if an arbitrary number of implicit read steps of content `c` may be
+ * taken at `node`.
+ */
+ predicate allowImplicitRead(Node node, Content c) { none() }
+
/**
* Gets the virtual dispatch branching limit when calculating field flow.
* This can be overridden to a smaller value to improve performance (a
@@ -182,78 +188,210 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
}
}
-private predicate inBarrier(Node node, Configuration config) {
- config.isBarrierIn(node) and
- config.isSource(node)
+private newtype TNodeEx =
+ TNodeNormal(Node n) or
+ TNodeImplicitRead(Node n, boolean hasRead) {
+ any(Configuration c).allowImplicitRead(n, _) and hasRead = [false, true]
+ }
+
+private class NodeEx extends TNodeEx {
+ string toString() {
+ result = this.asNode().toString()
+ or
+ exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]")
+ }
+
+ Node asNode() { this = TNodeNormal(result) }
+
+ predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) }
+
+ Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) }
+
+ pragma[nomagic]
+ private DataFlowCallable getEnclosingCallable0() {
+ nodeEnclosingCallable(this.projectToNode(), result)
+ }
+
+ pragma[inline]
+ DataFlowCallable getEnclosingCallable() {
+ pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result)
+ }
+
+ pragma[nomagic]
+ private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) }
+
+ pragma[inline]
+ DataFlowType getDataFlowType() {
+ pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result)
+ }
+
+ predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ }
}
-private predicate outBarrier(Node node, Configuration config) {
- config.isBarrierOut(node) and
- config.isSink(node)
+private class ArgNodeEx extends NodeEx {
+ ArgNodeEx() { this.asNode() instanceof ArgNode }
}
-private predicate fullBarrier(Node node, Configuration config) {
- config.isBarrier(node)
- or
- config.isBarrierIn(node) and
- not config.isSource(node)
- or
- config.isBarrierOut(node) and
- not config.isSink(node)
- or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- node = g.getAGuardedNode()
+private class ParamNodeEx extends NodeEx {
+ ParamNodeEx() { this.asNode() instanceof ParamNode }
+
+ predicate isParameterOf(DataFlowCallable c, int i) {
+ this.asNode().(ParamNode).isParameterOf(c, i)
+ }
+
+ int getPosition() { this.isParameterOf(_, result) }
+}
+
+private class RetNodeEx extends NodeEx {
+ RetNodeEx() { this.asNode() instanceof ReturnNodeExt }
+
+ ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) }
+
+ ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() }
+}
+
+private predicate inBarrier(NodeEx node, Configuration config) {
+ exists(Node n |
+ node.asNode() = n and
+ config.isBarrierIn(n) and
+ config.isSource(n)
+ )
+}
+
+private predicate outBarrier(NodeEx node, Configuration config) {
+ exists(Node n |
+ node.asNode() = n and
+ config.isBarrierOut(n) and
+ config.isSink(n)
+ )
+}
+
+private predicate fullBarrier(NodeEx node, Configuration config) {
+ exists(Node n | node.asNode() = n |
+ config.isBarrier(n)
+ or
+ config.isBarrierIn(n) and
+ not config.isSource(n)
+ or
+ config.isBarrierOut(n) and
+ not config.isSink(n)
+ or
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
)
}
+pragma[nomagic]
+private predicate sourceNode(NodeEx node, Configuration config) { config.isSource(node.asNode()) }
+
+pragma[nomagic]
+private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) }
+
/**
* Holds if data can flow in one local step from `node1` to `node2`.
*/
-private predicate localFlowStep(Node node1, Node node2, Configuration config) {
- (
- simpleLocalFlowStep(node1, node2) or
- reverseStepThroughInputOutputAlias(node1, node2)
- ) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ simpleLocalFlowStepExt(n1, n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+ or
+ exists(Node n |
+ config.allowImplicitRead(n, _) and
+ node1.asNode() = n and
+ node2.isImplicitReadNode(n, false)
+ )
}
/**
* Holds if the additional step from `node1` to `node2` does not jump between callables.
*/
-private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
- config.isAdditionalFlowStep(node1, node2) and
- getNodeEnclosingCallable(node1) = getNodeEnclosingCallable(node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ config.isAdditionalFlowStep(n1, n2) and
+ getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+ or
+ exists(Node n |
+ config.allowImplicitRead(n, _) and
+ node1.isImplicitReadNode(n, true) and
+ node2.asNode() = n
+ )
}
/**
* Holds if data can flow from `node1` to `node2` in a way that discards call contexts.
*/
-private predicate jumpStep(Node node1, Node node2, Configuration config) {
- jumpStep(node1, node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ jumpStepCached(n1, n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
}
/**
* Holds if the additional step from `node1` to `node2` jumps between callables.
*/
-private predicate additionalJumpStep(Node node1, Node node2, Configuration config) {
- config.isAdditionalFlowStep(node1, node2) and
- getNodeEnclosingCallable(node1) != getNodeEnclosingCallable(node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ config.isAdditionalFlowStep(n1, n2) and
+ getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+}
+
+private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ read(node1.asNode(), c, node2.asNode())
+ or
+ exists(Node n |
+ node2.isImplicitReadNode(n, true) and
+ node1.isImplicitReadNode(n, _) and
+ config.allowImplicitRead(n, c)
+ )
+}
+
+private predicate store(
+ NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
+) {
+ store(node1.asNode(), tc, node2.asNode(), contentType) and
+ read(_, tc.getContent(), _, config)
+}
+
+pragma[nomagic]
+private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) {
+ viableReturnPosOut(call, pos, out.asNode())
+}
+
+pragma[nomagic]
+private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) {
+ viableParamArg(call, p.asNode(), arg.asNode())
}
/**
@@ -277,39 +415,39 @@ private module Stage1 {
* The Boolean `cc` records whether the node is reached through an
* argument in a call.
*/
- predicate fwdFlow(Node node, Cc cc, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
not fullBarrier(node, config) and
(
- config.isSource(node) and
+ sourceNode(node, config) and
cc = false
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
localFlowStep(mid, node, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
additionalLocalFlowStep(mid, node, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, config) and
jumpStep(mid, node, config) and
cc = false
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, config) and
additionalJumpStep(mid, node, config) and
cc = false
)
or
// store
- exists(Node mid |
+ exists(NodeEx mid |
useFieldFlow(config) and
fwdFlow(mid, cc, config) and
- store(mid, _, node, _) and
+ store(mid, _, node, _, config) and
not outBarrier(mid, config)
)
or
@@ -321,9 +459,9 @@ private module Stage1 {
)
or
// flow into a callable
- exists(Node arg |
+ exists(NodeEx arg |
fwdFlow(arg, _, config) and
- viableParamArg(_, node, arg) and
+ viableParamArgEx(_, node, arg) and
cc = true
)
or
@@ -338,13 +476,13 @@ private module Stage1 {
)
}
- private predicate fwdFlow(Node node, Configuration config) { fwdFlow(node, _, config) }
+ private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
pragma[nomagic]
- private predicate fwdFlowRead(Content c, Node node, Cc cc, Configuration config) {
- exists(Node mid |
+ private predicate fwdFlowRead(Content c, NodeEx node, Cc cc, Configuration config) {
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
- read(mid, c, node)
+ read(mid, c, node, config)
)
}
@@ -353,33 +491,33 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate fwdFlowConsCand(Content c, Configuration config) {
- exists(Node mid, Node node, TypedContent tc |
+ exists(NodeEx mid, NodeEx node, TypedContent tc |
not fullBarrier(node, config) and
useFieldFlow(config) and
fwdFlow(mid, _, config) and
- store(mid, tc, node, _) and
+ store(mid, tc, node, _, config) and
c = tc.getContent()
)
}
pragma[nomagic]
private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc, Configuration config) {
- exists(ReturnNodeExt ret |
+ exists(RetNodeEx ret |
fwdFlow(ret, cc, config) and
- getReturnPosition(ret) = pos
+ ret.getReturnPosition() = pos
)
}
pragma[nomagic]
- private predicate fwdFlowOut(DataFlowCall call, Node out, Cc cc, Configuration config) {
+ private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
exists(ReturnPosition pos |
fwdFlowReturnPosition(pos, cc, config) and
- viableReturnPosOut(call, pos, out)
+ viableReturnPosOutEx(call, pos, out)
)
}
pragma[nomagic]
- private predicate fwdFlowOutFromArg(DataFlowCall call, Node out, Configuration config) {
+ private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out, Configuration config) {
fwdFlowOut(call, out, true, config)
}
@@ -388,9 +526,9 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
- exists(ArgumentNode arg |
+ exists(ArgNodeEx arg |
fwdFlow(arg, cc, config) and
- viableParamArg(call, _, arg)
+ viableParamArgEx(call, _, arg)
)
}
@@ -402,34 +540,34 @@ private module Stage1 {
* the enclosing callable in order to reach a sink.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and
fwdFlow(node, config)
}
pragma[nomagic]
- private predicate revFlow0(Node node, boolean toReturn, Configuration config) {
+ private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStep(node, mid, config) and
revFlow(mid, toReturn, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
additionalLocalFlowStep(node, mid, config) and
revFlow(mid, toReturn, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, config) and
toReturn = false
)
or
- exists(Node mid |
+ exists(NodeEx mid |
additionalJumpStep(node, mid, config) and
revFlow(mid, _, config) and
toReturn = false
@@ -442,8 +580,8 @@ private module Stage1 {
)
or
// read
- exists(Node mid, Content c |
- read(node, c, mid) and
+ exists(NodeEx mid, Content c |
+ read(node, c, mid, config) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
revFlow(mid, toReturn, pragma[only_bind_into](config))
)
@@ -460,7 +598,7 @@ private module Stage1 {
// flow out of a callable
exists(ReturnPosition pos |
revFlowOut(pos, config) and
- getReturnPosition(node) = pos and
+ node.(RetNodeEx).getReturnPosition() = pos and
toReturn = true
)
}
@@ -470,20 +608,20 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Content c, Configuration config) {
- exists(Node mid, Node node |
+ exists(NodeEx mid, NodeEx node |
fwdFlow(node, pragma[only_bind_into](config)) and
- read(node, c, mid) and
+ read(node, c, mid, config) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
revFlow(pragma[only_bind_into](mid), _, pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate revFlowStore(Content c, Node node, boolean toReturn, Configuration config) {
- exists(Node mid, TypedContent tc |
+ private predicate revFlowStore(Content c, NodeEx node, boolean toReturn, Configuration config) {
+ exists(NodeEx mid, TypedContent tc |
revFlow(mid, toReturn, pragma[only_bind_into](config)) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
- store(node, tc, mid, _) and
+ store(node, tc, mid, _, config) and
c = tc.getContent()
)
}
@@ -499,15 +637,15 @@ private module Stage1 {
pragma[nomagic]
predicate viableReturnPosOutNodeCandFwd1(
- DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+ DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
fwdFlowReturnPosition(pos, _, config) and
- viableReturnPosOut(call, pos, out)
+ viableReturnPosOutEx(call, pos, out)
}
pragma[nomagic]
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
- exists(DataFlowCall call, Node out |
+ exists(DataFlowCall call, NodeEx out |
revFlow(out, _, config) and
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
)
@@ -515,33 +653,35 @@ private module Stage1 {
pragma[nomagic]
predicate viableParamArgNodeCandFwd1(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+ DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
- viableParamArg(call, p, arg) and
+ viableParamArgEx(call, p, arg) and
fwdFlow(arg, config)
}
pragma[nomagic]
private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config
+ DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
revFlow(p, toReturn, config) and
viableParamArgNodeCandFwd1(call, p, arg, config)
)
}
pragma[nomagic]
- private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
+ private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg, Configuration config) {
revFlowIn(call, arg, true, config)
}
/**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`.
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
*/
pragma[nomagic]
private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) {
- exists(Node out |
+ exists(NodeEx out |
revFlow(out, toReturn, config) and
fwdFlowOutFromArg(call, out, config)
)
@@ -549,32 +689,33 @@ private module Stage1 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Content c |
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
revFlow(node2, pragma[only_bind_into](config)) and
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
c = tc.getContent() and
exists(ap1)
)
}
pragma[nomagic]
- predicate readStepCand(Node n1, Content c, Node n2, Configuration config) {
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) and
- read(n1, c, n2)
+ read(n1, c, n2, pragma[only_bind_into](config))
}
pragma[nomagic]
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow(node, toReturn, config) and exists(returnAp) and exists(ap)
}
- private predicate throughFlowNodeCand(Node node, Configuration config) {
+ private predicate throughFlowNodeCand(NodeEx node, Configuration config) {
revFlow(node, true, config) and
fwdFlow(node, true, config) and
not inBarrier(node, config) and
@@ -586,9 +727,9 @@ private module Stage1 {
private predicate returnFlowCallableNodeCand(
DataFlowCallable callable, ReturnKindExt kind, Configuration config
) {
- exists(ReturnNodeExt ret |
+ exists(RetNodeEx ret |
throughFlowNodeCand(ret, config) and
- callable = getNodeEnclosingCallable(ret) and
+ callable = ret.getEnclosingCallable() and
kind = ret.getKind()
)
}
@@ -597,50 +738,57 @@ private module Stage1 {
* Holds if flow may enter through `p` and reach a return node making `p` a
* candidate for the origin of a summary.
*/
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
- getNodeEnclosingCallable(p) = c and
+ p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself
- not exists(int pos |
- kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos)
- )
+ not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
+ )
+ }
+
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(ArgNodeEx arg, boolean toReturn |
+ revFlow(arg, toReturn, config) and
+ revFlowInToReturn(call, arg, config) and
+ revFlowIsReturned(call, toReturn, config)
)
}
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, config)) and
fields = count(Content f0 | fwdFlowConsCand(f0, config)) and
conscand = -1 and
- tuples = count(Node n, boolean b | fwdFlow(n, b, config))
+ tuples = count(NodeEx n, boolean b | fwdFlow(n, b, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, config)) and
fields = count(Content f0 | revFlowConsCand(f0, config)) and
conscand = -1 and
- tuples = count(Node n, boolean b | revFlow(n, b, config))
+ tuples = count(NodeEx n, boolean b | revFlow(n, b, config))
}
/* End: Stage 1 logic. */
}
pragma[noinline]
-private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) {
Stage1::revFlow(node2, config) and
localFlowStep(node1, node2, config)
}
pragma[noinline]
-private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) {
Stage1::revFlow(node2, config) and
additionalLocalFlowStep(node1, node2, config)
}
pragma[nomagic]
private predicate viableReturnPosOutNodeCand1(
- DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+ DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
Stage1::revFlow(out, config) and
Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out, config)
@@ -653,9 +801,9 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, ReturnNodeExt ret, Node out, Configuration config
+ DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
) {
- viableReturnPosOutNodeCand1(call, getReturnPosition(ret), out, config) and
+ viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
@@ -663,7 +811,7 @@ private predicate flowOutOfCallNodeCand1(
pragma[nomagic]
private predicate viableParamArgNodeCand1(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+ DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and
Stage1::revFlow(arg, config)
@@ -675,7 +823,7 @@ private predicate viableParamArgNodeCand1(
*/
pragma[nomagic]
private predicate flowIntoCallNodeCand1(
- DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, Configuration config
) {
viableParamArgNodeCand1(call, p, arg, config) and
Stage1::revFlow(p, config) and
@@ -688,9 +836,9 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
-private int branch(Node n1, Configuration conf) {
+private int branch(NodeEx n1, Configuration conf) {
result =
- strictcount(Node n |
+ strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -700,9 +848,9 @@ private int branch(Node n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
-private int join(Node n2, Configuration conf) {
+private int join(NodeEx n2, Configuration conf) {
result =
- strictcount(Node n |
+ strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -716,7 +864,7 @@ private int join(Node n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
exists(int b, int j |
@@ -735,8 +883,7 @@ private predicate flowOutOfCallNodeCand1(
*/
pragma[nomagic]
private predicate flowIntoCallNodeCand1(
- DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
exists(int b, int j |
@@ -762,7 +909,7 @@ private module Stage2 {
bindingset[result, ap]
private ApApprox getApprox(Ap ap) { any() }
- private ApNil getApNil(Node node) { PrevStage::revFlow(node, _) and exists(result) }
+ private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
@@ -805,10 +952,10 @@ private module Stage2 {
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { any() }
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
(
preservesValue = true and
@@ -829,10 +976,20 @@ private module Stage2 {
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
/* Begin: Stage 2 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -842,14 +999,14 @@ private module Stage2 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -860,7 +1017,7 @@ private module Stage2 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -868,7 +1025,7 @@ private module Stage2 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -899,19 +1056,17 @@ private module Stage2 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -935,7 +1090,7 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -944,32 +1099,29 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -979,9 +1131,15 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -992,7 +1150,7 @@ private module Stage2 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
)
@@ -1000,18 +1158,41 @@ private module Stage2 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1021,41 +1202,41 @@ private module Stage2 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -1071,20 +1252,18 @@ private module Stage2 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -1097,7 +1276,7 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -1111,7 +1290,7 @@ private module Stage2 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -1120,10 +1299,10 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -1132,13 +1311,12 @@ private module Stage2 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1146,9 +1324,14 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1160,7 +1343,7 @@ private module Stage2 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -1169,16 +1352,17 @@ private module Stage2 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -1187,7 +1371,7 @@ private module Stage2 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -1199,44 +1383,54 @@ private module Stage2 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 2 logic. */
}
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
- DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
@@ -1245,7 +1439,7 @@ private predicate flowOutOfCallNodeCand2(
pragma[nomagic]
private predicate flowIntoCallNodeCand2(
- DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
@@ -1258,10 +1452,10 @@ private module LocalFlowBigStep {
* A node where some checking is required, and hence the big-step relation
* is not allowed to step over.
*/
- private class FlowCheckNode extends Node {
+ private class FlowCheckNode extends NodeEx {
FlowCheckNode() {
- this instanceof CastNode or
- clearsContent(this, _)
+ castNode(this.asNode()) or
+ clearsContentCached(this.asNode(), _)
}
}
@@ -1269,16 +1463,16 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- predicate localFlowEntry(Node node, Configuration config) {
+ predicate localFlowEntry(NodeEx node, Configuration config) {
Stage2::revFlow(node, config) and
(
- config.isSource(node) or
+ sourceNode(node, config) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
- node instanceof ParameterNode or
- node instanceof OutNodeExt or
- store(_, _, node, _) or
- read(_, _, node) or
+ node instanceof ParamNodeEx or
+ node.asNode() instanceof OutNodeExt or
+ store(_, _, node, _, config) or
+ read(_, _, node, config) or
node instanceof FlowCheckNode
)
}
@@ -1287,23 +1481,25 @@ private module LocalFlowBigStep {
* Holds if `node` can be the last node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- private predicate localFlowExit(Node node, Configuration config) {
- exists(Node next | Stage2::revFlow(next, config) |
+ private predicate localFlowExit(NodeEx node, Configuration config) {
+ exists(NodeEx next | Stage2::revFlow(next, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
- store(node, _, next, _) or
- read(node, _, next)
+ store(node, _, next, _, config) or
+ read(node, _, next, config)
)
or
node instanceof FlowCheckNode
or
- config.isSink(node)
+ sinkNode(node, config)
}
pragma[noinline]
- private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
+ private predicate additionalLocalFlowStepNodeCand2(
+ NodeEx node1, NodeEx node2, Configuration config
+ ) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and
Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config))
@@ -1318,39 +1514,39 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
- Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config,
+ NodeEx node1, NodeEx node2, boolean preservesValue, DataFlowType t, Configuration config,
LocalCallContext cc
) {
- not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
+ not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
(
localFlowEntry(node1, pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
- t = getNodeType(node1)
+ t = node1.getDataFlowType() // irrelevant dummy value
or
additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
- t = getNodeType(node2)
+ t = node2.getDataFlowType()
) and
node1 != node2 and
- cc.relevantFor(getNodeEnclosingCallable(node1)) and
- not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
+ cc.relevantFor(node1.getEnclosingCallable()) and
+ not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
Stage2::revFlow(node2, pragma[only_bind_into](config))
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStepPlus(node1, mid, preservesValue, t, pragma[only_bind_into](config), cc) and
localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof FlowCheckNode and
Stage2::revFlow(node2, pragma[only_bind_into](config))
)
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStepPlus(node1, mid, _, _, pragma[only_bind_into](config), cc) and
additionalLocalFlowStepNodeCand2(mid, node2, config) and
not mid instanceof FlowCheckNode and
preservesValue = false and
- t = getNodeType(node2) and
+ t = node2.getDataFlowType() and
Stage2::revFlow(node2, pragma[only_bind_into](config))
)
)
@@ -1362,8 +1558,8 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, AccessPathFrontNil apf, Configuration config,
- LocalCallContext callContext
+ NodeEx node1, NodeEx node2, boolean preservesValue, AccessPathFrontNil apf,
+ Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, config)
@@ -1383,8 +1579,8 @@ private module Stage3 {
private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(Node node) {
- PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node))
+ private ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
@@ -1428,10 +1624,10 @@ private module Stage3 {
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { any() }
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, node2, preservesValue, ap, config, _) and exists(lcc)
}
@@ -1440,10 +1636,16 @@ private module Stage3 {
private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ pragma[nomagic]
+ private predicate clear(NodeEx node, Ap ap) { ap.isClearedAt(node.asNode()) }
+
+ pragma[nomagic]
+ private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
+
bindingset[node, ap]
- private predicate filter(Node node, Ap ap) {
- not ap.isClearedAt(node) and
- if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any()
+ private predicate filter(NodeEx node, Ap ap) {
+ not clear(node, ap) and
+ if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()
}
bindingset[ap, contentType]
@@ -1454,7 +1656,7 @@ private module Stage3 {
}
/* Begin: Stage 3 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
@@ -1465,6 +1667,16 @@ private module Stage3 {
)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -1474,21 +1686,21 @@ private module Stage3 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
pragma[nomagic]
- private predicate fwdFlow0(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -1499,7 +1711,7 @@ private module Stage3 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -1507,7 +1719,7 @@ private module Stage3 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -1538,19 +1750,17 @@ private module Stage3 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -1574,7 +1784,7 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -1583,32 +1793,29 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1618,9 +1825,15 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1631,7 +1844,7 @@ private module Stage3 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
@@ -1639,18 +1852,41 @@ private module Stage3 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1660,41 +1896,41 @@ private module Stage3 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -1710,20 +1946,18 @@ private module Stage3 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -1736,7 +1970,7 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -1750,7 +1984,7 @@ private module Stage3 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -1759,10 +1993,10 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -1771,13 +2005,12 @@ private module Stage3 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1785,9 +2018,14 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1799,7 +2037,7 @@ private module Stage3 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -1808,16 +2046,17 @@ private module Stage3 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -1826,7 +2065,7 @@ private module Stage3 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -1838,37 +2077,47 @@ private module Stage3 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 3 logic. */
}
@@ -1877,7 +2126,7 @@ private module Stage3 {
* Holds if `argApf` is recorded as the summary context for flow reaching `node`
* and remains relevant for the following pruning stage.
*/
-private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) {
+private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
@@ -1892,7 +2141,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
exists(int tails, int nodes, int apLimit, int tupleLimit |
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
- strictcount(Node n |
+ strictcount(NodeEx n |
Stage3::revFlow(n, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
@@ -2087,8 +2336,8 @@ private module Stage4 {
private ApApprox getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(Node node) {
- PrevStage::revFlow(node, _) and result = TNil(getNodeType(node))
+ private ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
@@ -2127,26 +2376,25 @@ private module Stage4 {
bindingset[innercc, inner, call]
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
resolveReturn(innercc, inner, call)
- or
- innercc.(CallContextCall).matchesCall(call)
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
localFlowEntry(node, config) and
- result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node))
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
}
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, node2, preservesValue, ap.getFront(), config, lcc)
}
pragma[nomagic]
private predicate flowOutOfCall(
- DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
@@ -2155,7 +2403,7 @@ private module Stage4 {
pragma[nomagic]
private predicate flowIntoCall(
- DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
@@ -2164,14 +2412,14 @@ private module Stage4 {
}
bindingset[node, ap]
- private predicate filter(Node node, Ap ap) { any() }
+ private predicate filter(NodeEx node, Ap ap) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
/* Begin: Stage 4 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
@@ -2182,6 +2430,16 @@ private module Stage4 {
)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -2191,21 +2449,21 @@ private module Stage4 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
pragma[nomagic]
- private predicate fwdFlow0(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -2216,7 +2474,7 @@ private module Stage4 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -2224,7 +2482,7 @@ private module Stage4 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -2255,19 +2513,17 @@ private module Stage4 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -2291,7 +2547,7 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -2300,32 +2556,29 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -2335,9 +2588,15 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -2348,7 +2607,7 @@ private module Stage4 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
@@ -2356,18 +2615,41 @@ private module Stage4 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -2377,41 +2659,41 @@ private module Stage4 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -2427,20 +2709,18 @@ private module Stage4 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -2453,7 +2733,7 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -2467,7 +2747,7 @@ private module Stage4 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -2476,10 +2756,10 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -2488,13 +2768,12 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -2502,9 +2781,14 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -2516,7 +2800,7 @@ private module Stage4 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -2525,16 +2809,17 @@ private module Stage4 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -2543,7 +2828,7 @@ private module Stage4 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -2555,37 +2840,47 @@ private module Stage4 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 4 logic. */
}
@@ -2595,18 +2890,18 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
}
-private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) {
+private predicate nodeMayUseSummary(NodeEx n, AccessPathApprox apa, Configuration config) {
exists(DataFlowCallable c, AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and
Stage4::revFlow(n, true, _, apa0, config) and
Stage4::fwdFlow(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
- getNodeEnclosingCallable(n) = c
+ n.getEnclosingCallable() = c
)
}
private newtype TSummaryCtx =
TSummaryCtxNone() or
- TSummaryCtxSome(ParameterNode p, AccessPath ap) {
+ TSummaryCtxSome(ParamNodeEx p, AccessPath ap) {
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _)
}
@@ -2627,7 +2922,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone {
/** A summary context from which a flow summary can be generated. */
private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
- private ParameterNode p;
+ private ParamNodeEx p;
private AccessPath ap;
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
@@ -2660,7 +2955,9 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
- strictcount(Node n | Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config))
+ strictcount(NodeEx n |
+ Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)
+ )
}
/**
@@ -2752,13 +3049,13 @@ private newtype TAccessPath =
}
private newtype TPathNode =
- TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
+ TPathNodeMid(NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
- ap = TAccessPathNil(getNodeType(node))
+ ap = TAccessPathNil(node.getDataFlowType())
or
// ... or a step from an existing PathNode to another node.
exists(PathNodeMid mid |
@@ -2767,12 +3064,12 @@ private newtype TPathNode =
Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config))
)
} or
- TPathNodeSink(Node node, Configuration config) {
- pragma[only_bind_into](config).isSink(node) and
+ TPathNodeSink(NodeEx node, Configuration config) {
+ sinkNode(node, pragma[only_bind_into](config)) and
Stage4::revFlow(node, pragma[only_bind_into](config)) and
(
// A sink that is also a source ...
- config.isSource(node)
+ sourceNode(node, config)
or
// ... or a sink that can be reached from a source
exists(PathNodeMid mid |
@@ -2973,15 +3270,17 @@ class PathNode extends TPathNode {
}
/** Gets the underlying `Node`. */
- Node getNode() { none() }
+ final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
private predicate isHidden() {
- nodeIsHidden(this.getNode()) and
+ hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
+ or
+ this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
}
private PathNode getASuccessorIfHidden() {
@@ -3003,6 +3302,8 @@ class PathNode extends TPathNode {
abstract private class PathNodeImpl extends PathNode {
abstract PathNode getASuccessorImpl();
+ abstract NodeEx getNodeEx();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3017,14 +3318,14 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
}
- override string toString() { result = this.getNode().toString() + ppAp() }
+ override string toString() { result = this.getNodeEx().toString() + ppAp() }
- override string toStringWithContext() { result = this.getNode().toString() + ppAp() + ppCtx() }
+ override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
@@ -3054,7 +3355,7 @@ module PathGraph {
* a `CallContext`, and a `Configuration`.
*/
private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
- Node node;
+ NodeEx node;
CallContext cc;
SummaryCtx sc;
AccessPath ap;
@@ -3062,7 +3363,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) }
- override Node getNode() { result = node }
+ override NodeEx getNodeEx() { result = node }
CallContext getCallContext() { result = cc }
@@ -3073,7 +3374,8 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
override Configuration getConfiguration() { result = config }
private PathNodeMid getSuccMid() {
- pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and
+ pathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx(),
+ result.getAp()) and
result.getConfiguration() = unbindConf(this.getConfiguration())
}
@@ -3084,7 +3386,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid, PathNodeSink sink |
mid = getSuccMid() and
- mid.getNode() = sink.getNode() and
+ mid.getNodeEx() = sink.getNodeEx() and
mid.getAp() instanceof AccessPathNil and
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
result = sink
@@ -3092,7 +3394,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
}
override predicate isSource() {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
@@ -3105,31 +3407,35 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
* excluding the `CallContext`.
*/
private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
- Node node;
+ NodeEx node;
Configuration config;
PathNodeSink() { this = TPathNodeSink(node, config) }
- override Node getNode() { result = node }
+ override NodeEx getNodeEx() { result = node }
override Configuration getConfiguration() { result = config }
override PathNode getASuccessorImpl() { none() }
- override predicate isSource() { config.isSource(node) }
+ override predicate isSource() { sourceNode(node, config) }
}
/**
* Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`.
*/
-private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
- exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
- midnode = mid.getNode() and
+private predicate pathStep(
+ PathNodeMid mid, NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap
+) {
+ exists(AccessPath ap0, NodeEx midnode, Configuration conf, LocalCallContext localCC |
+ midnode = mid.getNodeEx() and
conf = mid.getConfiguration() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
- localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and
+ localCC =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ midnode.getEnclosingCallable()) and
ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
@@ -3139,16 +3445,16 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
ap0 instanceof AccessPathNil
)
or
- jumpStep(mid.getNode(), node, mid.getConfiguration()) and
+ jumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
ap = mid.getAp()
or
- additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and
+ additionalJumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
mid.getAp() instanceof AccessPathNil and
- ap = TAccessPathNil(getNodeType(node))
+ ap = TAccessPathNil(node.getDataFlowType())
or
exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and
sc = mid.getSummaryCtx()
@@ -3165,20 +3471,20 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
pragma[nomagic]
private predicate pathReadStep(
- PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+ PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
- Stage4::readStepCand(mid.getNode(), tc.getContent(), node, mid.getConfiguration()) and
+ Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+ PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- Stage4::storeStepCand(mid.getNode(), _, tc, node, _, mid.getConfiguration()) and
+ Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
cc = mid.getCallContext()
}
@@ -3186,7 +3492,7 @@ private predicate pathOutOfCallable0(
PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa,
Configuration config
) {
- pos = getReturnPosition(mid.getNode()) and
+ pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and
innercc = mid.getCallContext() and
innercc instanceof CallContextNoCall and
apa = mid.getAp().getApprox() and
@@ -3209,10 +3515,10 @@ private predicate pathOutOfCallable1(
}
pragma[noinline]
-private Node getAnOutNodeFlow(
+private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
- result = kind.getAnOutNode(call) and
+ result.asNode() = kind.getAnOutNode(call) and
Stage4::revFlow(result, _, _, apa, config)
}
@@ -3221,7 +3527,7 @@ private Node getAnOutNodeFlow(
* is a return from a callable and is recorded by `cc`, if needed.
*/
pragma[noinline]
-private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
+private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) {
exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config |
pathOutOfCallable1(mid, call, kind, cc, apa, config) and
out = getAnOutNodeFlow(kind, call, apa, config)
@@ -3235,8 +3541,8 @@ pragma[noinline]
private predicate pathIntoArg(
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa
) {
- exists(ArgumentNode arg |
- arg = mid.getNode() and
+ exists(ArgNode arg |
+ arg = mid.getNodeEx().asNode() and
cc = mid.getCallContext() and
arg.argumentOf(call, i) and
ap = mid.getAp() and
@@ -3248,7 +3554,7 @@ pragma[noinline]
private predicate parameterCand(
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
Stage4::revFlow(p, _, _, apa, config) and
p.isParameterOf(callable, i)
)
@@ -3272,7 +3578,7 @@ private predicate pathIntoCallable0(
* respectively.
*/
private predicate pathIntoCallable(
- PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
+ PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
DataFlowCall call
) {
exists(int i, DataFlowCallable callable, AccessPath ap |
@@ -3297,8 +3603,8 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa,
Configuration config
) {
- exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
- mid.getNode() = ret and
+ exists(PathNodeMid mid, RetNodeEx ret, int pos |
+ mid.getNodeEx() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
@@ -3326,7 +3632,7 @@ private predicate pathThroughCallable0(
* The context `cc` is restored to its value prior to entering the callable.
*/
pragma[noinline]
-private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
+private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) {
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
pathThroughCallable0(call, mid, kind, cc, ap, apa) and
out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
@@ -3344,9 +3650,9 @@ private predicate flowsTo(
) {
flowsource.isSource() and
flowsource.getConfiguration() = configuration and
- flowsource.getNode() = source and
+ flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
- flowsink.getNode() = sink
+ flowsink.getNodeEx().asNode() = sink
}
/**
@@ -3361,13 +3667,13 @@ predicate flowsTo(Node source, Node sink, Configuration configuration) {
private predicate finalStats(boolean fwd, int nodes, int fields, int conscand, int tuples) {
fwd = true and
- nodes = count(Node n0 | exists(PathNode pn | pn.getNode() = n0)) and
+ nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
tuples = count(PathNode pn)
or
fwd = false and
- nodes = count(Node n0 | exists(PathNode pn | pn.getNode() = n0 and reach(pn))) and
+ nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
tuples = count(PathNode pn | reach(pn))
@@ -3404,19 +3710,19 @@ predicate stageStats(
private module FlowExploration {
private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2, Configuration config) {
- exists(Node node1, Node node2 |
+ exists(NodeEx node1, NodeEx node2 |
jumpStep(node1, node2, config)
or
additionalJumpStep(node1, node2, config)
or
// flow into callable
- viableParamArg(_, node2, node1)
+ viableParamArgEx(_, node2, node1)
or
// flow out of a callable
- viableReturnPosOut(_, getReturnPosition(node1), node2)
+ viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2)
|
- c1 = getNodeEnclosingCallable(node1) and
- c2 = getNodeEnclosingCallable(node2) and
+ c1 = node1.getEnclosingCallable() and
+ c2 = node2.getEnclosingCallable() and
c1 != c2
)
}
@@ -3568,7 +3874,7 @@ private module FlowExploration {
private newtype TSummaryCtx1 =
TSummaryCtx1None() or
- TSummaryCtx1Param(ParameterNode p)
+ TSummaryCtx1Param(ParamNodeEx p)
private newtype TSummaryCtx2 =
TSummaryCtx2None() or
@@ -3584,25 +3890,25 @@ private module FlowExploration {
private newtype TPartialPathNode =
TPartialPathNodeFwd(
- Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
+ NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
- distSrc(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
+ distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
- Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
+ NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
Configuration config
) {
- config.isSink(node) and
+ sinkNode(node, config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil() and
@@ -3611,23 +3917,23 @@ private module FlowExploration {
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
- not clearsContent(node, ap.getHead()) and
+ not clearsContentCached(node.asNode(), ap.getHead()) and
not fullBarrier(node, config) and
- distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
+ distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
}
pragma[nomagic]
private predicate partialPathNodeMk0(
- Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
+ NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
- not clearsContent(node, ap.getHead().getContent()) and
- if node instanceof CastingNode
- then compatibleTypes(getNodeType(node), ap.getType())
+ not clearsContentCached(node.asNode(), ap.getHead().getContent()) and
+ if node.asNode() instanceof CastingNode
+ then compatibleTypes(node.getDataFlowType(), ap.getType())
else any()
)
}
@@ -3637,13 +3943,15 @@ private module FlowExploration {
*/
class PartialPathNode extends TPartialPathNode {
/** Gets a textual representation of this element. */
- string toString() { result = this.getNode().toString() + this.ppAp() }
+ string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/**
* Gets a textual representation of this element, including a textual
* representation of the call context.
*/
- string toStringWithContext() { result = this.getNode().toString() + this.ppAp() + this.ppCtx() }
+ string toStringWithContext() {
+ result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
+ }
/**
* Holds if this element is at the specified location.
@@ -3655,11 +3963,16 @@ private module FlowExploration {
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the underlying `Node`. */
- Node getNode() { none() }
+ final Node getNode() { this.getNodeEx().projectToNode() = result }
+
+ private NodeEx getNodeEx() {
+ result = this.(PartialPathNodeFwd).getNodeEx() or
+ result = this.(PartialPathNodeRev).getNodeEx()
+ }
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
@@ -3672,7 +3985,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSourceDistance() {
- result = distSrc(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
+ result = distSrc(this.getNodeEx().getEnclosingCallable(), this.getConfiguration())
}
/**
@@ -3680,7 +3993,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSinkDistance() {
- result = distSink(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
+ result = distSink(this.getNodeEx().getEnclosingCallable(), this.getConfiguration())
}
private string ppAp() {
@@ -3712,7 +4025,7 @@ private module FlowExploration {
}
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
- Node node;
+ NodeEx node;
CallContext cc;
TSummaryCtx1 sc1;
TSummaryCtx2 sc2;
@@ -3721,7 +4034,7 @@ private module FlowExploration {
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
- override Node getNode() { result = node }
+ NodeEx getNodeEx() { result = node }
CallContext getCallContext() { result = cc }
@@ -3734,12 +4047,12 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodeFwd getASuccessor() {
- partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
+ partialPathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx1(),
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
}
predicate isSource() {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
@@ -3748,7 +4061,7 @@ private module FlowExploration {
}
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
- Node node;
+ NodeEx node;
TRevSummaryCtx1 sc1;
TRevSummaryCtx2 sc2;
RevPartialAccessPath ap;
@@ -3756,7 +4069,7 @@ private module FlowExploration {
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
- override Node getNode() { result = node }
+ NodeEx getNodeEx() { result = node }
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
@@ -3767,12 +4080,12 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodeRev getASuccessor() {
- revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
+ revPartialPathStep(result, this.getNodeEx(), this.getSummaryCtx1(), this.getSummaryCtx2(),
this.getAp(), this.getConfiguration())
}
predicate isSink() {
- config.isSink(node) and
+ sinkNode(node, config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil()
@@ -3780,40 +4093,40 @@ private module FlowExploration {
}
private predicate partialPathStep(
- PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
+ PartialPathNodeFwd mid, NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
- not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
+ not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and
(
- localFlowStep(mid.getNode(), node, config) and
+ localFlowStep(mid.getNodeEx(), node, config) and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalLocalFlowStep(mid.getNode(), node, config) and
+ additionalLocalFlowStep(mid.getNodeEx(), node, config) and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
config = mid.getConfiguration()
)
or
- jumpStep(mid.getNode(), node, config) and
+ jumpStep(mid.getNodeEx(), node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalJumpStep(mid.getNode(), node, config) and
+ additionalJumpStep(mid.getNodeEx(), node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
config = mid.getConfiguration()
or
partialPathStoreStep(mid, _, _, node, ap) and
@@ -3826,8 +4139,7 @@ private module FlowExploration {
partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, tc, ap0, config) and
- compatibleTypes(ap.getType(), getNodeType(node))
+ apConsFwd(ap, tc, ap0, config)
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -3846,12 +4158,13 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
+ PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, NodeEx node,
+ PartialAccessPath ap2
) {
- exists(Node midNode, DataFlowType contentType |
- midNode = mid.getNode() and
+ exists(NodeEx midNode, DataFlowType contentType |
+ midNode = mid.getNodeEx() and
ap1 = mid.getAp() and
- store(midNode, tc, node, contentType) and
+ store(midNode, tc, node, contentType, mid.getConfiguration()) and
ap2.getHead() = tc and
ap2.len() = unbindInt(ap1.len() + 1) and
compatibleTypes(ap1.getType(), contentType)
@@ -3870,15 +4183,15 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
+ PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, NodeEx node, CallContext cc,
Configuration config
) {
- exists(Node midNode |
- midNode = mid.getNode() and
+ exists(NodeEx midNode |
+ midNode = mid.getNodeEx() and
ap = mid.getAp() and
- read(midNode, tc.getContent(), node) and
+ read(midNode, tc.getContent(), node, pragma[only_bind_into](config)) and
ap.getHead() = tc and
- config = mid.getConfiguration() and
+ pragma[only_bind_into](config) = mid.getConfiguration() and
cc = mid.getCallContext()
)
}
@@ -3887,7 +4200,7 @@ private module FlowExploration {
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
Configuration config
) {
- pos = getReturnPosition(mid.getNode()) and
+ pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and
innercc = mid.getCallContext() and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
@@ -3910,12 +4223,12 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
- PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = kind.getAnOutNode(call)
+ out.asNode() = kind.getAnOutNode(call)
)
}
@@ -3924,8 +4237,8 @@ private module FlowExploration {
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
- exists(ArgumentNode arg |
- arg = mid.getNode() and
+ exists(ArgNode arg |
+ arg = mid.getNodeEx().asNode() and
cc = mid.getCallContext() and
arg.argumentOf(call, i) and
ap = mid.getAp() and
@@ -3943,7 +4256,7 @@ private module FlowExploration {
}
private predicate partialPathIntoCallable(
- PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
+ PartialPathNodeFwd mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc,
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
@@ -3964,8 +4277,8 @@ private module FlowExploration {
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
- exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
- mid.getNode() = ret and
+ exists(PartialPathNodeFwd mid, RetNodeEx ret |
+ mid.getNodeEx() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
@@ -3980,45 +4293,45 @@ private module FlowExploration {
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
- exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
- partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and
+ exists(CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
+ partialPathIntoCallable(mid, _, cc, innercc, sc1, sc2, call, _, config) and
paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config)
)
}
private predicate partialPathThroughCallable(
- PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
- out = kind.getAnOutNode(call)
+ out.asNode() = kind.getAnOutNode(call)
)
}
private predicate revPartialPathStep(
- PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
+ PartialPathNodeRev mid, NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
RevPartialAccessPath ap, Configuration config
) {
- localFlowStep(node, mid.getNode(), config) and
+ localFlowStep(node, mid.getNodeEx(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalLocalFlowStep(node, mid.getNode(), config) and
+ additionalLocalFlowStep(node, mid.getNodeEx(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
- jumpStep(node, mid.getNode(), config) and
+ jumpStep(node, mid.getNodeEx(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalJumpStep(node, mid.getNode(), config) and
+ additionalJumpStep(node, mid.getNodeEx(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
mid.getAp() instanceof RevPartialAccessPathNil and
@@ -4037,9 +4350,9 @@ private module FlowExploration {
apConsRev(ap, c, ap0, config)
)
or
- exists(ParameterNode p |
- mid.getNode() = p and
- viableParamArg(_, p, node) and
+ exists(ParamNodeEx p |
+ mid.getNodeEx() = p and
+ viableParamArgEx(_, p, node) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
sc1 = TRevSummaryCtx1None() and
@@ -4050,7 +4363,7 @@ private module FlowExploration {
or
exists(ReturnPosition pos |
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
- pos = getReturnPosition(node)
+ pos = getReturnPosition(node.asNode())
)
or
revPartialPathThroughCallable(mid, node, ap, config) and
@@ -4060,12 +4373,13 @@ private module FlowExploration {
pragma[inline]
private predicate revPartialPathReadStep(
- PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
+ PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, NodeEx node,
+ RevPartialAccessPath ap2
) {
- exists(Node midNode |
- midNode = mid.getNode() and
+ exists(NodeEx midNode |
+ midNode = mid.getNodeEx() and
ap1 = mid.getAp() and
- read(node, c, midNode) and
+ read(node, c, midNode, mid.getConfiguration()) and
ap2.getHead() = c and
ap2.len() = unbindInt(ap1.len() + 1)
)
@@ -4083,12 +4397,12 @@ private module FlowExploration {
pragma[nomagic]
private predicate revPartialPathStoreStep(
- PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
+ PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, NodeEx node, Configuration config
) {
- exists(Node midNode, TypedContent tc |
- midNode = mid.getNode() and
+ exists(NodeEx midNode, TypedContent tc |
+ midNode = mid.getNodeEx() and
ap = mid.getAp() and
- store(node, tc, midNode, _) and
+ store(node, tc, midNode, _, config) and
ap.getHead() = c and
config = mid.getConfiguration() and
tc.getContent() = c
@@ -4100,9 +4414,9 @@ private module FlowExploration {
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
DataFlowCall call, RevPartialAccessPath ap, Configuration config
) {
- exists(Node out |
- mid.getNode() = out and
- viableReturnPosOut(call, pos, out) and
+ exists(NodeEx out |
+ mid.getNodeEx() = out and
+ viableReturnPosOutEx(call, pos, out) and
sc1 = TRevSummaryCtx1Some(pos) and
sc2 = TRevSummaryCtx2Some(ap) and
ap = mid.getAp() and
@@ -4115,9 +4429,9 @@ private module FlowExploration {
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
Configuration config
) {
- exists(PartialPathNodeRev mid, ParameterNode p |
- mid.getNode() = p and
- p.isParameterOf(_, pos) and
+ exists(PartialPathNodeRev mid, ParamNodeEx p |
+ mid.getNodeEx() = p and
+ p.getPosition() = pos and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
@@ -4138,11 +4452,11 @@ private module FlowExploration {
pragma[nomagic]
private predicate revPartialPathThroughCallable(
- PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
+ PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, int pos |
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
- node.argumentOf(call, pos)
+ node.asNode().(ArgNode).argumentOf(call, pos)
)
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
index 9498e51e7e61..df59c128ad39 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
@@ -81,6 +81,12 @@ abstract class Configuration extends string {
*/
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
+ /**
+ * Holds if an arbitrary number of implicit read steps of content `c` may be
+ * taken at `node`.
+ */
+ predicate allowImplicitRead(Node node, Content c) { none() }
+
/**
* Gets the virtual dispatch branching limit when calculating field flow.
* This can be overridden to a smaller value to improve performance (a
@@ -182,78 +188,210 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
}
}
-private predicate inBarrier(Node node, Configuration config) {
- config.isBarrierIn(node) and
- config.isSource(node)
+private newtype TNodeEx =
+ TNodeNormal(Node n) or
+ TNodeImplicitRead(Node n, boolean hasRead) {
+ any(Configuration c).allowImplicitRead(n, _) and hasRead = [false, true]
+ }
+
+private class NodeEx extends TNodeEx {
+ string toString() {
+ result = this.asNode().toString()
+ or
+ exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]")
+ }
+
+ Node asNode() { this = TNodeNormal(result) }
+
+ predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) }
+
+ Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) }
+
+ pragma[nomagic]
+ private DataFlowCallable getEnclosingCallable0() {
+ nodeEnclosingCallable(this.projectToNode(), result)
+ }
+
+ pragma[inline]
+ DataFlowCallable getEnclosingCallable() {
+ pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result)
+ }
+
+ pragma[nomagic]
+ private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) }
+
+ pragma[inline]
+ DataFlowType getDataFlowType() {
+ pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result)
+ }
+
+ predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ }
}
-private predicate outBarrier(Node node, Configuration config) {
- config.isBarrierOut(node) and
- config.isSink(node)
+private class ArgNodeEx extends NodeEx {
+ ArgNodeEx() { this.asNode() instanceof ArgNode }
}
-private predicate fullBarrier(Node node, Configuration config) {
- config.isBarrier(node)
- or
- config.isBarrierIn(node) and
- not config.isSource(node)
- or
- config.isBarrierOut(node) and
- not config.isSink(node)
- or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- node = g.getAGuardedNode()
+private class ParamNodeEx extends NodeEx {
+ ParamNodeEx() { this.asNode() instanceof ParamNode }
+
+ predicate isParameterOf(DataFlowCallable c, int i) {
+ this.asNode().(ParamNode).isParameterOf(c, i)
+ }
+
+ int getPosition() { this.isParameterOf(_, result) }
+}
+
+private class RetNodeEx extends NodeEx {
+ RetNodeEx() { this.asNode() instanceof ReturnNodeExt }
+
+ ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) }
+
+ ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() }
+}
+
+private predicate inBarrier(NodeEx node, Configuration config) {
+ exists(Node n |
+ node.asNode() = n and
+ config.isBarrierIn(n) and
+ config.isSource(n)
+ )
+}
+
+private predicate outBarrier(NodeEx node, Configuration config) {
+ exists(Node n |
+ node.asNode() = n and
+ config.isBarrierOut(n) and
+ config.isSink(n)
+ )
+}
+
+private predicate fullBarrier(NodeEx node, Configuration config) {
+ exists(Node n | node.asNode() = n |
+ config.isBarrier(n)
+ or
+ config.isBarrierIn(n) and
+ not config.isSource(n)
+ or
+ config.isBarrierOut(n) and
+ not config.isSink(n)
+ or
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
)
}
+pragma[nomagic]
+private predicate sourceNode(NodeEx node, Configuration config) { config.isSource(node.asNode()) }
+
+pragma[nomagic]
+private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) }
+
/**
* Holds if data can flow in one local step from `node1` to `node2`.
*/
-private predicate localFlowStep(Node node1, Node node2, Configuration config) {
- (
- simpleLocalFlowStep(node1, node2) or
- reverseStepThroughInputOutputAlias(node1, node2)
- ) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ simpleLocalFlowStepExt(n1, n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+ or
+ exists(Node n |
+ config.allowImplicitRead(n, _) and
+ node1.asNode() = n and
+ node2.isImplicitReadNode(n, false)
+ )
}
/**
* Holds if the additional step from `node1` to `node2` does not jump between callables.
*/
-private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
- config.isAdditionalFlowStep(node1, node2) and
- getNodeEnclosingCallable(node1) = getNodeEnclosingCallable(node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ config.isAdditionalFlowStep(n1, n2) and
+ getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+ or
+ exists(Node n |
+ config.allowImplicitRead(n, _) and
+ node1.isImplicitReadNode(n, true) and
+ node2.asNode() = n
+ )
}
/**
* Holds if data can flow from `node1` to `node2` in a way that discards call contexts.
*/
-private predicate jumpStep(Node node1, Node node2, Configuration config) {
- jumpStep(node1, node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ jumpStepCached(n1, n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
}
/**
* Holds if the additional step from `node1` to `node2` jumps between callables.
*/
-private predicate additionalJumpStep(Node node1, Node node2, Configuration config) {
- config.isAdditionalFlowStep(node1, node2) and
- getNodeEnclosingCallable(node1) != getNodeEnclosingCallable(node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ config.isAdditionalFlowStep(n1, n2) and
+ getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+}
+
+private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ read(node1.asNode(), c, node2.asNode())
+ or
+ exists(Node n |
+ node2.isImplicitReadNode(n, true) and
+ node1.isImplicitReadNode(n, _) and
+ config.allowImplicitRead(n, c)
+ )
+}
+
+private predicate store(
+ NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
+) {
+ store(node1.asNode(), tc, node2.asNode(), contentType) and
+ read(_, tc.getContent(), _, config)
+}
+
+pragma[nomagic]
+private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) {
+ viableReturnPosOut(call, pos, out.asNode())
+}
+
+pragma[nomagic]
+private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) {
+ viableParamArg(call, p.asNode(), arg.asNode())
}
/**
@@ -277,39 +415,39 @@ private module Stage1 {
* The Boolean `cc` records whether the node is reached through an
* argument in a call.
*/
- predicate fwdFlow(Node node, Cc cc, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
not fullBarrier(node, config) and
(
- config.isSource(node) and
+ sourceNode(node, config) and
cc = false
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
localFlowStep(mid, node, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
additionalLocalFlowStep(mid, node, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, config) and
jumpStep(mid, node, config) and
cc = false
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, config) and
additionalJumpStep(mid, node, config) and
cc = false
)
or
// store
- exists(Node mid |
+ exists(NodeEx mid |
useFieldFlow(config) and
fwdFlow(mid, cc, config) and
- store(mid, _, node, _) and
+ store(mid, _, node, _, config) and
not outBarrier(mid, config)
)
or
@@ -321,9 +459,9 @@ private module Stage1 {
)
or
// flow into a callable
- exists(Node arg |
+ exists(NodeEx arg |
fwdFlow(arg, _, config) and
- viableParamArg(_, node, arg) and
+ viableParamArgEx(_, node, arg) and
cc = true
)
or
@@ -338,13 +476,13 @@ private module Stage1 {
)
}
- private predicate fwdFlow(Node node, Configuration config) { fwdFlow(node, _, config) }
+ private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
pragma[nomagic]
- private predicate fwdFlowRead(Content c, Node node, Cc cc, Configuration config) {
- exists(Node mid |
+ private predicate fwdFlowRead(Content c, NodeEx node, Cc cc, Configuration config) {
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
- read(mid, c, node)
+ read(mid, c, node, config)
)
}
@@ -353,33 +491,33 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate fwdFlowConsCand(Content c, Configuration config) {
- exists(Node mid, Node node, TypedContent tc |
+ exists(NodeEx mid, NodeEx node, TypedContent tc |
not fullBarrier(node, config) and
useFieldFlow(config) and
fwdFlow(mid, _, config) and
- store(mid, tc, node, _) and
+ store(mid, tc, node, _, config) and
c = tc.getContent()
)
}
pragma[nomagic]
private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc, Configuration config) {
- exists(ReturnNodeExt ret |
+ exists(RetNodeEx ret |
fwdFlow(ret, cc, config) and
- getReturnPosition(ret) = pos
+ ret.getReturnPosition() = pos
)
}
pragma[nomagic]
- private predicate fwdFlowOut(DataFlowCall call, Node out, Cc cc, Configuration config) {
+ private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
exists(ReturnPosition pos |
fwdFlowReturnPosition(pos, cc, config) and
- viableReturnPosOut(call, pos, out)
+ viableReturnPosOutEx(call, pos, out)
)
}
pragma[nomagic]
- private predicate fwdFlowOutFromArg(DataFlowCall call, Node out, Configuration config) {
+ private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out, Configuration config) {
fwdFlowOut(call, out, true, config)
}
@@ -388,9 +526,9 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
- exists(ArgumentNode arg |
+ exists(ArgNodeEx arg |
fwdFlow(arg, cc, config) and
- viableParamArg(call, _, arg)
+ viableParamArgEx(call, _, arg)
)
}
@@ -402,34 +540,34 @@ private module Stage1 {
* the enclosing callable in order to reach a sink.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and
fwdFlow(node, config)
}
pragma[nomagic]
- private predicate revFlow0(Node node, boolean toReturn, Configuration config) {
+ private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStep(node, mid, config) and
revFlow(mid, toReturn, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
additionalLocalFlowStep(node, mid, config) and
revFlow(mid, toReturn, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, config) and
toReturn = false
)
or
- exists(Node mid |
+ exists(NodeEx mid |
additionalJumpStep(node, mid, config) and
revFlow(mid, _, config) and
toReturn = false
@@ -442,8 +580,8 @@ private module Stage1 {
)
or
// read
- exists(Node mid, Content c |
- read(node, c, mid) and
+ exists(NodeEx mid, Content c |
+ read(node, c, mid, config) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
revFlow(mid, toReturn, pragma[only_bind_into](config))
)
@@ -460,7 +598,7 @@ private module Stage1 {
// flow out of a callable
exists(ReturnPosition pos |
revFlowOut(pos, config) and
- getReturnPosition(node) = pos and
+ node.(RetNodeEx).getReturnPosition() = pos and
toReturn = true
)
}
@@ -470,20 +608,20 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Content c, Configuration config) {
- exists(Node mid, Node node |
+ exists(NodeEx mid, NodeEx node |
fwdFlow(node, pragma[only_bind_into](config)) and
- read(node, c, mid) and
+ read(node, c, mid, config) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
revFlow(pragma[only_bind_into](mid), _, pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate revFlowStore(Content c, Node node, boolean toReturn, Configuration config) {
- exists(Node mid, TypedContent tc |
+ private predicate revFlowStore(Content c, NodeEx node, boolean toReturn, Configuration config) {
+ exists(NodeEx mid, TypedContent tc |
revFlow(mid, toReturn, pragma[only_bind_into](config)) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
- store(node, tc, mid, _) and
+ store(node, tc, mid, _, config) and
c = tc.getContent()
)
}
@@ -499,15 +637,15 @@ private module Stage1 {
pragma[nomagic]
predicate viableReturnPosOutNodeCandFwd1(
- DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+ DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
fwdFlowReturnPosition(pos, _, config) and
- viableReturnPosOut(call, pos, out)
+ viableReturnPosOutEx(call, pos, out)
}
pragma[nomagic]
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
- exists(DataFlowCall call, Node out |
+ exists(DataFlowCall call, NodeEx out |
revFlow(out, _, config) and
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
)
@@ -515,33 +653,35 @@ private module Stage1 {
pragma[nomagic]
predicate viableParamArgNodeCandFwd1(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+ DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
- viableParamArg(call, p, arg) and
+ viableParamArgEx(call, p, arg) and
fwdFlow(arg, config)
}
pragma[nomagic]
private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config
+ DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
revFlow(p, toReturn, config) and
viableParamArgNodeCandFwd1(call, p, arg, config)
)
}
pragma[nomagic]
- private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
+ private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg, Configuration config) {
revFlowIn(call, arg, true, config)
}
/**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`.
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
*/
pragma[nomagic]
private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) {
- exists(Node out |
+ exists(NodeEx out |
revFlow(out, toReturn, config) and
fwdFlowOutFromArg(call, out, config)
)
@@ -549,32 +689,33 @@ private module Stage1 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Content c |
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
revFlow(node2, pragma[only_bind_into](config)) and
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
c = tc.getContent() and
exists(ap1)
)
}
pragma[nomagic]
- predicate readStepCand(Node n1, Content c, Node n2, Configuration config) {
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) and
- read(n1, c, n2)
+ read(n1, c, n2, pragma[only_bind_into](config))
}
pragma[nomagic]
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow(node, toReturn, config) and exists(returnAp) and exists(ap)
}
- private predicate throughFlowNodeCand(Node node, Configuration config) {
+ private predicate throughFlowNodeCand(NodeEx node, Configuration config) {
revFlow(node, true, config) and
fwdFlow(node, true, config) and
not inBarrier(node, config) and
@@ -586,9 +727,9 @@ private module Stage1 {
private predicate returnFlowCallableNodeCand(
DataFlowCallable callable, ReturnKindExt kind, Configuration config
) {
- exists(ReturnNodeExt ret |
+ exists(RetNodeEx ret |
throughFlowNodeCand(ret, config) and
- callable = getNodeEnclosingCallable(ret) and
+ callable = ret.getEnclosingCallable() and
kind = ret.getKind()
)
}
@@ -597,50 +738,57 @@ private module Stage1 {
* Holds if flow may enter through `p` and reach a return node making `p` a
* candidate for the origin of a summary.
*/
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
- getNodeEnclosingCallable(p) = c and
+ p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself
- not exists(int pos |
- kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos)
- )
+ not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
+ )
+ }
+
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(ArgNodeEx arg, boolean toReturn |
+ revFlow(arg, toReturn, config) and
+ revFlowInToReturn(call, arg, config) and
+ revFlowIsReturned(call, toReturn, config)
)
}
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, config)) and
fields = count(Content f0 | fwdFlowConsCand(f0, config)) and
conscand = -1 and
- tuples = count(Node n, boolean b | fwdFlow(n, b, config))
+ tuples = count(NodeEx n, boolean b | fwdFlow(n, b, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, config)) and
fields = count(Content f0 | revFlowConsCand(f0, config)) and
conscand = -1 and
- tuples = count(Node n, boolean b | revFlow(n, b, config))
+ tuples = count(NodeEx n, boolean b | revFlow(n, b, config))
}
/* End: Stage 1 logic. */
}
pragma[noinline]
-private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) {
Stage1::revFlow(node2, config) and
localFlowStep(node1, node2, config)
}
pragma[noinline]
-private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) {
Stage1::revFlow(node2, config) and
additionalLocalFlowStep(node1, node2, config)
}
pragma[nomagic]
private predicate viableReturnPosOutNodeCand1(
- DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+ DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
Stage1::revFlow(out, config) and
Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out, config)
@@ -653,9 +801,9 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, ReturnNodeExt ret, Node out, Configuration config
+ DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
) {
- viableReturnPosOutNodeCand1(call, getReturnPosition(ret), out, config) and
+ viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
@@ -663,7 +811,7 @@ private predicate flowOutOfCallNodeCand1(
pragma[nomagic]
private predicate viableParamArgNodeCand1(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+ DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and
Stage1::revFlow(arg, config)
@@ -675,7 +823,7 @@ private predicate viableParamArgNodeCand1(
*/
pragma[nomagic]
private predicate flowIntoCallNodeCand1(
- DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, Configuration config
) {
viableParamArgNodeCand1(call, p, arg, config) and
Stage1::revFlow(p, config) and
@@ -688,9 +836,9 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
-private int branch(Node n1, Configuration conf) {
+private int branch(NodeEx n1, Configuration conf) {
result =
- strictcount(Node n |
+ strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -700,9 +848,9 @@ private int branch(Node n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
-private int join(Node n2, Configuration conf) {
+private int join(NodeEx n2, Configuration conf) {
result =
- strictcount(Node n |
+ strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -716,7 +864,7 @@ private int join(Node n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
exists(int b, int j |
@@ -735,8 +883,7 @@ private predicate flowOutOfCallNodeCand1(
*/
pragma[nomagic]
private predicate flowIntoCallNodeCand1(
- DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
exists(int b, int j |
@@ -762,7 +909,7 @@ private module Stage2 {
bindingset[result, ap]
private ApApprox getApprox(Ap ap) { any() }
- private ApNil getApNil(Node node) { PrevStage::revFlow(node, _) and exists(result) }
+ private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
@@ -805,10 +952,10 @@ private module Stage2 {
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { any() }
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
(
preservesValue = true and
@@ -829,10 +976,20 @@ private module Stage2 {
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
/* Begin: Stage 2 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -842,14 +999,14 @@ private module Stage2 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -860,7 +1017,7 @@ private module Stage2 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -868,7 +1025,7 @@ private module Stage2 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -899,19 +1056,17 @@ private module Stage2 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -935,7 +1090,7 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -944,32 +1099,29 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -979,9 +1131,15 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -992,7 +1150,7 @@ private module Stage2 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
)
@@ -1000,18 +1158,41 @@ private module Stage2 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1021,41 +1202,41 @@ private module Stage2 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -1071,20 +1252,18 @@ private module Stage2 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -1097,7 +1276,7 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -1111,7 +1290,7 @@ private module Stage2 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -1120,10 +1299,10 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -1132,13 +1311,12 @@ private module Stage2 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1146,9 +1324,14 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1160,7 +1343,7 @@ private module Stage2 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -1169,16 +1352,17 @@ private module Stage2 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -1187,7 +1371,7 @@ private module Stage2 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -1199,44 +1383,54 @@ private module Stage2 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 2 logic. */
}
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
- DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
@@ -1245,7 +1439,7 @@ private predicate flowOutOfCallNodeCand2(
pragma[nomagic]
private predicate flowIntoCallNodeCand2(
- DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
@@ -1258,10 +1452,10 @@ private module LocalFlowBigStep {
* A node where some checking is required, and hence the big-step relation
* is not allowed to step over.
*/
- private class FlowCheckNode extends Node {
+ private class FlowCheckNode extends NodeEx {
FlowCheckNode() {
- this instanceof CastNode or
- clearsContent(this, _)
+ castNode(this.asNode()) or
+ clearsContentCached(this.asNode(), _)
}
}
@@ -1269,16 +1463,16 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- predicate localFlowEntry(Node node, Configuration config) {
+ predicate localFlowEntry(NodeEx node, Configuration config) {
Stage2::revFlow(node, config) and
(
- config.isSource(node) or
+ sourceNode(node, config) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
- node instanceof ParameterNode or
- node instanceof OutNodeExt or
- store(_, _, node, _) or
- read(_, _, node) or
+ node instanceof ParamNodeEx or
+ node.asNode() instanceof OutNodeExt or
+ store(_, _, node, _, config) or
+ read(_, _, node, config) or
node instanceof FlowCheckNode
)
}
@@ -1287,23 +1481,25 @@ private module LocalFlowBigStep {
* Holds if `node` can be the last node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- private predicate localFlowExit(Node node, Configuration config) {
- exists(Node next | Stage2::revFlow(next, config) |
+ private predicate localFlowExit(NodeEx node, Configuration config) {
+ exists(NodeEx next | Stage2::revFlow(next, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
- store(node, _, next, _) or
- read(node, _, next)
+ store(node, _, next, _, config) or
+ read(node, _, next, config)
)
or
node instanceof FlowCheckNode
or
- config.isSink(node)
+ sinkNode(node, config)
}
pragma[noinline]
- private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
+ private predicate additionalLocalFlowStepNodeCand2(
+ NodeEx node1, NodeEx node2, Configuration config
+ ) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and
Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config))
@@ -1318,39 +1514,39 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
- Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config,
+ NodeEx node1, NodeEx node2, boolean preservesValue, DataFlowType t, Configuration config,
LocalCallContext cc
) {
- not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
+ not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
(
localFlowEntry(node1, pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
- t = getNodeType(node1)
+ t = node1.getDataFlowType() // irrelevant dummy value
or
additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
- t = getNodeType(node2)
+ t = node2.getDataFlowType()
) and
node1 != node2 and
- cc.relevantFor(getNodeEnclosingCallable(node1)) and
- not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
+ cc.relevantFor(node1.getEnclosingCallable()) and
+ not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
Stage2::revFlow(node2, pragma[only_bind_into](config))
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStepPlus(node1, mid, preservesValue, t, pragma[only_bind_into](config), cc) and
localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof FlowCheckNode and
Stage2::revFlow(node2, pragma[only_bind_into](config))
)
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStepPlus(node1, mid, _, _, pragma[only_bind_into](config), cc) and
additionalLocalFlowStepNodeCand2(mid, node2, config) and
not mid instanceof FlowCheckNode and
preservesValue = false and
- t = getNodeType(node2) and
+ t = node2.getDataFlowType() and
Stage2::revFlow(node2, pragma[only_bind_into](config))
)
)
@@ -1362,8 +1558,8 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, AccessPathFrontNil apf, Configuration config,
- LocalCallContext callContext
+ NodeEx node1, NodeEx node2, boolean preservesValue, AccessPathFrontNil apf,
+ Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, config)
@@ -1383,8 +1579,8 @@ private module Stage3 {
private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(Node node) {
- PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node))
+ private ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
@@ -1428,10 +1624,10 @@ private module Stage3 {
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { any() }
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, node2, preservesValue, ap, config, _) and exists(lcc)
}
@@ -1440,10 +1636,16 @@ private module Stage3 {
private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ pragma[nomagic]
+ private predicate clear(NodeEx node, Ap ap) { ap.isClearedAt(node.asNode()) }
+
+ pragma[nomagic]
+ private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
+
bindingset[node, ap]
- private predicate filter(Node node, Ap ap) {
- not ap.isClearedAt(node) and
- if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any()
+ private predicate filter(NodeEx node, Ap ap) {
+ not clear(node, ap) and
+ if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()
}
bindingset[ap, contentType]
@@ -1454,7 +1656,7 @@ private module Stage3 {
}
/* Begin: Stage 3 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
@@ -1465,6 +1667,16 @@ private module Stage3 {
)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -1474,21 +1686,21 @@ private module Stage3 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
pragma[nomagic]
- private predicate fwdFlow0(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -1499,7 +1711,7 @@ private module Stage3 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -1507,7 +1719,7 @@ private module Stage3 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -1538,19 +1750,17 @@ private module Stage3 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -1574,7 +1784,7 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -1583,32 +1793,29 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1618,9 +1825,15 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1631,7 +1844,7 @@ private module Stage3 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
@@ -1639,18 +1852,41 @@ private module Stage3 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1660,41 +1896,41 @@ private module Stage3 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -1710,20 +1946,18 @@ private module Stage3 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -1736,7 +1970,7 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -1750,7 +1984,7 @@ private module Stage3 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -1759,10 +1993,10 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -1771,13 +2005,12 @@ private module Stage3 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1785,9 +2018,14 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1799,7 +2037,7 @@ private module Stage3 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -1808,16 +2046,17 @@ private module Stage3 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -1826,7 +2065,7 @@ private module Stage3 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -1838,37 +2077,47 @@ private module Stage3 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 3 logic. */
}
@@ -1877,7 +2126,7 @@ private module Stage3 {
* Holds if `argApf` is recorded as the summary context for flow reaching `node`
* and remains relevant for the following pruning stage.
*/
-private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) {
+private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
@@ -1892,7 +2141,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
exists(int tails, int nodes, int apLimit, int tupleLimit |
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
- strictcount(Node n |
+ strictcount(NodeEx n |
Stage3::revFlow(n, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
@@ -2087,8 +2336,8 @@ private module Stage4 {
private ApApprox getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(Node node) {
- PrevStage::revFlow(node, _) and result = TNil(getNodeType(node))
+ private ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
@@ -2127,26 +2376,25 @@ private module Stage4 {
bindingset[innercc, inner, call]
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
resolveReturn(innercc, inner, call)
- or
- innercc.(CallContextCall).matchesCall(call)
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
localFlowEntry(node, config) and
- result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node))
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
}
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, node2, preservesValue, ap.getFront(), config, lcc)
}
pragma[nomagic]
private predicate flowOutOfCall(
- DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
@@ -2155,7 +2403,7 @@ private module Stage4 {
pragma[nomagic]
private predicate flowIntoCall(
- DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
@@ -2164,14 +2412,14 @@ private module Stage4 {
}
bindingset[node, ap]
- private predicate filter(Node node, Ap ap) { any() }
+ private predicate filter(NodeEx node, Ap ap) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
/* Begin: Stage 4 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
@@ -2182,6 +2430,16 @@ private module Stage4 {
)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -2191,21 +2449,21 @@ private module Stage4 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
pragma[nomagic]
- private predicate fwdFlow0(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -2216,7 +2474,7 @@ private module Stage4 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -2224,7 +2482,7 @@ private module Stage4 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -2255,19 +2513,17 @@ private module Stage4 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -2291,7 +2547,7 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -2300,32 +2556,29 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -2335,9 +2588,15 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -2348,7 +2607,7 @@ private module Stage4 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
@@ -2356,18 +2615,41 @@ private module Stage4 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -2377,41 +2659,41 @@ private module Stage4 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -2427,20 +2709,18 @@ private module Stage4 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -2453,7 +2733,7 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -2467,7 +2747,7 @@ private module Stage4 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -2476,10 +2756,10 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -2488,13 +2768,12 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -2502,9 +2781,14 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -2516,7 +2800,7 @@ private module Stage4 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -2525,16 +2809,17 @@ private module Stage4 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -2543,7 +2828,7 @@ private module Stage4 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -2555,37 +2840,47 @@ private module Stage4 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 4 logic. */
}
@@ -2595,18 +2890,18 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
}
-private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) {
+private predicate nodeMayUseSummary(NodeEx n, AccessPathApprox apa, Configuration config) {
exists(DataFlowCallable c, AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and
Stage4::revFlow(n, true, _, apa0, config) and
Stage4::fwdFlow(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
- getNodeEnclosingCallable(n) = c
+ n.getEnclosingCallable() = c
)
}
private newtype TSummaryCtx =
TSummaryCtxNone() or
- TSummaryCtxSome(ParameterNode p, AccessPath ap) {
+ TSummaryCtxSome(ParamNodeEx p, AccessPath ap) {
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _)
}
@@ -2627,7 +2922,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone {
/** A summary context from which a flow summary can be generated. */
private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
- private ParameterNode p;
+ private ParamNodeEx p;
private AccessPath ap;
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
@@ -2660,7 +2955,9 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
- strictcount(Node n | Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config))
+ strictcount(NodeEx n |
+ Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)
+ )
}
/**
@@ -2752,13 +3049,13 @@ private newtype TAccessPath =
}
private newtype TPathNode =
- TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
+ TPathNodeMid(NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
- ap = TAccessPathNil(getNodeType(node))
+ ap = TAccessPathNil(node.getDataFlowType())
or
// ... or a step from an existing PathNode to another node.
exists(PathNodeMid mid |
@@ -2767,12 +3064,12 @@ private newtype TPathNode =
Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config))
)
} or
- TPathNodeSink(Node node, Configuration config) {
- pragma[only_bind_into](config).isSink(node) and
+ TPathNodeSink(NodeEx node, Configuration config) {
+ sinkNode(node, pragma[only_bind_into](config)) and
Stage4::revFlow(node, pragma[only_bind_into](config)) and
(
// A sink that is also a source ...
- config.isSource(node)
+ sourceNode(node, config)
or
// ... or a sink that can be reached from a source
exists(PathNodeMid mid |
@@ -2973,15 +3270,17 @@ class PathNode extends TPathNode {
}
/** Gets the underlying `Node`. */
- Node getNode() { none() }
+ final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
private predicate isHidden() {
- nodeIsHidden(this.getNode()) and
+ hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
+ or
+ this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
}
private PathNode getASuccessorIfHidden() {
@@ -3003,6 +3302,8 @@ class PathNode extends TPathNode {
abstract private class PathNodeImpl extends PathNode {
abstract PathNode getASuccessorImpl();
+ abstract NodeEx getNodeEx();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3017,14 +3318,14 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
}
- override string toString() { result = this.getNode().toString() + ppAp() }
+ override string toString() { result = this.getNodeEx().toString() + ppAp() }
- override string toStringWithContext() { result = this.getNode().toString() + ppAp() + ppCtx() }
+ override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
@@ -3054,7 +3355,7 @@ module PathGraph {
* a `CallContext`, and a `Configuration`.
*/
private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
- Node node;
+ NodeEx node;
CallContext cc;
SummaryCtx sc;
AccessPath ap;
@@ -3062,7 +3363,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) }
- override Node getNode() { result = node }
+ override NodeEx getNodeEx() { result = node }
CallContext getCallContext() { result = cc }
@@ -3073,7 +3374,8 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
override Configuration getConfiguration() { result = config }
private PathNodeMid getSuccMid() {
- pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and
+ pathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx(),
+ result.getAp()) and
result.getConfiguration() = unbindConf(this.getConfiguration())
}
@@ -3084,7 +3386,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid, PathNodeSink sink |
mid = getSuccMid() and
- mid.getNode() = sink.getNode() and
+ mid.getNodeEx() = sink.getNodeEx() and
mid.getAp() instanceof AccessPathNil and
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
result = sink
@@ -3092,7 +3394,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
}
override predicate isSource() {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
@@ -3105,31 +3407,35 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
* excluding the `CallContext`.
*/
private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
- Node node;
+ NodeEx node;
Configuration config;
PathNodeSink() { this = TPathNodeSink(node, config) }
- override Node getNode() { result = node }
+ override NodeEx getNodeEx() { result = node }
override Configuration getConfiguration() { result = config }
override PathNode getASuccessorImpl() { none() }
- override predicate isSource() { config.isSource(node) }
+ override predicate isSource() { sourceNode(node, config) }
}
/**
* Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`.
*/
-private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
- exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
- midnode = mid.getNode() and
+private predicate pathStep(
+ PathNodeMid mid, NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap
+) {
+ exists(AccessPath ap0, NodeEx midnode, Configuration conf, LocalCallContext localCC |
+ midnode = mid.getNodeEx() and
conf = mid.getConfiguration() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
- localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and
+ localCC =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ midnode.getEnclosingCallable()) and
ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
@@ -3139,16 +3445,16 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
ap0 instanceof AccessPathNil
)
or
- jumpStep(mid.getNode(), node, mid.getConfiguration()) and
+ jumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
ap = mid.getAp()
or
- additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and
+ additionalJumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
mid.getAp() instanceof AccessPathNil and
- ap = TAccessPathNil(getNodeType(node))
+ ap = TAccessPathNil(node.getDataFlowType())
or
exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and
sc = mid.getSummaryCtx()
@@ -3165,20 +3471,20 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
pragma[nomagic]
private predicate pathReadStep(
- PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+ PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
- Stage4::readStepCand(mid.getNode(), tc.getContent(), node, mid.getConfiguration()) and
+ Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+ PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- Stage4::storeStepCand(mid.getNode(), _, tc, node, _, mid.getConfiguration()) and
+ Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
cc = mid.getCallContext()
}
@@ -3186,7 +3492,7 @@ private predicate pathOutOfCallable0(
PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa,
Configuration config
) {
- pos = getReturnPosition(mid.getNode()) and
+ pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and
innercc = mid.getCallContext() and
innercc instanceof CallContextNoCall and
apa = mid.getAp().getApprox() and
@@ -3209,10 +3515,10 @@ private predicate pathOutOfCallable1(
}
pragma[noinline]
-private Node getAnOutNodeFlow(
+private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
- result = kind.getAnOutNode(call) and
+ result.asNode() = kind.getAnOutNode(call) and
Stage4::revFlow(result, _, _, apa, config)
}
@@ -3221,7 +3527,7 @@ private Node getAnOutNodeFlow(
* is a return from a callable and is recorded by `cc`, if needed.
*/
pragma[noinline]
-private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
+private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) {
exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config |
pathOutOfCallable1(mid, call, kind, cc, apa, config) and
out = getAnOutNodeFlow(kind, call, apa, config)
@@ -3235,8 +3541,8 @@ pragma[noinline]
private predicate pathIntoArg(
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa
) {
- exists(ArgumentNode arg |
- arg = mid.getNode() and
+ exists(ArgNode arg |
+ arg = mid.getNodeEx().asNode() and
cc = mid.getCallContext() and
arg.argumentOf(call, i) and
ap = mid.getAp() and
@@ -3248,7 +3554,7 @@ pragma[noinline]
private predicate parameterCand(
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
Stage4::revFlow(p, _, _, apa, config) and
p.isParameterOf(callable, i)
)
@@ -3272,7 +3578,7 @@ private predicate pathIntoCallable0(
* respectively.
*/
private predicate pathIntoCallable(
- PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
+ PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
DataFlowCall call
) {
exists(int i, DataFlowCallable callable, AccessPath ap |
@@ -3297,8 +3603,8 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa,
Configuration config
) {
- exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
- mid.getNode() = ret and
+ exists(PathNodeMid mid, RetNodeEx ret, int pos |
+ mid.getNodeEx() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
@@ -3326,7 +3632,7 @@ private predicate pathThroughCallable0(
* The context `cc` is restored to its value prior to entering the callable.
*/
pragma[noinline]
-private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
+private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) {
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
pathThroughCallable0(call, mid, kind, cc, ap, apa) and
out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
@@ -3344,9 +3650,9 @@ private predicate flowsTo(
) {
flowsource.isSource() and
flowsource.getConfiguration() = configuration and
- flowsource.getNode() = source and
+ flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
- flowsink.getNode() = sink
+ flowsink.getNodeEx().asNode() = sink
}
/**
@@ -3361,13 +3667,13 @@ predicate flowsTo(Node source, Node sink, Configuration configuration) {
private predicate finalStats(boolean fwd, int nodes, int fields, int conscand, int tuples) {
fwd = true and
- nodes = count(Node n0 | exists(PathNode pn | pn.getNode() = n0)) and
+ nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
tuples = count(PathNode pn)
or
fwd = false and
- nodes = count(Node n0 | exists(PathNode pn | pn.getNode() = n0 and reach(pn))) and
+ nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
tuples = count(PathNode pn | reach(pn))
@@ -3404,19 +3710,19 @@ predicate stageStats(
private module FlowExploration {
private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2, Configuration config) {
- exists(Node node1, Node node2 |
+ exists(NodeEx node1, NodeEx node2 |
jumpStep(node1, node2, config)
or
additionalJumpStep(node1, node2, config)
or
// flow into callable
- viableParamArg(_, node2, node1)
+ viableParamArgEx(_, node2, node1)
or
// flow out of a callable
- viableReturnPosOut(_, getReturnPosition(node1), node2)
+ viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2)
|
- c1 = getNodeEnclosingCallable(node1) and
- c2 = getNodeEnclosingCallable(node2) and
+ c1 = node1.getEnclosingCallable() and
+ c2 = node2.getEnclosingCallable() and
c1 != c2
)
}
@@ -3568,7 +3874,7 @@ private module FlowExploration {
private newtype TSummaryCtx1 =
TSummaryCtx1None() or
- TSummaryCtx1Param(ParameterNode p)
+ TSummaryCtx1Param(ParamNodeEx p)
private newtype TSummaryCtx2 =
TSummaryCtx2None() or
@@ -3584,25 +3890,25 @@ private module FlowExploration {
private newtype TPartialPathNode =
TPartialPathNodeFwd(
- Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
+ NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
- distSrc(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
+ distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
- Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
+ NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
Configuration config
) {
- config.isSink(node) and
+ sinkNode(node, config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil() and
@@ -3611,23 +3917,23 @@ private module FlowExploration {
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
- not clearsContent(node, ap.getHead()) and
+ not clearsContentCached(node.asNode(), ap.getHead()) and
not fullBarrier(node, config) and
- distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
+ distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
}
pragma[nomagic]
private predicate partialPathNodeMk0(
- Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
+ NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
- not clearsContent(node, ap.getHead().getContent()) and
- if node instanceof CastingNode
- then compatibleTypes(getNodeType(node), ap.getType())
+ not clearsContentCached(node.asNode(), ap.getHead().getContent()) and
+ if node.asNode() instanceof CastingNode
+ then compatibleTypes(node.getDataFlowType(), ap.getType())
else any()
)
}
@@ -3637,13 +3943,15 @@ private module FlowExploration {
*/
class PartialPathNode extends TPartialPathNode {
/** Gets a textual representation of this element. */
- string toString() { result = this.getNode().toString() + this.ppAp() }
+ string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/**
* Gets a textual representation of this element, including a textual
* representation of the call context.
*/
- string toStringWithContext() { result = this.getNode().toString() + this.ppAp() + this.ppCtx() }
+ string toStringWithContext() {
+ result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
+ }
/**
* Holds if this element is at the specified location.
@@ -3655,11 +3963,16 @@ private module FlowExploration {
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the underlying `Node`. */
- Node getNode() { none() }
+ final Node getNode() { this.getNodeEx().projectToNode() = result }
+
+ private NodeEx getNodeEx() {
+ result = this.(PartialPathNodeFwd).getNodeEx() or
+ result = this.(PartialPathNodeRev).getNodeEx()
+ }
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
@@ -3672,7 +3985,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSourceDistance() {
- result = distSrc(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
+ result = distSrc(this.getNodeEx().getEnclosingCallable(), this.getConfiguration())
}
/**
@@ -3680,7 +3993,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSinkDistance() {
- result = distSink(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
+ result = distSink(this.getNodeEx().getEnclosingCallable(), this.getConfiguration())
}
private string ppAp() {
@@ -3712,7 +4025,7 @@ private module FlowExploration {
}
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
- Node node;
+ NodeEx node;
CallContext cc;
TSummaryCtx1 sc1;
TSummaryCtx2 sc2;
@@ -3721,7 +4034,7 @@ private module FlowExploration {
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
- override Node getNode() { result = node }
+ NodeEx getNodeEx() { result = node }
CallContext getCallContext() { result = cc }
@@ -3734,12 +4047,12 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodeFwd getASuccessor() {
- partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
+ partialPathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx1(),
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
}
predicate isSource() {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
@@ -3748,7 +4061,7 @@ private module FlowExploration {
}
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
- Node node;
+ NodeEx node;
TRevSummaryCtx1 sc1;
TRevSummaryCtx2 sc2;
RevPartialAccessPath ap;
@@ -3756,7 +4069,7 @@ private module FlowExploration {
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
- override Node getNode() { result = node }
+ NodeEx getNodeEx() { result = node }
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
@@ -3767,12 +4080,12 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodeRev getASuccessor() {
- revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
+ revPartialPathStep(result, this.getNodeEx(), this.getSummaryCtx1(), this.getSummaryCtx2(),
this.getAp(), this.getConfiguration())
}
predicate isSink() {
- config.isSink(node) and
+ sinkNode(node, config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil()
@@ -3780,40 +4093,40 @@ private module FlowExploration {
}
private predicate partialPathStep(
- PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
+ PartialPathNodeFwd mid, NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
- not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
+ not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and
(
- localFlowStep(mid.getNode(), node, config) and
+ localFlowStep(mid.getNodeEx(), node, config) and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalLocalFlowStep(mid.getNode(), node, config) and
+ additionalLocalFlowStep(mid.getNodeEx(), node, config) and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
config = mid.getConfiguration()
)
or
- jumpStep(mid.getNode(), node, config) and
+ jumpStep(mid.getNodeEx(), node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalJumpStep(mid.getNode(), node, config) and
+ additionalJumpStep(mid.getNodeEx(), node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
config = mid.getConfiguration()
or
partialPathStoreStep(mid, _, _, node, ap) and
@@ -3826,8 +4139,7 @@ private module FlowExploration {
partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, tc, ap0, config) and
- compatibleTypes(ap.getType(), getNodeType(node))
+ apConsFwd(ap, tc, ap0, config)
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -3846,12 +4158,13 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
+ PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, NodeEx node,
+ PartialAccessPath ap2
) {
- exists(Node midNode, DataFlowType contentType |
- midNode = mid.getNode() and
+ exists(NodeEx midNode, DataFlowType contentType |
+ midNode = mid.getNodeEx() and
ap1 = mid.getAp() and
- store(midNode, tc, node, contentType) and
+ store(midNode, tc, node, contentType, mid.getConfiguration()) and
ap2.getHead() = tc and
ap2.len() = unbindInt(ap1.len() + 1) and
compatibleTypes(ap1.getType(), contentType)
@@ -3870,15 +4183,15 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
+ PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, NodeEx node, CallContext cc,
Configuration config
) {
- exists(Node midNode |
- midNode = mid.getNode() and
+ exists(NodeEx midNode |
+ midNode = mid.getNodeEx() and
ap = mid.getAp() and
- read(midNode, tc.getContent(), node) and
+ read(midNode, tc.getContent(), node, pragma[only_bind_into](config)) and
ap.getHead() = tc and
- config = mid.getConfiguration() and
+ pragma[only_bind_into](config) = mid.getConfiguration() and
cc = mid.getCallContext()
)
}
@@ -3887,7 +4200,7 @@ private module FlowExploration {
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
Configuration config
) {
- pos = getReturnPosition(mid.getNode()) and
+ pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and
innercc = mid.getCallContext() and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
@@ -3910,12 +4223,12 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
- PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = kind.getAnOutNode(call)
+ out.asNode() = kind.getAnOutNode(call)
)
}
@@ -3924,8 +4237,8 @@ private module FlowExploration {
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
- exists(ArgumentNode arg |
- arg = mid.getNode() and
+ exists(ArgNode arg |
+ arg = mid.getNodeEx().asNode() and
cc = mid.getCallContext() and
arg.argumentOf(call, i) and
ap = mid.getAp() and
@@ -3943,7 +4256,7 @@ private module FlowExploration {
}
private predicate partialPathIntoCallable(
- PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
+ PartialPathNodeFwd mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc,
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
@@ -3964,8 +4277,8 @@ private module FlowExploration {
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
- exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
- mid.getNode() = ret and
+ exists(PartialPathNodeFwd mid, RetNodeEx ret |
+ mid.getNodeEx() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
@@ -3980,45 +4293,45 @@ private module FlowExploration {
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
- exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
- partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and
+ exists(CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
+ partialPathIntoCallable(mid, _, cc, innercc, sc1, sc2, call, _, config) and
paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config)
)
}
private predicate partialPathThroughCallable(
- PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
- out = kind.getAnOutNode(call)
+ out.asNode() = kind.getAnOutNode(call)
)
}
private predicate revPartialPathStep(
- PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
+ PartialPathNodeRev mid, NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
RevPartialAccessPath ap, Configuration config
) {
- localFlowStep(node, mid.getNode(), config) and
+ localFlowStep(node, mid.getNodeEx(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalLocalFlowStep(node, mid.getNode(), config) and
+ additionalLocalFlowStep(node, mid.getNodeEx(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
- jumpStep(node, mid.getNode(), config) and
+ jumpStep(node, mid.getNodeEx(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalJumpStep(node, mid.getNode(), config) and
+ additionalJumpStep(node, mid.getNodeEx(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
mid.getAp() instanceof RevPartialAccessPathNil and
@@ -4037,9 +4350,9 @@ private module FlowExploration {
apConsRev(ap, c, ap0, config)
)
or
- exists(ParameterNode p |
- mid.getNode() = p and
- viableParamArg(_, p, node) and
+ exists(ParamNodeEx p |
+ mid.getNodeEx() = p and
+ viableParamArgEx(_, p, node) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
sc1 = TRevSummaryCtx1None() and
@@ -4050,7 +4363,7 @@ private module FlowExploration {
or
exists(ReturnPosition pos |
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
- pos = getReturnPosition(node)
+ pos = getReturnPosition(node.asNode())
)
or
revPartialPathThroughCallable(mid, node, ap, config) and
@@ -4060,12 +4373,13 @@ private module FlowExploration {
pragma[inline]
private predicate revPartialPathReadStep(
- PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
+ PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, NodeEx node,
+ RevPartialAccessPath ap2
) {
- exists(Node midNode |
- midNode = mid.getNode() and
+ exists(NodeEx midNode |
+ midNode = mid.getNodeEx() and
ap1 = mid.getAp() and
- read(node, c, midNode) and
+ read(node, c, midNode, mid.getConfiguration()) and
ap2.getHead() = c and
ap2.len() = unbindInt(ap1.len() + 1)
)
@@ -4083,12 +4397,12 @@ private module FlowExploration {
pragma[nomagic]
private predicate revPartialPathStoreStep(
- PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
+ PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, NodeEx node, Configuration config
) {
- exists(Node midNode, TypedContent tc |
- midNode = mid.getNode() and
+ exists(NodeEx midNode, TypedContent tc |
+ midNode = mid.getNodeEx() and
ap = mid.getAp() and
- store(node, tc, midNode, _) and
+ store(node, tc, midNode, _, config) and
ap.getHead() = c and
config = mid.getConfiguration() and
tc.getContent() = c
@@ -4100,9 +4414,9 @@ private module FlowExploration {
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
DataFlowCall call, RevPartialAccessPath ap, Configuration config
) {
- exists(Node out |
- mid.getNode() = out and
- viableReturnPosOut(call, pos, out) and
+ exists(NodeEx out |
+ mid.getNodeEx() = out and
+ viableReturnPosOutEx(call, pos, out) and
sc1 = TRevSummaryCtx1Some(pos) and
sc2 = TRevSummaryCtx2Some(ap) and
ap = mid.getAp() and
@@ -4115,9 +4429,9 @@ private module FlowExploration {
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
Configuration config
) {
- exists(PartialPathNodeRev mid, ParameterNode p |
- mid.getNode() = p and
- p.isParameterOf(_, pos) and
+ exists(PartialPathNodeRev mid, ParamNodeEx p |
+ mid.getNodeEx() = p and
+ p.getPosition() = pos and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
@@ -4138,11 +4452,11 @@ private module FlowExploration {
pragma[nomagic]
private predicate revPartialPathThroughCallable(
- PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
+ PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, int pos |
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
- node.argumentOf(call, pos)
+ node.asNode().(ArgNode).argumentOf(call, pos)
)
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
index a51c20c22881..eaed77326c71 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
@@ -31,26 +31,26 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) {
* currently excludes read-steps, store-steps, and flow-through.
*
* The analysis uses non-linear recursion: When computing a flow path in or out
- * of a call, we use the results of the analysis recursively to resolve lamba
+ * of a call, we use the results of the analysis recursively to resolve lambda
* calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly.
*/
private module LambdaFlow {
- private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) {
+ private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) {
p.isParameterOf(viableCallable(call), i)
}
- private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) {
+ private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) {
p.isParameterOf(viableCallableLambda(call, _), i)
}
- private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) {
exists(int i |
viableParamNonLambda(call, i, p) and
arg.argumentOf(call, i)
)
}
- private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) {
exists(int i |
viableParamLambda(call, i, p) and
arg.argumentOf(call, i)
@@ -118,8 +118,8 @@ private module LambdaFlow {
boolean toJump, DataFlowCallOption lastCall
) {
revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and
- if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode
- then compatibleTypes(t, getNodeType(node))
+ if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode
+ then compatibleTypes(t, getNodeDataFlowType(node))
else any()
}
@@ -129,7 +129,7 @@ private module LambdaFlow {
boolean toJump, DataFlowCallOption lastCall
) {
lambdaCall(lambdaCall, kind, node) and
- t = getNodeType(node) and
+ t = getNodeDataFlowType(node) and
toReturn = false and
toJump = false and
lastCall = TDataFlowCallNone()
@@ -146,7 +146,7 @@ private module LambdaFlow {
getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid)
|
preservesValue = false and
- t = getNodeType(node)
+ t = getNodeDataFlowType(node)
or
preservesValue = true and
t = t0
@@ -160,7 +160,7 @@ private module LambdaFlow {
toJump = true and
lastCall = TDataFlowCallNone()
|
- jumpStep(node, mid) and
+ jumpStepCached(node, mid) and
t = t0
or
exists(boolean preservesValue |
@@ -168,7 +168,7 @@ private module LambdaFlow {
getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid)
|
preservesValue = false and
- t = getNodeType(node)
+ t = getNodeDataFlowType(node)
or
preservesValue = true and
t = t0
@@ -176,7 +176,7 @@ private module LambdaFlow {
)
or
// flow into a callable
- exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call |
+ exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call |
revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and
(
if lastCall0 = TDataFlowCallNone() and toJump = false
@@ -227,7 +227,7 @@ private module LambdaFlow {
pragma[nomagic]
predicate revLambdaFlowIn(
- DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump,
+ DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump,
DataFlowCallOption lastCall
) {
revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall)
@@ -242,6 +242,89 @@ private DataFlowCallable viableCallableExt(DataFlowCall call) {
cached
private module Cached {
+ /**
+ * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to
+ * force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby
+ * collapsing the two stages.
+ */
+ cached
+ predicate forceCachingInSameStage() { any() }
+
+ cached
+ predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() }
+
+ cached
+ predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) {
+ c = call.getEnclosingCallable()
+ }
+
+ cached
+ predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) }
+
+ cached
+ predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) }
+
+ cached
+ predicate clearsContentCached(Node n, Content c) { clearsContent(n, c) }
+
+ cached
+ predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) }
+
+ cached
+ predicate outNodeExt(Node n) {
+ n instanceof OutNode
+ or
+ n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode
+ }
+
+ cached
+ predicate hiddenNode(Node n) { nodeIsHidden(n) }
+
+ cached
+ OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) {
+ result = getAnOutNode(call, k.(ValueReturnKind).getKind())
+ or
+ exists(ArgNode arg |
+ result.(PostUpdateNode).getPreUpdateNode() = arg and
+ arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition())
+ )
+ }
+
+ cached
+ predicate returnNodeExt(Node n, ReturnKindExt k) {
+ k = TValueReturn(n.(ReturnNode).getKind())
+ or
+ exists(ParamNode p, int pos |
+ parameterValueFlowsToPreUpdate(p, n) and
+ p.isParameterOf(_, pos) and
+ k = TParamUpdate(pos)
+ )
+ }
+
+ cached
+ predicate castNode(Node n) { n instanceof CastNode }
+
+ cached
+ predicate castingNode(Node n) {
+ castNode(n) or
+ n instanceof ParamNode or
+ n 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`.
+ read(_, _, n)
+ }
+
+ cached
+ predicate parameterNode(Node n, DataFlowCallable c, int i) {
+ n.(ParameterNode).isParameterOf(c, i)
+ }
+
+ cached
+ predicate argumentNode(Node n, DataFlowCall call, int pos) {
+ n.(ArgumentNode).argumentOf(call, pos)
+ }
+
/**
* Gets a viable target for the lambda call `call`.
*
@@ -261,7 +344,7 @@ private module Cached {
* The instance parameter is considered to have index `-1`.
*/
pragma[nomagic]
- private predicate viableParam(DataFlowCall call, int i, ParameterNode p) {
+ private predicate viableParam(DataFlowCall call, int i, ParamNode p) {
p.isParameterOf(viableCallableExt(call), i)
}
@@ -270,11 +353,11 @@ private module Cached {
* dispatch into account.
*/
cached
- predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) {
exists(int i |
viableParam(call, i, p) and
arg.argumentOf(call, i) and
- compatibleTypes(getNodeType(arg), getNodeType(p))
+ compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p))
)
}
@@ -312,7 +395,7 @@ private module Cached {
* `read` indicates whether it is contents of `p` that can flow to `node`.
*/
pragma[nomagic]
- private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) {
+ private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) {
p = node and
read = false
or
@@ -325,30 +408,30 @@ private module Cached {
// read
exists(Node mid |
parameterValueFlowCand(p, mid, false) and
- readStep(mid, _, node) and
+ read(mid, _, node) and
read = true
)
or
// flow through: no prior read
- exists(ArgumentNode arg |
+ exists(ArgNode arg |
parameterValueFlowArgCand(p, arg, false) and
argumentValueFlowsThroughCand(arg, node, read)
)
or
// flow through: no read inside method
- exists(ArgumentNode arg |
+ exists(ArgNode arg |
parameterValueFlowArgCand(p, arg, read) and
argumentValueFlowsThroughCand(arg, node, false)
)
}
pragma[nomagic]
- private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) {
+ private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) {
parameterValueFlowCand(p, arg, read)
}
pragma[nomagic]
- predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) {
+ predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) {
parameterValueFlowCand(p, n.getPreUpdateNode(), false)
}
@@ -360,7 +443,7 @@ private module Cached {
* `read` indicates whether it is contents of `p` that can flow to the return
* node.
*/
- predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) {
+ predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) {
exists(ReturnNode ret |
parameterValueFlowCand(p, ret, read) and
kind = ret.getKind()
@@ -369,9 +452,9 @@ private module Cached {
pragma[nomagic]
private predicate argumentValueFlowsThroughCand0(
- DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read
+ DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read
) {
- exists(ParameterNode param | viableParamArg(call, param, arg) |
+ exists(ParamNode param | viableParamArg(call, param, arg) |
parameterValueFlowReturnCand(param, kind, read)
)
}
@@ -382,14 +465,14 @@ private module Cached {
*
* `read` indicates whether it is contents of `arg` that can flow to `out`.
*/
- predicate argumentValueFlowsThroughCand(ArgumentNode arg, Node out, boolean read) {
+ predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) {
exists(DataFlowCall call, ReturnKind kind |
argumentValueFlowsThroughCand0(call, arg, kind, read) and
out = getAnOutNode(call, kind)
)
}
- predicate cand(ParameterNode p, Node n) {
+ predicate cand(ParamNode p, Node n) {
parameterValueFlowCand(p, n, _) and
(
parameterValueFlowReturnCand(p, _, _)
@@ -416,21 +499,21 @@ private module Cached {
* If a read step was taken, then `read` captures the `Content`, the
* container type, and the content type.
*/
- predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) {
+ predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) {
parameterValueFlow0(p, node, read) and
if node instanceof CastingNode
then
// normal flow through
read = TReadStepTypesNone() and
- compatibleTypes(getNodeType(p), getNodeType(node))
+ compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node))
or
// getter
- compatibleTypes(read.getContentType(), getNodeType(node))
+ compatibleTypes(read.getContentType(), getNodeDataFlowType(node))
else any()
}
pragma[nomagic]
- private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) {
+ private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) {
p = node and
Cand::cand(p, _) and
read = TReadStepTypesNone()
@@ -447,7 +530,7 @@ private module Cached {
readStepWithTypes(mid, read.getContainerType(), read.getContent(), node,
read.getContentType()) and
Cand::parameterValueFlowReturnCand(p, _, true) and
- compatibleTypes(getNodeType(p), read.getContainerType())
+ compatibleTypes(getNodeDataFlowType(p), read.getContainerType())
)
or
parameterValueFlow0_0(TReadStepTypesNone(), p, node, read)
@@ -455,34 +538,32 @@ private module Cached {
pragma[nomagic]
private predicate parameterValueFlow0_0(
- ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read
+ ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read
) {
// flow through: no prior read
- exists(ArgumentNode arg |
+ exists(ArgNode arg |
parameterValueFlowArg(p, arg, mustBeNone) and
argumentValueFlowsThrough(arg, read, node)
)
or
// flow through: no read inside method
- exists(ArgumentNode arg |
+ exists(ArgNode arg |
parameterValueFlowArg(p, arg, read) and
argumentValueFlowsThrough(arg, mustBeNone, node)
)
}
pragma[nomagic]
- private predicate parameterValueFlowArg(
- ParameterNode p, ArgumentNode arg, ReadStepTypesOption read
- ) {
+ private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) {
parameterValueFlow(p, arg, read) and
Cand::argumentValueFlowsThroughCand(arg, _, _)
}
pragma[nomagic]
private predicate argumentValueFlowsThrough0(
- DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read
+ DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read
) {
- exists(ParameterNode param | viableParamArg(call, param, arg) |
+ exists(ParamNode param | viableParamArg(call, param, arg) |
parameterValueFlowReturn(param, kind, read)
)
}
@@ -496,18 +577,18 @@ private module Cached {
* container type, and the content type.
*/
pragma[nomagic]
- predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) {
+ predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) {
exists(DataFlowCall call, ReturnKind kind |
argumentValueFlowsThrough0(call, arg, kind, read) and
out = getAnOutNode(call, kind)
|
// normal flow through
read = TReadStepTypesNone() and
- compatibleTypes(getNodeType(arg), getNodeType(out))
+ compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out))
or
// getter
- compatibleTypes(getNodeType(arg), read.getContainerType()) and
- compatibleTypes(read.getContentType(), getNodeType(out))
+ compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and
+ compatibleTypes(read.getContentType(), getNodeDataFlowType(out))
)
}
@@ -516,7 +597,7 @@ private module Cached {
* 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) {
+ predicate getterStep(ArgNode arg, Content c, Node out) {
argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out)
}
@@ -529,7 +610,7 @@ private module Cached {
* container type, and the content type.
*/
private predicate parameterValueFlowReturn(
- ParameterNode p, ReturnKind kind, ReadStepTypesOption read
+ ParamNode p, ReturnKind kind, ReadStepTypesOption read
) {
exists(ReturnNode ret |
parameterValueFlow(p, ret, read) and
@@ -553,7 +634,7 @@ private module Cached {
private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) {
mayBenefitFromCallContext(call, callable)
or
- callable = call.getEnclosingCallable() and
+ callEnclosingCallable(call, callable) and
exists(viableCallableLambda(call, TDataFlowCallSome(_)))
}
@@ -611,7 +692,7 @@ private module Cached {
mayBenefitFromCallContextExt(call, _) and
c = viableCallableExt(call) and
ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and
- tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and
+ tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and
ctxtgts < tgts
)
}
@@ -635,8 +716,7 @@ private module Cached {
* Holds if `p` can flow to the pre-update node associated with post-update
* node `n`, in the same callable, using only value-preserving steps.
*/
- cached
- predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) {
+ private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) {
parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone())
}
@@ -644,9 +724,8 @@ private module Cached {
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
storeStep(node1, c, node2) and
- readStep(_, c, _) and
- contentType = getNodeType(node1) and
- containerType = getNodeType(node2)
+ contentType = getNodeDataFlowType(node1) and
+ containerType = getNodeDataFlowType(node2)
or
exists(Node n1, Node n2 |
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
@@ -654,12 +733,15 @@ private module Cached {
|
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1)
or
- readStep(n2, c, n1) and
- contentType = getNodeType(n1) and
- containerType = getNodeType(n2)
+ read(n2, c, n1) and
+ contentType = getNodeDataFlowType(n1) and
+ containerType = getNodeDataFlowType(n2)
)
}
+ cached
+ predicate read(Node node1, Content c, Node node2) { readStep(node1, c, node2) }
+
/**
* Holds if data can flow from `node1` to `node2` via a direct assignment to
* `f`.
@@ -678,8 +760,9 @@ private module Cached {
* are aliases. A typical example is a function returning `this`, implementing a fluent
* interface.
*/
- cached
- predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) {
+ private predicate reverseStepThroughInputOutputAlias(
+ PostUpdateNode fromNode, PostUpdateNode toNode
+ ) {
exists(Node fromPre, Node toPre |
fromPre = fromNode.getPreUpdateNode() and
toPre = toNode.getPreUpdateNode()
@@ -688,14 +771,20 @@ private module Cached {
// Does the language-specific simpleLocalFlowStep already model flow
// from function input to output?
fromPre = getAnOutNode(c, _) and
- toPre.(ArgumentNode).argumentOf(c, _) and
- simpleLocalFlowStep(toPre.(ArgumentNode), fromPre)
+ toPre.(ArgNode).argumentOf(c, _) and
+ simpleLocalFlowStep(toPre.(ArgNode), fromPre)
)
or
argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre)
)
}
+ cached
+ predicate simpleLocalFlowStepExt(Node node1, Node node2) {
+ simpleLocalFlowStep(node1, node2) or
+ reverseStepThroughInputOutputAlias(node1, node2)
+ }
+
/**
* Holds if the call context `call` either improves virtual dispatch in
* `callable` or if it allows us to prune unreachable nodes in `callable`.
@@ -704,7 +793,7 @@ private module Cached {
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
reducedViableImplInCallContext(_, callable, call)
or
- exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCall(n, call))
+ exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call))
}
cached
@@ -726,12 +815,12 @@ private module Cached {
cached
newtype TLocalFlowCallContext =
TAnyLocalCall() or
- TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) }
+ TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) }
cached
newtype TReturnKindExt =
TValueReturn(ReturnKind kind) or
- TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) }
+ TParamUpdate(int pos) { exists(ParamNode p | p.isParameterOf(_, pos)) }
cached
newtype TBooleanOption =
@@ -761,23 +850,15 @@ private module Cached {
* A `Node` at which a cast can occur such that the type should be checked.
*/
class CastingNode extends Node {
- CastingNode() {
- this instanceof ParameterNode or
- this instanceof CastNode or
- 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)
- }
+ CastingNode() { castingNode(this) }
}
private predicate readStepWithTypes(
Node n1, DataFlowType container, Content c, Node n2, DataFlowType content
) {
- readStep(n1, c, n2) and
- container = getNodeType(n1) and
- content = getNodeType(n2)
+ read(n1, c, n2) and
+ container = getNodeDataFlowType(n1) and
+ content = getNodeDataFlowType(n2)
}
private newtype TReadStepTypesOption =
@@ -854,7 +935,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall {
override string toString() { result = "CcSomeCall" }
override predicate relevantFor(DataFlowCallable callable) {
- exists(ParameterNode p | getNodeEnclosingCallable(p) = callable)
+ exists(ParamNode p | getNodeEnclosingCallable(p) = callable)
}
override predicate matchesCall(DataFlowCall call) { any() }
@@ -866,7 +947,7 @@ class CallContextReturn extends CallContextNoCall, TReturn {
}
override predicate relevantFor(DataFlowCallable callable) {
- exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable)
+ exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable))
}
}
@@ -899,7 +980,7 @@ class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall
}
private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) {
- exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCall(n, call))
+ exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call))
}
/**
@@ -913,26 +994,37 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable)
else result instanceof LocalCallContextAny
}
+/**
+ * The value of a parameter at function entry, viewed as a node in a data
+ * flow graph.
+ */
+class ParamNode extends Node {
+ ParamNode() { parameterNode(this, _, _) }
+
+ /**
+ * Holds if this node is the parameter of callable `c` at the specified
+ * (zero-based) position.
+ */
+ predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) }
+}
+
+/** A data-flow node that represents a call argument. */
+class ArgNode extends Node {
+ ArgNode() { argumentNode(this, _, _) }
+
+ /** Holds if this argument occurs at the given position in the given call. */
+ final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) }
+}
+
/**
* A node from which flow can return to the caller. This is either a regular
* `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter.
*/
class ReturnNodeExt extends Node {
- ReturnNodeExt() {
- this instanceof ReturnNode or
- parameterValueFlowsToPreUpdate(_, this)
- }
+ ReturnNodeExt() { returnNodeExt(this, _) }
/** Gets the kind of this returned value. */
- ReturnKindExt getKind() {
- result = TValueReturn(this.(ReturnNode).getKind())
- or
- exists(ParameterNode p, int pos |
- parameterValueFlowsToPreUpdate(p, this) and
- p.isParameterOf(_, pos) and
- result = TParamUpdate(pos)
- )
- }
+ ReturnKindExt getKind() { returnNodeExt(this, result) }
}
/**
@@ -940,11 +1032,7 @@ class ReturnNodeExt extends Node {
* or a post-update node associated with a call argument.
*/
class OutNodeExt extends Node {
- OutNodeExt() {
- this instanceof OutNode
- or
- this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode
- }
+ OutNodeExt() { outNodeExt(this) }
}
/**
@@ -957,7 +1045,7 @@ abstract class ReturnKindExt extends TReturnKindExt {
abstract string toString();
/** Gets a node corresponding to data flow out of `call`. */
- abstract OutNodeExt getAnOutNode(DataFlowCall call);
+ final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) }
}
class ValueReturnKind extends ReturnKindExt, TValueReturn {
@@ -968,10 +1056,6 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn {
ReturnKind getKind() { result = kind }
override string toString() { result = kind.toString() }
-
- override OutNodeExt getAnOutNode(DataFlowCall call) {
- result = getAnOutNode(call, this.getKind())
- }
}
class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
@@ -982,13 +1066,6 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
int getPosition() { result = pos }
override string toString() { result = "param update " + pos }
-
- override OutNodeExt getAnOutNode(DataFlowCall call) {
- exists(ArgumentNode arg |
- result.(PostUpdateNode).getPreUpdateNode() = arg and
- arg.argumentOf(call, this.getPosition())
- )
- }
}
/** A callable tagged with a relevant return kind. */
@@ -1015,10 +1092,13 @@ class ReturnPosition extends TReturnPosition0 {
*/
pragma[inline]
DataFlowCallable getNodeEnclosingCallable(Node n) {
- exists(Node n0 |
- pragma[only_bind_into](n0) = n and
- pragma[only_bind_into](result) = n0.getEnclosingCallable()
- )
+ nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result))
+}
+
+/** Gets the type of `n` used for type pruning. */
+pragma[inline]
+DataFlowType getNodeDataFlowType(Node n) {
+ nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result))
}
pragma[noinline]
@@ -1042,7 +1122,7 @@ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall
cc instanceof CallContextAny and callable = viableCallableExt(call)
or
exists(DataFlowCallable c0, DataFlowCall call0 |
- call0.getEnclosingCallable() = callable and
+ callEnclosingCallable(call0, callable) and
cc = TReturn(c0, call0) and
c0 = prunedViableImplInCallContextReverse(call0, call)
)
@@ -1063,8 +1143,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
result = viableCallableExt(call) and cc instanceof CallContextReturn
}
-predicate read = readStep/3;
-
/** An optional Boolean value. */
class BooleanOption extends TBooleanOption {
string toString() {
@@ -1116,7 +1194,7 @@ abstract class AccessPathFront extends TAccessPathFront {
TypedContent getHead() { this = TFrontHead(result) }
- predicate isClearedAt(Node n) { clearsContent(n, getHead().getContent()) }
+ predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) }
}
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll
index 4e1cd281488f..a55e65a81f69 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll
@@ -168,7 +168,13 @@ module Consistency {
msg = "ArgumentNode is missing PostUpdateNode."
}
- query predicate postWithInFlow(PostUpdateNode n, string msg) {
+ // This predicate helps the compiler forget that in some languages
+ // it is impossible for a `PostUpdateNode` to be the target of
+ // `simpleLocalFlowStep`.
+ private predicate isPostUpdateNode(Node n) { n instanceof PostUpdateNode or none() }
+
+ query predicate postWithInFlow(Node n, string msg) {
+ isPostUpdateNode(n) and
simpleLocalFlowStep(_, n) and
msg = "PostUpdateNode should not be the target of local flow."
}
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
index 9498e51e7e61..df59c128ad39 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
@@ -81,6 +81,12 @@ abstract class Configuration extends string {
*/
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
+ /**
+ * Holds if an arbitrary number of implicit read steps of content `c` may be
+ * taken at `node`.
+ */
+ predicate allowImplicitRead(Node node, Content c) { none() }
+
/**
* Gets the virtual dispatch branching limit when calculating field flow.
* This can be overridden to a smaller value to improve performance (a
@@ -182,78 +188,210 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
}
}
-private predicate inBarrier(Node node, Configuration config) {
- config.isBarrierIn(node) and
- config.isSource(node)
+private newtype TNodeEx =
+ TNodeNormal(Node n) or
+ TNodeImplicitRead(Node n, boolean hasRead) {
+ any(Configuration c).allowImplicitRead(n, _) and hasRead = [false, true]
+ }
+
+private class NodeEx extends TNodeEx {
+ string toString() {
+ result = this.asNode().toString()
+ or
+ exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]")
+ }
+
+ Node asNode() { this = TNodeNormal(result) }
+
+ predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) }
+
+ Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) }
+
+ pragma[nomagic]
+ private DataFlowCallable getEnclosingCallable0() {
+ nodeEnclosingCallable(this.projectToNode(), result)
+ }
+
+ pragma[inline]
+ DataFlowCallable getEnclosingCallable() {
+ pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result)
+ }
+
+ pragma[nomagic]
+ private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) }
+
+ pragma[inline]
+ DataFlowType getDataFlowType() {
+ pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result)
+ }
+
+ predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ }
}
-private predicate outBarrier(Node node, Configuration config) {
- config.isBarrierOut(node) and
- config.isSink(node)
+private class ArgNodeEx extends NodeEx {
+ ArgNodeEx() { this.asNode() instanceof ArgNode }
}
-private predicate fullBarrier(Node node, Configuration config) {
- config.isBarrier(node)
- or
- config.isBarrierIn(node) and
- not config.isSource(node)
- or
- config.isBarrierOut(node) and
- not config.isSink(node)
- or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- node = g.getAGuardedNode()
+private class ParamNodeEx extends NodeEx {
+ ParamNodeEx() { this.asNode() instanceof ParamNode }
+
+ predicate isParameterOf(DataFlowCallable c, int i) {
+ this.asNode().(ParamNode).isParameterOf(c, i)
+ }
+
+ int getPosition() { this.isParameterOf(_, result) }
+}
+
+private class RetNodeEx extends NodeEx {
+ RetNodeEx() { this.asNode() instanceof ReturnNodeExt }
+
+ ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) }
+
+ ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() }
+}
+
+private predicate inBarrier(NodeEx node, Configuration config) {
+ exists(Node n |
+ node.asNode() = n and
+ config.isBarrierIn(n) and
+ config.isSource(n)
+ )
+}
+
+private predicate outBarrier(NodeEx node, Configuration config) {
+ exists(Node n |
+ node.asNode() = n and
+ config.isBarrierOut(n) and
+ config.isSink(n)
+ )
+}
+
+private predicate fullBarrier(NodeEx node, Configuration config) {
+ exists(Node n | node.asNode() = n |
+ config.isBarrier(n)
+ or
+ config.isBarrierIn(n) and
+ not config.isSource(n)
+ or
+ config.isBarrierOut(n) and
+ not config.isSink(n)
+ or
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
)
}
+pragma[nomagic]
+private predicate sourceNode(NodeEx node, Configuration config) { config.isSource(node.asNode()) }
+
+pragma[nomagic]
+private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) }
+
/**
* Holds if data can flow in one local step from `node1` to `node2`.
*/
-private predicate localFlowStep(Node node1, Node node2, Configuration config) {
- (
- simpleLocalFlowStep(node1, node2) or
- reverseStepThroughInputOutputAlias(node1, node2)
- ) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ simpleLocalFlowStepExt(n1, n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+ or
+ exists(Node n |
+ config.allowImplicitRead(n, _) and
+ node1.asNode() = n and
+ node2.isImplicitReadNode(n, false)
+ )
}
/**
* Holds if the additional step from `node1` to `node2` does not jump between callables.
*/
-private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
- config.isAdditionalFlowStep(node1, node2) and
- getNodeEnclosingCallable(node1) = getNodeEnclosingCallable(node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ config.isAdditionalFlowStep(n1, n2) and
+ getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+ or
+ exists(Node n |
+ config.allowImplicitRead(n, _) and
+ node1.isImplicitReadNode(n, true) and
+ node2.asNode() = n
+ )
}
/**
* Holds if data can flow from `node1` to `node2` in a way that discards call contexts.
*/
-private predicate jumpStep(Node node1, Node node2, Configuration config) {
- jumpStep(node1, node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ jumpStepCached(n1, n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
}
/**
* Holds if the additional step from `node1` to `node2` jumps between callables.
*/
-private predicate additionalJumpStep(Node node1, Node node2, Configuration config) {
- config.isAdditionalFlowStep(node1, node2) and
- getNodeEnclosingCallable(node1) != getNodeEnclosingCallable(node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ config.isAdditionalFlowStep(n1, n2) and
+ getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+}
+
+private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ read(node1.asNode(), c, node2.asNode())
+ or
+ exists(Node n |
+ node2.isImplicitReadNode(n, true) and
+ node1.isImplicitReadNode(n, _) and
+ config.allowImplicitRead(n, c)
+ )
+}
+
+private predicate store(
+ NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
+) {
+ store(node1.asNode(), tc, node2.asNode(), contentType) and
+ read(_, tc.getContent(), _, config)
+}
+
+pragma[nomagic]
+private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) {
+ viableReturnPosOut(call, pos, out.asNode())
+}
+
+pragma[nomagic]
+private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) {
+ viableParamArg(call, p.asNode(), arg.asNode())
}
/**
@@ -277,39 +415,39 @@ private module Stage1 {
* The Boolean `cc` records whether the node is reached through an
* argument in a call.
*/
- predicate fwdFlow(Node node, Cc cc, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
not fullBarrier(node, config) and
(
- config.isSource(node) and
+ sourceNode(node, config) and
cc = false
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
localFlowStep(mid, node, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
additionalLocalFlowStep(mid, node, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, config) and
jumpStep(mid, node, config) and
cc = false
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, config) and
additionalJumpStep(mid, node, config) and
cc = false
)
or
// store
- exists(Node mid |
+ exists(NodeEx mid |
useFieldFlow(config) and
fwdFlow(mid, cc, config) and
- store(mid, _, node, _) and
+ store(mid, _, node, _, config) and
not outBarrier(mid, config)
)
or
@@ -321,9 +459,9 @@ private module Stage1 {
)
or
// flow into a callable
- exists(Node arg |
+ exists(NodeEx arg |
fwdFlow(arg, _, config) and
- viableParamArg(_, node, arg) and
+ viableParamArgEx(_, node, arg) and
cc = true
)
or
@@ -338,13 +476,13 @@ private module Stage1 {
)
}
- private predicate fwdFlow(Node node, Configuration config) { fwdFlow(node, _, config) }
+ private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
pragma[nomagic]
- private predicate fwdFlowRead(Content c, Node node, Cc cc, Configuration config) {
- exists(Node mid |
+ private predicate fwdFlowRead(Content c, NodeEx node, Cc cc, Configuration config) {
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
- read(mid, c, node)
+ read(mid, c, node, config)
)
}
@@ -353,33 +491,33 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate fwdFlowConsCand(Content c, Configuration config) {
- exists(Node mid, Node node, TypedContent tc |
+ exists(NodeEx mid, NodeEx node, TypedContent tc |
not fullBarrier(node, config) and
useFieldFlow(config) and
fwdFlow(mid, _, config) and
- store(mid, tc, node, _) and
+ store(mid, tc, node, _, config) and
c = tc.getContent()
)
}
pragma[nomagic]
private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc, Configuration config) {
- exists(ReturnNodeExt ret |
+ exists(RetNodeEx ret |
fwdFlow(ret, cc, config) and
- getReturnPosition(ret) = pos
+ ret.getReturnPosition() = pos
)
}
pragma[nomagic]
- private predicate fwdFlowOut(DataFlowCall call, Node out, Cc cc, Configuration config) {
+ private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
exists(ReturnPosition pos |
fwdFlowReturnPosition(pos, cc, config) and
- viableReturnPosOut(call, pos, out)
+ viableReturnPosOutEx(call, pos, out)
)
}
pragma[nomagic]
- private predicate fwdFlowOutFromArg(DataFlowCall call, Node out, Configuration config) {
+ private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out, Configuration config) {
fwdFlowOut(call, out, true, config)
}
@@ -388,9 +526,9 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
- exists(ArgumentNode arg |
+ exists(ArgNodeEx arg |
fwdFlow(arg, cc, config) and
- viableParamArg(call, _, arg)
+ viableParamArgEx(call, _, arg)
)
}
@@ -402,34 +540,34 @@ private module Stage1 {
* the enclosing callable in order to reach a sink.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and
fwdFlow(node, config)
}
pragma[nomagic]
- private predicate revFlow0(Node node, boolean toReturn, Configuration config) {
+ private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStep(node, mid, config) and
revFlow(mid, toReturn, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
additionalLocalFlowStep(node, mid, config) and
revFlow(mid, toReturn, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, config) and
toReturn = false
)
or
- exists(Node mid |
+ exists(NodeEx mid |
additionalJumpStep(node, mid, config) and
revFlow(mid, _, config) and
toReturn = false
@@ -442,8 +580,8 @@ private module Stage1 {
)
or
// read
- exists(Node mid, Content c |
- read(node, c, mid) and
+ exists(NodeEx mid, Content c |
+ read(node, c, mid, config) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
revFlow(mid, toReturn, pragma[only_bind_into](config))
)
@@ -460,7 +598,7 @@ private module Stage1 {
// flow out of a callable
exists(ReturnPosition pos |
revFlowOut(pos, config) and
- getReturnPosition(node) = pos and
+ node.(RetNodeEx).getReturnPosition() = pos and
toReturn = true
)
}
@@ -470,20 +608,20 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Content c, Configuration config) {
- exists(Node mid, Node node |
+ exists(NodeEx mid, NodeEx node |
fwdFlow(node, pragma[only_bind_into](config)) and
- read(node, c, mid) and
+ read(node, c, mid, config) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
revFlow(pragma[only_bind_into](mid), _, pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate revFlowStore(Content c, Node node, boolean toReturn, Configuration config) {
- exists(Node mid, TypedContent tc |
+ private predicate revFlowStore(Content c, NodeEx node, boolean toReturn, Configuration config) {
+ exists(NodeEx mid, TypedContent tc |
revFlow(mid, toReturn, pragma[only_bind_into](config)) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
- store(node, tc, mid, _) and
+ store(node, tc, mid, _, config) and
c = tc.getContent()
)
}
@@ -499,15 +637,15 @@ private module Stage1 {
pragma[nomagic]
predicate viableReturnPosOutNodeCandFwd1(
- DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+ DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
fwdFlowReturnPosition(pos, _, config) and
- viableReturnPosOut(call, pos, out)
+ viableReturnPosOutEx(call, pos, out)
}
pragma[nomagic]
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
- exists(DataFlowCall call, Node out |
+ exists(DataFlowCall call, NodeEx out |
revFlow(out, _, config) and
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
)
@@ -515,33 +653,35 @@ private module Stage1 {
pragma[nomagic]
predicate viableParamArgNodeCandFwd1(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+ DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
- viableParamArg(call, p, arg) and
+ viableParamArgEx(call, p, arg) and
fwdFlow(arg, config)
}
pragma[nomagic]
private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config
+ DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
revFlow(p, toReturn, config) and
viableParamArgNodeCandFwd1(call, p, arg, config)
)
}
pragma[nomagic]
- private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
+ private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg, Configuration config) {
revFlowIn(call, arg, true, config)
}
/**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`.
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
*/
pragma[nomagic]
private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) {
- exists(Node out |
+ exists(NodeEx out |
revFlow(out, toReturn, config) and
fwdFlowOutFromArg(call, out, config)
)
@@ -549,32 +689,33 @@ private module Stage1 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Content c |
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
revFlow(node2, pragma[only_bind_into](config)) and
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
c = tc.getContent() and
exists(ap1)
)
}
pragma[nomagic]
- predicate readStepCand(Node n1, Content c, Node n2, Configuration config) {
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) and
- read(n1, c, n2)
+ read(n1, c, n2, pragma[only_bind_into](config))
}
pragma[nomagic]
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow(node, toReturn, config) and exists(returnAp) and exists(ap)
}
- private predicate throughFlowNodeCand(Node node, Configuration config) {
+ private predicate throughFlowNodeCand(NodeEx node, Configuration config) {
revFlow(node, true, config) and
fwdFlow(node, true, config) and
not inBarrier(node, config) and
@@ -586,9 +727,9 @@ private module Stage1 {
private predicate returnFlowCallableNodeCand(
DataFlowCallable callable, ReturnKindExt kind, Configuration config
) {
- exists(ReturnNodeExt ret |
+ exists(RetNodeEx ret |
throughFlowNodeCand(ret, config) and
- callable = getNodeEnclosingCallable(ret) and
+ callable = ret.getEnclosingCallable() and
kind = ret.getKind()
)
}
@@ -597,50 +738,57 @@ private module Stage1 {
* Holds if flow may enter through `p` and reach a return node making `p` a
* candidate for the origin of a summary.
*/
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
- getNodeEnclosingCallable(p) = c and
+ p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself
- not exists(int pos |
- kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos)
- )
+ not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
+ )
+ }
+
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(ArgNodeEx arg, boolean toReturn |
+ revFlow(arg, toReturn, config) and
+ revFlowInToReturn(call, arg, config) and
+ revFlowIsReturned(call, toReturn, config)
)
}
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, config)) and
fields = count(Content f0 | fwdFlowConsCand(f0, config)) and
conscand = -1 and
- tuples = count(Node n, boolean b | fwdFlow(n, b, config))
+ tuples = count(NodeEx n, boolean b | fwdFlow(n, b, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, config)) and
fields = count(Content f0 | revFlowConsCand(f0, config)) and
conscand = -1 and
- tuples = count(Node n, boolean b | revFlow(n, b, config))
+ tuples = count(NodeEx n, boolean b | revFlow(n, b, config))
}
/* End: Stage 1 logic. */
}
pragma[noinline]
-private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) {
Stage1::revFlow(node2, config) and
localFlowStep(node1, node2, config)
}
pragma[noinline]
-private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) {
Stage1::revFlow(node2, config) and
additionalLocalFlowStep(node1, node2, config)
}
pragma[nomagic]
private predicate viableReturnPosOutNodeCand1(
- DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+ DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
Stage1::revFlow(out, config) and
Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out, config)
@@ -653,9 +801,9 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, ReturnNodeExt ret, Node out, Configuration config
+ DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
) {
- viableReturnPosOutNodeCand1(call, getReturnPosition(ret), out, config) and
+ viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
@@ -663,7 +811,7 @@ private predicate flowOutOfCallNodeCand1(
pragma[nomagic]
private predicate viableParamArgNodeCand1(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+ DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and
Stage1::revFlow(arg, config)
@@ -675,7 +823,7 @@ private predicate viableParamArgNodeCand1(
*/
pragma[nomagic]
private predicate flowIntoCallNodeCand1(
- DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, Configuration config
) {
viableParamArgNodeCand1(call, p, arg, config) and
Stage1::revFlow(p, config) and
@@ -688,9 +836,9 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
-private int branch(Node n1, Configuration conf) {
+private int branch(NodeEx n1, Configuration conf) {
result =
- strictcount(Node n |
+ strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -700,9 +848,9 @@ private int branch(Node n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
-private int join(Node n2, Configuration conf) {
+private int join(NodeEx n2, Configuration conf) {
result =
- strictcount(Node n |
+ strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -716,7 +864,7 @@ private int join(Node n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
exists(int b, int j |
@@ -735,8 +883,7 @@ private predicate flowOutOfCallNodeCand1(
*/
pragma[nomagic]
private predicate flowIntoCallNodeCand1(
- DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
exists(int b, int j |
@@ -762,7 +909,7 @@ private module Stage2 {
bindingset[result, ap]
private ApApprox getApprox(Ap ap) { any() }
- private ApNil getApNil(Node node) { PrevStage::revFlow(node, _) and exists(result) }
+ private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
@@ -805,10 +952,10 @@ private module Stage2 {
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { any() }
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
(
preservesValue = true and
@@ -829,10 +976,20 @@ private module Stage2 {
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
/* Begin: Stage 2 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -842,14 +999,14 @@ private module Stage2 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -860,7 +1017,7 @@ private module Stage2 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -868,7 +1025,7 @@ private module Stage2 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -899,19 +1056,17 @@ private module Stage2 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -935,7 +1090,7 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -944,32 +1099,29 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -979,9 +1131,15 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -992,7 +1150,7 @@ private module Stage2 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
)
@@ -1000,18 +1158,41 @@ private module Stage2 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1021,41 +1202,41 @@ private module Stage2 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -1071,20 +1252,18 @@ private module Stage2 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -1097,7 +1276,7 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -1111,7 +1290,7 @@ private module Stage2 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -1120,10 +1299,10 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -1132,13 +1311,12 @@ private module Stage2 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1146,9 +1324,14 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1160,7 +1343,7 @@ private module Stage2 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -1169,16 +1352,17 @@ private module Stage2 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -1187,7 +1371,7 @@ private module Stage2 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -1199,44 +1383,54 @@ private module Stage2 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 2 logic. */
}
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
- DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
@@ -1245,7 +1439,7 @@ private predicate flowOutOfCallNodeCand2(
pragma[nomagic]
private predicate flowIntoCallNodeCand2(
- DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
@@ -1258,10 +1452,10 @@ private module LocalFlowBigStep {
* A node where some checking is required, and hence the big-step relation
* is not allowed to step over.
*/
- private class FlowCheckNode extends Node {
+ private class FlowCheckNode extends NodeEx {
FlowCheckNode() {
- this instanceof CastNode or
- clearsContent(this, _)
+ castNode(this.asNode()) or
+ clearsContentCached(this.asNode(), _)
}
}
@@ -1269,16 +1463,16 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- predicate localFlowEntry(Node node, Configuration config) {
+ predicate localFlowEntry(NodeEx node, Configuration config) {
Stage2::revFlow(node, config) and
(
- config.isSource(node) or
+ sourceNode(node, config) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
- node instanceof ParameterNode or
- node instanceof OutNodeExt or
- store(_, _, node, _) or
- read(_, _, node) or
+ node instanceof ParamNodeEx or
+ node.asNode() instanceof OutNodeExt or
+ store(_, _, node, _, config) or
+ read(_, _, node, config) or
node instanceof FlowCheckNode
)
}
@@ -1287,23 +1481,25 @@ private module LocalFlowBigStep {
* Holds if `node` can be the last node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- private predicate localFlowExit(Node node, Configuration config) {
- exists(Node next | Stage2::revFlow(next, config) |
+ private predicate localFlowExit(NodeEx node, Configuration config) {
+ exists(NodeEx next | Stage2::revFlow(next, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
- store(node, _, next, _) or
- read(node, _, next)
+ store(node, _, next, _, config) or
+ read(node, _, next, config)
)
or
node instanceof FlowCheckNode
or
- config.isSink(node)
+ sinkNode(node, config)
}
pragma[noinline]
- private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
+ private predicate additionalLocalFlowStepNodeCand2(
+ NodeEx node1, NodeEx node2, Configuration config
+ ) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and
Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config))
@@ -1318,39 +1514,39 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
- Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config,
+ NodeEx node1, NodeEx node2, boolean preservesValue, DataFlowType t, Configuration config,
LocalCallContext cc
) {
- not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
+ not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
(
localFlowEntry(node1, pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
- t = getNodeType(node1)
+ t = node1.getDataFlowType() // irrelevant dummy value
or
additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
- t = getNodeType(node2)
+ t = node2.getDataFlowType()
) and
node1 != node2 and
- cc.relevantFor(getNodeEnclosingCallable(node1)) and
- not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
+ cc.relevantFor(node1.getEnclosingCallable()) and
+ not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
Stage2::revFlow(node2, pragma[only_bind_into](config))
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStepPlus(node1, mid, preservesValue, t, pragma[only_bind_into](config), cc) and
localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof FlowCheckNode and
Stage2::revFlow(node2, pragma[only_bind_into](config))
)
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStepPlus(node1, mid, _, _, pragma[only_bind_into](config), cc) and
additionalLocalFlowStepNodeCand2(mid, node2, config) and
not mid instanceof FlowCheckNode and
preservesValue = false and
- t = getNodeType(node2) and
+ t = node2.getDataFlowType() and
Stage2::revFlow(node2, pragma[only_bind_into](config))
)
)
@@ -1362,8 +1558,8 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, AccessPathFrontNil apf, Configuration config,
- LocalCallContext callContext
+ NodeEx node1, NodeEx node2, boolean preservesValue, AccessPathFrontNil apf,
+ Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, config)
@@ -1383,8 +1579,8 @@ private module Stage3 {
private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(Node node) {
- PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node))
+ private ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
@@ -1428,10 +1624,10 @@ private module Stage3 {
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { any() }
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, node2, preservesValue, ap, config, _) and exists(lcc)
}
@@ -1440,10 +1636,16 @@ private module Stage3 {
private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ pragma[nomagic]
+ private predicate clear(NodeEx node, Ap ap) { ap.isClearedAt(node.asNode()) }
+
+ pragma[nomagic]
+ private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
+
bindingset[node, ap]
- private predicate filter(Node node, Ap ap) {
- not ap.isClearedAt(node) and
- if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any()
+ private predicate filter(NodeEx node, Ap ap) {
+ not clear(node, ap) and
+ if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()
}
bindingset[ap, contentType]
@@ -1454,7 +1656,7 @@ private module Stage3 {
}
/* Begin: Stage 3 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
@@ -1465,6 +1667,16 @@ private module Stage3 {
)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -1474,21 +1686,21 @@ private module Stage3 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
pragma[nomagic]
- private predicate fwdFlow0(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -1499,7 +1711,7 @@ private module Stage3 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -1507,7 +1719,7 @@ private module Stage3 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -1538,19 +1750,17 @@ private module Stage3 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -1574,7 +1784,7 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -1583,32 +1793,29 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1618,9 +1825,15 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1631,7 +1844,7 @@ private module Stage3 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
@@ -1639,18 +1852,41 @@ private module Stage3 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1660,41 +1896,41 @@ private module Stage3 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -1710,20 +1946,18 @@ private module Stage3 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -1736,7 +1970,7 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -1750,7 +1984,7 @@ private module Stage3 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -1759,10 +1993,10 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -1771,13 +2005,12 @@ private module Stage3 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1785,9 +2018,14 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1799,7 +2037,7 @@ private module Stage3 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -1808,16 +2046,17 @@ private module Stage3 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -1826,7 +2065,7 @@ private module Stage3 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -1838,37 +2077,47 @@ private module Stage3 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 3 logic. */
}
@@ -1877,7 +2126,7 @@ private module Stage3 {
* Holds if `argApf` is recorded as the summary context for flow reaching `node`
* and remains relevant for the following pruning stage.
*/
-private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) {
+private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
@@ -1892,7 +2141,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
exists(int tails, int nodes, int apLimit, int tupleLimit |
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
- strictcount(Node n |
+ strictcount(NodeEx n |
Stage3::revFlow(n, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
@@ -2087,8 +2336,8 @@ private module Stage4 {
private ApApprox getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(Node node) {
- PrevStage::revFlow(node, _) and result = TNil(getNodeType(node))
+ private ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
@@ -2127,26 +2376,25 @@ private module Stage4 {
bindingset[innercc, inner, call]
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
resolveReturn(innercc, inner, call)
- or
- innercc.(CallContextCall).matchesCall(call)
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
localFlowEntry(node, config) and
- result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node))
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
}
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, node2, preservesValue, ap.getFront(), config, lcc)
}
pragma[nomagic]
private predicate flowOutOfCall(
- DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
@@ -2155,7 +2403,7 @@ private module Stage4 {
pragma[nomagic]
private predicate flowIntoCall(
- DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
@@ -2164,14 +2412,14 @@ private module Stage4 {
}
bindingset[node, ap]
- private predicate filter(Node node, Ap ap) { any() }
+ private predicate filter(NodeEx node, Ap ap) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
/* Begin: Stage 4 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
@@ -2182,6 +2430,16 @@ private module Stage4 {
)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -2191,21 +2449,21 @@ private module Stage4 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
pragma[nomagic]
- private predicate fwdFlow0(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -2216,7 +2474,7 @@ private module Stage4 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -2224,7 +2482,7 @@ private module Stage4 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -2255,19 +2513,17 @@ private module Stage4 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -2291,7 +2547,7 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -2300,32 +2556,29 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -2335,9 +2588,15 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -2348,7 +2607,7 @@ private module Stage4 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
@@ -2356,18 +2615,41 @@ private module Stage4 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -2377,41 +2659,41 @@ private module Stage4 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -2427,20 +2709,18 @@ private module Stage4 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -2453,7 +2733,7 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -2467,7 +2747,7 @@ private module Stage4 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -2476,10 +2756,10 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -2488,13 +2768,12 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -2502,9 +2781,14 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -2516,7 +2800,7 @@ private module Stage4 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -2525,16 +2809,17 @@ private module Stage4 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -2543,7 +2828,7 @@ private module Stage4 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -2555,37 +2840,47 @@ private module Stage4 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 4 logic. */
}
@@ -2595,18 +2890,18 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
}
-private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) {
+private predicate nodeMayUseSummary(NodeEx n, AccessPathApprox apa, Configuration config) {
exists(DataFlowCallable c, AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and
Stage4::revFlow(n, true, _, apa0, config) and
Stage4::fwdFlow(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
- getNodeEnclosingCallable(n) = c
+ n.getEnclosingCallable() = c
)
}
private newtype TSummaryCtx =
TSummaryCtxNone() or
- TSummaryCtxSome(ParameterNode p, AccessPath ap) {
+ TSummaryCtxSome(ParamNodeEx p, AccessPath ap) {
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _)
}
@@ -2627,7 +2922,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone {
/** A summary context from which a flow summary can be generated. */
private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
- private ParameterNode p;
+ private ParamNodeEx p;
private AccessPath ap;
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
@@ -2660,7 +2955,9 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
- strictcount(Node n | Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config))
+ strictcount(NodeEx n |
+ Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)
+ )
}
/**
@@ -2752,13 +3049,13 @@ private newtype TAccessPath =
}
private newtype TPathNode =
- TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
+ TPathNodeMid(NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
- ap = TAccessPathNil(getNodeType(node))
+ ap = TAccessPathNil(node.getDataFlowType())
or
// ... or a step from an existing PathNode to another node.
exists(PathNodeMid mid |
@@ -2767,12 +3064,12 @@ private newtype TPathNode =
Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config))
)
} or
- TPathNodeSink(Node node, Configuration config) {
- pragma[only_bind_into](config).isSink(node) and
+ TPathNodeSink(NodeEx node, Configuration config) {
+ sinkNode(node, pragma[only_bind_into](config)) and
Stage4::revFlow(node, pragma[only_bind_into](config)) and
(
// A sink that is also a source ...
- config.isSource(node)
+ sourceNode(node, config)
or
// ... or a sink that can be reached from a source
exists(PathNodeMid mid |
@@ -2973,15 +3270,17 @@ class PathNode extends TPathNode {
}
/** Gets the underlying `Node`. */
- Node getNode() { none() }
+ final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
private predicate isHidden() {
- nodeIsHidden(this.getNode()) and
+ hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
+ or
+ this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
}
private PathNode getASuccessorIfHidden() {
@@ -3003,6 +3302,8 @@ class PathNode extends TPathNode {
abstract private class PathNodeImpl extends PathNode {
abstract PathNode getASuccessorImpl();
+ abstract NodeEx getNodeEx();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3017,14 +3318,14 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
}
- override string toString() { result = this.getNode().toString() + ppAp() }
+ override string toString() { result = this.getNodeEx().toString() + ppAp() }
- override string toStringWithContext() { result = this.getNode().toString() + ppAp() + ppCtx() }
+ override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
@@ -3054,7 +3355,7 @@ module PathGraph {
* a `CallContext`, and a `Configuration`.
*/
private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
- Node node;
+ NodeEx node;
CallContext cc;
SummaryCtx sc;
AccessPath ap;
@@ -3062,7 +3363,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) }
- override Node getNode() { result = node }
+ override NodeEx getNodeEx() { result = node }
CallContext getCallContext() { result = cc }
@@ -3073,7 +3374,8 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
override Configuration getConfiguration() { result = config }
private PathNodeMid getSuccMid() {
- pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and
+ pathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx(),
+ result.getAp()) and
result.getConfiguration() = unbindConf(this.getConfiguration())
}
@@ -3084,7 +3386,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid, PathNodeSink sink |
mid = getSuccMid() and
- mid.getNode() = sink.getNode() and
+ mid.getNodeEx() = sink.getNodeEx() and
mid.getAp() instanceof AccessPathNil and
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
result = sink
@@ -3092,7 +3394,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
}
override predicate isSource() {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
@@ -3105,31 +3407,35 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
* excluding the `CallContext`.
*/
private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
- Node node;
+ NodeEx node;
Configuration config;
PathNodeSink() { this = TPathNodeSink(node, config) }
- override Node getNode() { result = node }
+ override NodeEx getNodeEx() { result = node }
override Configuration getConfiguration() { result = config }
override PathNode getASuccessorImpl() { none() }
- override predicate isSource() { config.isSource(node) }
+ override predicate isSource() { sourceNode(node, config) }
}
/**
* Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`.
*/
-private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
- exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
- midnode = mid.getNode() and
+private predicate pathStep(
+ PathNodeMid mid, NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap
+) {
+ exists(AccessPath ap0, NodeEx midnode, Configuration conf, LocalCallContext localCC |
+ midnode = mid.getNodeEx() and
conf = mid.getConfiguration() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
- localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and
+ localCC =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ midnode.getEnclosingCallable()) and
ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
@@ -3139,16 +3445,16 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
ap0 instanceof AccessPathNil
)
or
- jumpStep(mid.getNode(), node, mid.getConfiguration()) and
+ jumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
ap = mid.getAp()
or
- additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and
+ additionalJumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
mid.getAp() instanceof AccessPathNil and
- ap = TAccessPathNil(getNodeType(node))
+ ap = TAccessPathNil(node.getDataFlowType())
or
exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and
sc = mid.getSummaryCtx()
@@ -3165,20 +3471,20 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
pragma[nomagic]
private predicate pathReadStep(
- PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+ PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
- Stage4::readStepCand(mid.getNode(), tc.getContent(), node, mid.getConfiguration()) and
+ Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+ PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- Stage4::storeStepCand(mid.getNode(), _, tc, node, _, mid.getConfiguration()) and
+ Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
cc = mid.getCallContext()
}
@@ -3186,7 +3492,7 @@ private predicate pathOutOfCallable0(
PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa,
Configuration config
) {
- pos = getReturnPosition(mid.getNode()) and
+ pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and
innercc = mid.getCallContext() and
innercc instanceof CallContextNoCall and
apa = mid.getAp().getApprox() and
@@ -3209,10 +3515,10 @@ private predicate pathOutOfCallable1(
}
pragma[noinline]
-private Node getAnOutNodeFlow(
+private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
- result = kind.getAnOutNode(call) and
+ result.asNode() = kind.getAnOutNode(call) and
Stage4::revFlow(result, _, _, apa, config)
}
@@ -3221,7 +3527,7 @@ private Node getAnOutNodeFlow(
* is a return from a callable and is recorded by `cc`, if needed.
*/
pragma[noinline]
-private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
+private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) {
exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config |
pathOutOfCallable1(mid, call, kind, cc, apa, config) and
out = getAnOutNodeFlow(kind, call, apa, config)
@@ -3235,8 +3541,8 @@ pragma[noinline]
private predicate pathIntoArg(
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa
) {
- exists(ArgumentNode arg |
- arg = mid.getNode() and
+ exists(ArgNode arg |
+ arg = mid.getNodeEx().asNode() and
cc = mid.getCallContext() and
arg.argumentOf(call, i) and
ap = mid.getAp() and
@@ -3248,7 +3554,7 @@ pragma[noinline]
private predicate parameterCand(
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
Stage4::revFlow(p, _, _, apa, config) and
p.isParameterOf(callable, i)
)
@@ -3272,7 +3578,7 @@ private predicate pathIntoCallable0(
* respectively.
*/
private predicate pathIntoCallable(
- PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
+ PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
DataFlowCall call
) {
exists(int i, DataFlowCallable callable, AccessPath ap |
@@ -3297,8 +3603,8 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa,
Configuration config
) {
- exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
- mid.getNode() = ret and
+ exists(PathNodeMid mid, RetNodeEx ret, int pos |
+ mid.getNodeEx() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
@@ -3326,7 +3632,7 @@ private predicate pathThroughCallable0(
* The context `cc` is restored to its value prior to entering the callable.
*/
pragma[noinline]
-private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
+private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) {
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
pathThroughCallable0(call, mid, kind, cc, ap, apa) and
out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
@@ -3344,9 +3650,9 @@ private predicate flowsTo(
) {
flowsource.isSource() and
flowsource.getConfiguration() = configuration and
- flowsource.getNode() = source and
+ flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
- flowsink.getNode() = sink
+ flowsink.getNodeEx().asNode() = sink
}
/**
@@ -3361,13 +3667,13 @@ predicate flowsTo(Node source, Node sink, Configuration configuration) {
private predicate finalStats(boolean fwd, int nodes, int fields, int conscand, int tuples) {
fwd = true and
- nodes = count(Node n0 | exists(PathNode pn | pn.getNode() = n0)) and
+ nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
tuples = count(PathNode pn)
or
fwd = false and
- nodes = count(Node n0 | exists(PathNode pn | pn.getNode() = n0 and reach(pn))) and
+ nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
tuples = count(PathNode pn | reach(pn))
@@ -3404,19 +3710,19 @@ predicate stageStats(
private module FlowExploration {
private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2, Configuration config) {
- exists(Node node1, Node node2 |
+ exists(NodeEx node1, NodeEx node2 |
jumpStep(node1, node2, config)
or
additionalJumpStep(node1, node2, config)
or
// flow into callable
- viableParamArg(_, node2, node1)
+ viableParamArgEx(_, node2, node1)
or
// flow out of a callable
- viableReturnPosOut(_, getReturnPosition(node1), node2)
+ viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2)
|
- c1 = getNodeEnclosingCallable(node1) and
- c2 = getNodeEnclosingCallable(node2) and
+ c1 = node1.getEnclosingCallable() and
+ c2 = node2.getEnclosingCallable() and
c1 != c2
)
}
@@ -3568,7 +3874,7 @@ private module FlowExploration {
private newtype TSummaryCtx1 =
TSummaryCtx1None() or
- TSummaryCtx1Param(ParameterNode p)
+ TSummaryCtx1Param(ParamNodeEx p)
private newtype TSummaryCtx2 =
TSummaryCtx2None() or
@@ -3584,25 +3890,25 @@ private module FlowExploration {
private newtype TPartialPathNode =
TPartialPathNodeFwd(
- Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
+ NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
- distSrc(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
+ distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
- Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
+ NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
Configuration config
) {
- config.isSink(node) and
+ sinkNode(node, config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil() and
@@ -3611,23 +3917,23 @@ private module FlowExploration {
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
- not clearsContent(node, ap.getHead()) and
+ not clearsContentCached(node.asNode(), ap.getHead()) and
not fullBarrier(node, config) and
- distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
+ distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
}
pragma[nomagic]
private predicate partialPathNodeMk0(
- Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
+ NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
- not clearsContent(node, ap.getHead().getContent()) and
- if node instanceof CastingNode
- then compatibleTypes(getNodeType(node), ap.getType())
+ not clearsContentCached(node.asNode(), ap.getHead().getContent()) and
+ if node.asNode() instanceof CastingNode
+ then compatibleTypes(node.getDataFlowType(), ap.getType())
else any()
)
}
@@ -3637,13 +3943,15 @@ private module FlowExploration {
*/
class PartialPathNode extends TPartialPathNode {
/** Gets a textual representation of this element. */
- string toString() { result = this.getNode().toString() + this.ppAp() }
+ string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/**
* Gets a textual representation of this element, including a textual
* representation of the call context.
*/
- string toStringWithContext() { result = this.getNode().toString() + this.ppAp() + this.ppCtx() }
+ string toStringWithContext() {
+ result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
+ }
/**
* Holds if this element is at the specified location.
@@ -3655,11 +3963,16 @@ private module FlowExploration {
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the underlying `Node`. */
- Node getNode() { none() }
+ final Node getNode() { this.getNodeEx().projectToNode() = result }
+
+ private NodeEx getNodeEx() {
+ result = this.(PartialPathNodeFwd).getNodeEx() or
+ result = this.(PartialPathNodeRev).getNodeEx()
+ }
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
@@ -3672,7 +3985,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSourceDistance() {
- result = distSrc(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
+ result = distSrc(this.getNodeEx().getEnclosingCallable(), this.getConfiguration())
}
/**
@@ -3680,7 +3993,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSinkDistance() {
- result = distSink(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
+ result = distSink(this.getNodeEx().getEnclosingCallable(), this.getConfiguration())
}
private string ppAp() {
@@ -3712,7 +4025,7 @@ private module FlowExploration {
}
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
- Node node;
+ NodeEx node;
CallContext cc;
TSummaryCtx1 sc1;
TSummaryCtx2 sc2;
@@ -3721,7 +4034,7 @@ private module FlowExploration {
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
- override Node getNode() { result = node }
+ NodeEx getNodeEx() { result = node }
CallContext getCallContext() { result = cc }
@@ -3734,12 +4047,12 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodeFwd getASuccessor() {
- partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
+ partialPathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx1(),
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
}
predicate isSource() {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
@@ -3748,7 +4061,7 @@ private module FlowExploration {
}
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
- Node node;
+ NodeEx node;
TRevSummaryCtx1 sc1;
TRevSummaryCtx2 sc2;
RevPartialAccessPath ap;
@@ -3756,7 +4069,7 @@ private module FlowExploration {
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
- override Node getNode() { result = node }
+ NodeEx getNodeEx() { result = node }
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
@@ -3767,12 +4080,12 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodeRev getASuccessor() {
- revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
+ revPartialPathStep(result, this.getNodeEx(), this.getSummaryCtx1(), this.getSummaryCtx2(),
this.getAp(), this.getConfiguration())
}
predicate isSink() {
- config.isSink(node) and
+ sinkNode(node, config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil()
@@ -3780,40 +4093,40 @@ private module FlowExploration {
}
private predicate partialPathStep(
- PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
+ PartialPathNodeFwd mid, NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
- not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
+ not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and
(
- localFlowStep(mid.getNode(), node, config) and
+ localFlowStep(mid.getNodeEx(), node, config) and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalLocalFlowStep(mid.getNode(), node, config) and
+ additionalLocalFlowStep(mid.getNodeEx(), node, config) and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
config = mid.getConfiguration()
)
or
- jumpStep(mid.getNode(), node, config) and
+ jumpStep(mid.getNodeEx(), node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalJumpStep(mid.getNode(), node, config) and
+ additionalJumpStep(mid.getNodeEx(), node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
config = mid.getConfiguration()
or
partialPathStoreStep(mid, _, _, node, ap) and
@@ -3826,8 +4139,7 @@ private module FlowExploration {
partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, tc, ap0, config) and
- compatibleTypes(ap.getType(), getNodeType(node))
+ apConsFwd(ap, tc, ap0, config)
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -3846,12 +4158,13 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
+ PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, NodeEx node,
+ PartialAccessPath ap2
) {
- exists(Node midNode, DataFlowType contentType |
- midNode = mid.getNode() and
+ exists(NodeEx midNode, DataFlowType contentType |
+ midNode = mid.getNodeEx() and
ap1 = mid.getAp() and
- store(midNode, tc, node, contentType) and
+ store(midNode, tc, node, contentType, mid.getConfiguration()) and
ap2.getHead() = tc and
ap2.len() = unbindInt(ap1.len() + 1) and
compatibleTypes(ap1.getType(), contentType)
@@ -3870,15 +4183,15 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
+ PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, NodeEx node, CallContext cc,
Configuration config
) {
- exists(Node midNode |
- midNode = mid.getNode() and
+ exists(NodeEx midNode |
+ midNode = mid.getNodeEx() and
ap = mid.getAp() and
- read(midNode, tc.getContent(), node) and
+ read(midNode, tc.getContent(), node, pragma[only_bind_into](config)) and
ap.getHead() = tc and
- config = mid.getConfiguration() and
+ pragma[only_bind_into](config) = mid.getConfiguration() and
cc = mid.getCallContext()
)
}
@@ -3887,7 +4200,7 @@ private module FlowExploration {
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
Configuration config
) {
- pos = getReturnPosition(mid.getNode()) and
+ pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and
innercc = mid.getCallContext() and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
@@ -3910,12 +4223,12 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
- PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = kind.getAnOutNode(call)
+ out.asNode() = kind.getAnOutNode(call)
)
}
@@ -3924,8 +4237,8 @@ private module FlowExploration {
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
- exists(ArgumentNode arg |
- arg = mid.getNode() and
+ exists(ArgNode arg |
+ arg = mid.getNodeEx().asNode() and
cc = mid.getCallContext() and
arg.argumentOf(call, i) and
ap = mid.getAp() and
@@ -3943,7 +4256,7 @@ private module FlowExploration {
}
private predicate partialPathIntoCallable(
- PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
+ PartialPathNodeFwd mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc,
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
@@ -3964,8 +4277,8 @@ private module FlowExploration {
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
- exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
- mid.getNode() = ret and
+ exists(PartialPathNodeFwd mid, RetNodeEx ret |
+ mid.getNodeEx() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
@@ -3980,45 +4293,45 @@ private module FlowExploration {
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
- exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
- partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and
+ exists(CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
+ partialPathIntoCallable(mid, _, cc, innercc, sc1, sc2, call, _, config) and
paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config)
)
}
private predicate partialPathThroughCallable(
- PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
- out = kind.getAnOutNode(call)
+ out.asNode() = kind.getAnOutNode(call)
)
}
private predicate revPartialPathStep(
- PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
+ PartialPathNodeRev mid, NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
RevPartialAccessPath ap, Configuration config
) {
- localFlowStep(node, mid.getNode(), config) and
+ localFlowStep(node, mid.getNodeEx(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalLocalFlowStep(node, mid.getNode(), config) and
+ additionalLocalFlowStep(node, mid.getNodeEx(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
- jumpStep(node, mid.getNode(), config) and
+ jumpStep(node, mid.getNodeEx(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalJumpStep(node, mid.getNode(), config) and
+ additionalJumpStep(node, mid.getNodeEx(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
mid.getAp() instanceof RevPartialAccessPathNil and
@@ -4037,9 +4350,9 @@ private module FlowExploration {
apConsRev(ap, c, ap0, config)
)
or
- exists(ParameterNode p |
- mid.getNode() = p and
- viableParamArg(_, p, node) and
+ exists(ParamNodeEx p |
+ mid.getNodeEx() = p and
+ viableParamArgEx(_, p, node) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
sc1 = TRevSummaryCtx1None() and
@@ -4050,7 +4363,7 @@ private module FlowExploration {
or
exists(ReturnPosition pos |
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
- pos = getReturnPosition(node)
+ pos = getReturnPosition(node.asNode())
)
or
revPartialPathThroughCallable(mid, node, ap, config) and
@@ -4060,12 +4373,13 @@ private module FlowExploration {
pragma[inline]
private predicate revPartialPathReadStep(
- PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
+ PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, NodeEx node,
+ RevPartialAccessPath ap2
) {
- exists(Node midNode |
- midNode = mid.getNode() and
+ exists(NodeEx midNode |
+ midNode = mid.getNodeEx() and
ap1 = mid.getAp() and
- read(node, c, midNode) and
+ read(node, c, midNode, mid.getConfiguration()) and
ap2.getHead() = c and
ap2.len() = unbindInt(ap1.len() + 1)
)
@@ -4083,12 +4397,12 @@ private module FlowExploration {
pragma[nomagic]
private predicate revPartialPathStoreStep(
- PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
+ PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, NodeEx node, Configuration config
) {
- exists(Node midNode, TypedContent tc |
- midNode = mid.getNode() and
+ exists(NodeEx midNode, TypedContent tc |
+ midNode = mid.getNodeEx() and
ap = mid.getAp() and
- store(node, tc, midNode, _) and
+ store(node, tc, midNode, _, config) and
ap.getHead() = c and
config = mid.getConfiguration() and
tc.getContent() = c
@@ -4100,9 +4414,9 @@ private module FlowExploration {
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
DataFlowCall call, RevPartialAccessPath ap, Configuration config
) {
- exists(Node out |
- mid.getNode() = out and
- viableReturnPosOut(call, pos, out) and
+ exists(NodeEx out |
+ mid.getNodeEx() = out and
+ viableReturnPosOutEx(call, pos, out) and
sc1 = TRevSummaryCtx1Some(pos) and
sc2 = TRevSummaryCtx2Some(ap) and
ap = mid.getAp() and
@@ -4115,9 +4429,9 @@ private module FlowExploration {
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
Configuration config
) {
- exists(PartialPathNodeRev mid, ParameterNode p |
- mid.getNode() = p and
- p.isParameterOf(_, pos) and
+ exists(PartialPathNodeRev mid, ParamNodeEx p |
+ mid.getNodeEx() = p and
+ p.getPosition() = pos and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
@@ -4138,11 +4452,11 @@ private module FlowExploration {
pragma[nomagic]
private predicate revPartialPathThroughCallable(
- PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
+ PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, int pos |
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
- node.argumentOf(call, pos)
+ node.asNode().(ArgNode).argumentOf(call, pos)
)
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
index 835e6c464518..e64f82775284 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
@@ -133,46 +133,6 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
*/
predicate jumpStep(Node n1, Node n2) { none() }
-private newtype TContent =
- TFieldContent(Field f) or
- TCollectionContent() or
- TArrayContent()
-
-/**
- * A reference contained in an object. Examples include instance fields, the
- * contents of a collection object, or the contents of an array.
- */
-class Content extends TContent {
- /** Gets a textual representation of this element. */
- abstract string toString();
-
- 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
- }
-}
-
-private class FieldContent extends Content, TFieldContent {
- Field f;
-
- FieldContent() { this = TFieldContent(f) }
-
- Field getField() { result = f }
-
- override string toString() { result = f.toString() }
-
- override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
- f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
- }
-}
-
-private class CollectionContent extends Content, TCollectionContent {
- override string toString() { result = "collection" }
-}
-
-private class ArrayContent extends Content, TArrayContent {
- override string toString() { result = "array" }
-}
-
/**
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
* Thus, `node2` references an object with a field `f` that contains the
@@ -321,5 +281,5 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c)
/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() }
-/** Extra data-flow steps needed for lamba flow analysis. */
+/** Extra data-flow steps needed for lambda flow analysis. */
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
index 690f24fc59a9..0d723ac05b6b 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
@@ -526,7 +526,6 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) {
* This is the local flow predicate that's used as a building block in global
* data flow. It may have less flow than the `localFlowStep` predicate.
*/
-cached
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
// Expr -> Expr
exprToExprStep_nocfg(nodeFrom.asExpr(), nodeTo.asExpr())
@@ -694,7 +693,12 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) {
fromExpr = call.getQualifier()
) and
call.getTarget() = f and
- outModel.isReturnValue()
+ // AST dataflow treats a reference as if it were the referred-to object, while the dataflow
+ // models treat references as pointers. If the return type of the call is a reference, then
+ // look for data flow the the referred-to object, rather than the reference itself.
+ if call.getType().getUnspecifiedType() instanceof ReferenceType
+ then outModel.isReturnValueDeref()
+ else outModel.isReturnValue()
)
)
}
@@ -764,6 +768,50 @@ VariableAccess getAnAccessToAssignedVariable(Expr assign) {
)
}
+private newtype TContent =
+ TFieldContent(Field f) or
+ TCollectionContent() or
+ TArrayContent()
+
+/**
+ * A description of the way data may be stored inside an object. Examples
+ * include instance fields, the contents of a collection object, or the contents
+ * of an array.
+ */
+class Content extends TContent {
+ /** Gets a textual representation of this element. */
+ abstract string toString();
+
+ 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
+ }
+}
+
+/** A reference through an instance field. */
+class FieldContent extends Content, TFieldContent {
+ Field f;
+
+ FieldContent() { this = TFieldContent(f) }
+
+ Field getField() { result = f }
+
+ override string toString() { result = f.toString() }
+
+ override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
+ f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
+ }
+}
+
+/** A reference through an array. */
+private class ArrayContent extends Content, TArrayContent {
+ override string toString() { result = "[]" }
+}
+
+/** A reference through the contents of some collection-like container. */
+private class CollectionContent extends Content, TCollectionContent {
+ override string toString() { result = "" }
+}
+
/**
* A guard that validates some expression.
*
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
index 591f461c8eb9..eae16081974c 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
@@ -34,6 +34,13 @@ predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
localAdditionalTaintStep(src, sink)
}
+/**
+ * Holds if default `TaintTracking::Configuration`s should allow implicit reads
+ * of `c` at sinks and inputs to additional taint steps.
+ */
+bindingset[node]
+predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::Content c) { none() }
+
/**
* Holds if `node` should be a sanitizer in all global taint flow configurations
* but not in local taint.
@@ -45,6 +52,7 @@ predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
* local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent
* different objects.
*/
+cached
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// Taint can flow through expressions that alter the value but preserve
// more than one bit of it _or_ expressions that follow data through
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
index b509fad9cd20..f4f73b8247c5 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
@@ -105,6 +105,11 @@ abstract class Configuration extends DataFlow::Configuration {
defaultAdditionalTaintStep(node1, node2)
}
+ override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) {
+ (this.isSink(node) or this.isAdditionalTaintStep(node, _)) and
+ defaultImplicitTaintRead(node, c)
+ }
+
/**
* Holds if taint may flow from `source` to `sink` for this configuration.
*/
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
index b509fad9cd20..f4f73b8247c5 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
@@ -105,6 +105,11 @@ abstract class Configuration extends DataFlow::Configuration {
defaultAdditionalTaintStep(node1, node2)
}
+ override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) {
+ (this.isSink(node) or this.isAdditionalTaintStep(node, _)) and
+ defaultImplicitTaintRead(node, c)
+ }
+
/**
* Holds if taint may flow from `source` to `sink` for this configuration.
*/
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll
index e02a72fe680a..f77518c2f561 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll
@@ -850,6 +850,24 @@ class NewOrNewArrayExpr extends Expr, @any_new_expr {
this.getAllocatorCall()
.getArgument(this.getAllocator().(OperatorNewAllocationFunction).getPlacementArgument())
}
+
+ /**
+ * For `operator new`, this gets the call or expression that initializes the allocated object, if any.
+ *
+ * As examples, for `new int(4)`, this will be `4`, and for `new std::vector(4)`, this will
+ * be a call to the constructor `std::vector::vector(size_t)` with `4` as an argument.
+ *
+ * For `operator new[]`, this gets the call or expression that initializes the first element of the
+ * array, if any.
+ *
+ * This will either be a call to the default constructor for the array's element type (as
+ * in `new std::string[10]`), or a literal zero for arrays of scalars which are zero-initialized
+ * due to extra parentheses (as in `new int[10]()`).
+ *
+ * At runtime, the constructor will be called once for each element in the array, but the
+ * constructor call only exists once in the AST.
+ */
+ final Expr getInitializer() { result = this.getChild(1) }
}
/**
@@ -871,14 +889,6 @@ class NewExpr extends NewOrNewArrayExpr, @new_expr {
override Type getAllocatedType() {
new_allocated_type(underlyingElement(this), unresolveElement(result))
}
-
- /**
- * Gets the call or expression that initializes the allocated object, if any.
- *
- * As examples, for `new int(4)`, this will be `4`, and for `new std::vector(4)`, this will
- * be a call to the constructor `std::vector::vector(size_t)` with `4` as an argument.
- */
- Expr getInitializer() { result = this.getChild(1) }
}
/**
@@ -909,18 +919,6 @@ class NewArrayExpr extends NewOrNewArrayExpr, @new_array_expr {
result = getType().getUnderlyingType().(PointerType).getBaseType()
}
- /**
- * Gets the call or expression that initializes the first element of the array, if any.
- *
- * This will either be a call to the default constructor for the array's element type (as
- * in `new std::string[10]`), or a literal zero for arrays of scalars which are zero-initialized
- * due to extra parentheses (as in `new int[10]()`).
- *
- * At runtime, the constructor will be called once for each element in the array, but the
- * constructor call only exists once in the AST.
- */
- Expr getInitializer() { result = this.getChild(1) }
-
/**
* Gets the extent of the non-constant array dimension, if any.
*
@@ -1271,7 +1269,8 @@ private predicate convparents(Expr child, int idx, Element parent) {
)
}
-// Pulled out for performance. See QL-796.
+// Pulled out for performance. See
+// https://github.com/github/codeql-coreql-team/issues/1044.
private predicate hasNoConversions(Expr e) { not e.hasConversion() }
/**
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
index 3092031cbc7d..49d11a7e3cc6 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
@@ -165,106 +165,133 @@ private predicate nodeIsBarrierEqualityCandidate(
any(IRGuardCondition guard).ensuresEq(access, _, _, node.asInstruction().getBlock(), true)
}
-private predicate nodeIsBarrier(DataFlow::Node node) {
- exists(Variable checkedVar |
- readsVariable(node.asInstruction(), checkedVar) and
- hasUpperBoundsCheck(checkedVar)
- )
- or
- exists(Variable checkedVar, Operand access |
- /*
- * This node is guarded by a condition that forces the accessed variable
- * to equal something else. For example:
- * ```
- * x = taintsource()
- * if (x == 10) {
- * taintsink(x); // not considered tainted
- * }
- * ```
- */
+cached
+private module Cached {
+ cached
+ predicate nodeIsBarrier(DataFlow::Node node) {
+ exists(Variable checkedVar |
+ readsVariable(node.asInstruction(), checkedVar) and
+ hasUpperBoundsCheck(checkedVar)
+ )
+ or
+ exists(Variable checkedVar, Operand access |
+ /*
+ * This node is guarded by a condition that forces the accessed variable
+ * to equal something else. For example:
+ * ```
+ * x = taintsource()
+ * if (x == 10) {
+ * taintsink(x); // not considered tainted
+ * }
+ * ```
+ */
+
+ nodeIsBarrierEqualityCandidate(node, access, checkedVar) and
+ readsVariable(access.getDef(), checkedVar)
+ )
+ }
- nodeIsBarrierEqualityCandidate(node, access, checkedVar) and
- readsVariable(access.getDef(), checkedVar)
- )
-}
+ cached
+ predicate nodeIsBarrierIn(DataFlow::Node node) {
+ // don't use dataflow into taint sources, as this leads to duplicate results.
+ exists(Expr source | isUserInput(source, _) |
+ node = DataFlow::exprNode(source)
+ or
+ // This case goes together with the similar (but not identical) rule in
+ // `getNodeForSource`.
+ node = DataFlow::definitionByReferenceNodeFromArgument(source)
+ )
+ or
+ // don't use dataflow into binary instructions if both operands are unpredictable
+ exists(BinaryInstruction iTo |
+ iTo = node.asInstruction() and
+ not predictableInstruction(iTo.getLeft()) and
+ not predictableInstruction(iTo.getRight()) and
+ // propagate taint from either the pointer or the offset, regardless of predictability
+ not iTo instanceof PointerArithmeticInstruction
+ )
+ or
+ // don't use dataflow through calls to pure functions if two or more operands
+ // are unpredictable
+ exists(Instruction iFrom1, Instruction iFrom2, CallInstruction iTo |
+ iTo = node.asInstruction() and
+ isPureFunction(iTo.getStaticCallTarget().getName()) and
+ iFrom1 = iTo.getAnArgument() and
+ iFrom2 = iTo.getAnArgument() and
+ not predictableInstruction(iFrom1) and
+ not predictableInstruction(iFrom2) and
+ iFrom1 != iFrom2
+ )
+ }
-private predicate nodeIsBarrierIn(DataFlow::Node node) {
- // don't use dataflow into taint sources, as this leads to duplicate results.
- exists(Expr source | isUserInput(source, _) |
- node = DataFlow::exprNode(source)
+ cached
+ Element adjustedSink(DataFlow::Node sink) {
+ // TODO: is it more appropriate to use asConvertedExpr here and avoid
+ // `getConversion*`? Or will that cause us to miss some cases where there's
+ // flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to
+ // pretend there was flow to the converted `Expr` for the sake of
+ // compatibility.
+ sink.asExpr().getConversion*() = result
or
- // This case goes together with the similar (but not identical) rule in
- // `getNodeForSource`.
- node = DataFlow::definitionByReferenceNodeFromArgument(source)
- )
- or
- // don't use dataflow into binary instructions if both operands are unpredictable
- exists(BinaryInstruction iTo |
- iTo = node.asInstruction() and
- not predictableInstruction(iTo.getLeft()) and
- not predictableInstruction(iTo.getRight()) and
- // propagate taint from either the pointer or the offset, regardless of predictability
- not iTo instanceof PointerArithmeticInstruction
- )
- or
- // don't use dataflow through calls to pure functions if two or more operands
- // are unpredictable
- exists(Instruction iFrom1, Instruction iFrom2, CallInstruction iTo |
- iTo = node.asInstruction() and
- isPureFunction(iTo.getStaticCallTarget().getName()) and
- iFrom1 = iTo.getAnArgument() and
- iFrom2 = iTo.getAnArgument() and
- not predictableInstruction(iFrom1) and
- not predictableInstruction(iFrom2) and
- iFrom1 != iFrom2
- )
-}
+ // For compatibility, send flow from arguments to parameters, even for
+ // functions with no body.
+ exists(FunctionCall call, int i |
+ sink.asExpr() = call.getArgument(i) and
+ result = resolveCall(call).getParameter(i)
+ )
+ or
+ // For compatibility, send flow into a `Variable` if there is flow to any
+ // Load or Store of that variable.
+ exists(CopyInstruction copy |
+ copy.getSourceValue() = sink.asInstruction() and
+ (
+ readsVariable(copy, result) or
+ writesVariable(copy, result)
+ ) and
+ not hasUpperBoundsCheck(result)
+ )
+ or
+ // For compatibility, send flow into a `NotExpr` even if it's part of a
+ // short-circuiting condition and thus might get skipped.
+ result.(NotExpr).getOperand() = sink.asExpr()
+ or
+ // Taint postfix and prefix crement operations when their operand is tainted.
+ result.(CrementOperation).getAnOperand() = sink.asExpr()
+ or
+ // Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted.
+ result.(AssignOperation).getAnOperand() = sink.asExpr()
+ or
+ result =
+ sink.asOperand()
+ .(SideEffectOperand)
+ .getUse()
+ .(ReadSideEffectInstruction)
+ .getArgumentDef()
+ .getUnconvertedResultExpression()
+ }
-private Element adjustedSink(DataFlow::Node sink) {
- // TODO: is it more appropriate to use asConvertedExpr here and avoid
- // `getConversion*`? Or will that cause us to miss some cases where there's
- // flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to
- // pretend there was flow to the converted `Expr` for the sake of
- // compatibility.
- sink.asExpr().getConversion*() = result
- or
- // For compatibility, send flow from arguments to parameters, even for
- // functions with no body.
- exists(FunctionCall call, int i |
- sink.asExpr() = call.getArgument(i) and
- result = resolveCall(call).getParameter(i)
- )
- or
- // For compatibility, send flow into a `Variable` if there is flow to any
- // Load or Store of that variable.
- exists(CopyInstruction copy |
- copy.getSourceValue() = sink.asInstruction() and
- (
- readsVariable(copy, result) or
- writesVariable(copy, result)
- ) and
- not hasUpperBoundsCheck(result)
- )
- or
- // For compatibility, send flow into a `NotExpr` even if it's part of a
- // short-circuiting condition and thus might get skipped.
- result.(NotExpr).getOperand() = sink.asExpr()
- or
- // Taint postfix and prefix crement operations when their operand is tainted.
- result.(CrementOperation).getAnOperand() = sink.asExpr()
- or
- // Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted.
- result.(AssignOperation).getAnOperand() = sink.asExpr()
- or
- result =
- sink.asOperand()
- .(SideEffectOperand)
- .getUse()
- .(ReadSideEffectInstruction)
- .getArgumentDef()
- .getUnconvertedResultExpression()
+ /**
+ * Step to return value of a modeled function when an input taints the
+ * dereference of the return value.
+ */
+ cached
+ predicate additionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
+ exists(CallInstruction call, Function func, FunctionInput modelIn, FunctionOutput modelOut |
+ n1.asOperand() = callInput(call, modelIn) and
+ (
+ func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
+ or
+ func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
+ ) and
+ call.getStaticCallTarget() = func and
+ modelOut.isReturnValueDeref() and
+ call = n2.asInstruction()
+ )
+ }
}
+private import Cached
+
/**
* Holds if `tainted` may contain taint from `source`.
*
@@ -402,19 +429,7 @@ module TaintedWithPath {
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
)
or
- // Step to return value of a modeled function when an input taints the
- // dereference of the return value
- exists(CallInstruction call, Function func, FunctionInput modelIn, FunctionOutput modelOut |
- n1.asOperand() = callInput(call, modelIn) and
- (
- func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
- or
- func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
- ) and
- call.getStaticCallTarget() = func and
- modelOut.isReturnValueDeref() and
- call = n2.asInstruction()
- )
+ additionalTaintStep(n1, n2)
}
override predicate isSanitizer(DataFlow::Node node) {
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll
index e927634fec23..99d8555f8cad 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll
@@ -2,11 +2,14 @@ private import cpp
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
+private import DataFlowImplCommon as DataFlowImplCommon
/**
* Gets a function that might be called by `call`.
*/
+cached
Function viableCallable(CallInstruction call) {
+ DataFlowImplCommon::forceCachingInSameStage() and
result = call.getStaticCallTarget()
or
// If the target of the call does not have a body in the snapshot, it might
@@ -43,7 +46,6 @@ private module VirtualDispatch {
abstract DataFlow::Node getDispatchValue();
/** Gets a candidate target for this call. */
- cached
abstract Function resolve();
/**
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
index 9498e51e7e61..df59c128ad39 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
@@ -81,6 +81,12 @@ abstract class Configuration extends string {
*/
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
+ /**
+ * Holds if an arbitrary number of implicit read steps of content `c` may be
+ * taken at `node`.
+ */
+ predicate allowImplicitRead(Node node, Content c) { none() }
+
/**
* Gets the virtual dispatch branching limit when calculating field flow.
* This can be overridden to a smaller value to improve performance (a
@@ -182,78 +188,210 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
}
}
-private predicate inBarrier(Node node, Configuration config) {
- config.isBarrierIn(node) and
- config.isSource(node)
+private newtype TNodeEx =
+ TNodeNormal(Node n) or
+ TNodeImplicitRead(Node n, boolean hasRead) {
+ any(Configuration c).allowImplicitRead(n, _) and hasRead = [false, true]
+ }
+
+private class NodeEx extends TNodeEx {
+ string toString() {
+ result = this.asNode().toString()
+ or
+ exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]")
+ }
+
+ Node asNode() { this = TNodeNormal(result) }
+
+ predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) }
+
+ Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) }
+
+ pragma[nomagic]
+ private DataFlowCallable getEnclosingCallable0() {
+ nodeEnclosingCallable(this.projectToNode(), result)
+ }
+
+ pragma[inline]
+ DataFlowCallable getEnclosingCallable() {
+ pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result)
+ }
+
+ pragma[nomagic]
+ private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) }
+
+ pragma[inline]
+ DataFlowType getDataFlowType() {
+ pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result)
+ }
+
+ predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ }
}
-private predicate outBarrier(Node node, Configuration config) {
- config.isBarrierOut(node) and
- config.isSink(node)
+private class ArgNodeEx extends NodeEx {
+ ArgNodeEx() { this.asNode() instanceof ArgNode }
}
-private predicate fullBarrier(Node node, Configuration config) {
- config.isBarrier(node)
- or
- config.isBarrierIn(node) and
- not config.isSource(node)
- or
- config.isBarrierOut(node) and
- not config.isSink(node)
- or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- node = g.getAGuardedNode()
+private class ParamNodeEx extends NodeEx {
+ ParamNodeEx() { this.asNode() instanceof ParamNode }
+
+ predicate isParameterOf(DataFlowCallable c, int i) {
+ this.asNode().(ParamNode).isParameterOf(c, i)
+ }
+
+ int getPosition() { this.isParameterOf(_, result) }
+}
+
+private class RetNodeEx extends NodeEx {
+ RetNodeEx() { this.asNode() instanceof ReturnNodeExt }
+
+ ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) }
+
+ ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() }
+}
+
+private predicate inBarrier(NodeEx node, Configuration config) {
+ exists(Node n |
+ node.asNode() = n and
+ config.isBarrierIn(n) and
+ config.isSource(n)
+ )
+}
+
+private predicate outBarrier(NodeEx node, Configuration config) {
+ exists(Node n |
+ node.asNode() = n and
+ config.isBarrierOut(n) and
+ config.isSink(n)
+ )
+}
+
+private predicate fullBarrier(NodeEx node, Configuration config) {
+ exists(Node n | node.asNode() = n |
+ config.isBarrier(n)
+ or
+ config.isBarrierIn(n) and
+ not config.isSource(n)
+ or
+ config.isBarrierOut(n) and
+ not config.isSink(n)
+ or
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
)
}
+pragma[nomagic]
+private predicate sourceNode(NodeEx node, Configuration config) { config.isSource(node.asNode()) }
+
+pragma[nomagic]
+private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) }
+
/**
* Holds if data can flow in one local step from `node1` to `node2`.
*/
-private predicate localFlowStep(Node node1, Node node2, Configuration config) {
- (
- simpleLocalFlowStep(node1, node2) or
- reverseStepThroughInputOutputAlias(node1, node2)
- ) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ simpleLocalFlowStepExt(n1, n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+ or
+ exists(Node n |
+ config.allowImplicitRead(n, _) and
+ node1.asNode() = n and
+ node2.isImplicitReadNode(n, false)
+ )
}
/**
* Holds if the additional step from `node1` to `node2` does not jump between callables.
*/
-private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
- config.isAdditionalFlowStep(node1, node2) and
- getNodeEnclosingCallable(node1) = getNodeEnclosingCallable(node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ config.isAdditionalFlowStep(n1, n2) and
+ getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+ or
+ exists(Node n |
+ config.allowImplicitRead(n, _) and
+ node1.isImplicitReadNode(n, true) and
+ node2.asNode() = n
+ )
}
/**
* Holds if data can flow from `node1` to `node2` in a way that discards call contexts.
*/
-private predicate jumpStep(Node node1, Node node2, Configuration config) {
- jumpStep(node1, node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ jumpStepCached(n1, n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
}
/**
* Holds if the additional step from `node1` to `node2` jumps between callables.
*/
-private predicate additionalJumpStep(Node node1, Node node2, Configuration config) {
- config.isAdditionalFlowStep(node1, node2) and
- getNodeEnclosingCallable(node1) != getNodeEnclosingCallable(node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ config.isAdditionalFlowStep(n1, n2) and
+ getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+}
+
+private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ read(node1.asNode(), c, node2.asNode())
+ or
+ exists(Node n |
+ node2.isImplicitReadNode(n, true) and
+ node1.isImplicitReadNode(n, _) and
+ config.allowImplicitRead(n, c)
+ )
+}
+
+private predicate store(
+ NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
+) {
+ store(node1.asNode(), tc, node2.asNode(), contentType) and
+ read(_, tc.getContent(), _, config)
+}
+
+pragma[nomagic]
+private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) {
+ viableReturnPosOut(call, pos, out.asNode())
+}
+
+pragma[nomagic]
+private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) {
+ viableParamArg(call, p.asNode(), arg.asNode())
}
/**
@@ -277,39 +415,39 @@ private module Stage1 {
* The Boolean `cc` records whether the node is reached through an
* argument in a call.
*/
- predicate fwdFlow(Node node, Cc cc, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
not fullBarrier(node, config) and
(
- config.isSource(node) and
+ sourceNode(node, config) and
cc = false
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
localFlowStep(mid, node, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
additionalLocalFlowStep(mid, node, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, config) and
jumpStep(mid, node, config) and
cc = false
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, config) and
additionalJumpStep(mid, node, config) and
cc = false
)
or
// store
- exists(Node mid |
+ exists(NodeEx mid |
useFieldFlow(config) and
fwdFlow(mid, cc, config) and
- store(mid, _, node, _) and
+ store(mid, _, node, _, config) and
not outBarrier(mid, config)
)
or
@@ -321,9 +459,9 @@ private module Stage1 {
)
or
// flow into a callable
- exists(Node arg |
+ exists(NodeEx arg |
fwdFlow(arg, _, config) and
- viableParamArg(_, node, arg) and
+ viableParamArgEx(_, node, arg) and
cc = true
)
or
@@ -338,13 +476,13 @@ private module Stage1 {
)
}
- private predicate fwdFlow(Node node, Configuration config) { fwdFlow(node, _, config) }
+ private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
pragma[nomagic]
- private predicate fwdFlowRead(Content c, Node node, Cc cc, Configuration config) {
- exists(Node mid |
+ private predicate fwdFlowRead(Content c, NodeEx node, Cc cc, Configuration config) {
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
- read(mid, c, node)
+ read(mid, c, node, config)
)
}
@@ -353,33 +491,33 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate fwdFlowConsCand(Content c, Configuration config) {
- exists(Node mid, Node node, TypedContent tc |
+ exists(NodeEx mid, NodeEx node, TypedContent tc |
not fullBarrier(node, config) and
useFieldFlow(config) and
fwdFlow(mid, _, config) and
- store(mid, tc, node, _) and
+ store(mid, tc, node, _, config) and
c = tc.getContent()
)
}
pragma[nomagic]
private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc, Configuration config) {
- exists(ReturnNodeExt ret |
+ exists(RetNodeEx ret |
fwdFlow(ret, cc, config) and
- getReturnPosition(ret) = pos
+ ret.getReturnPosition() = pos
)
}
pragma[nomagic]
- private predicate fwdFlowOut(DataFlowCall call, Node out, Cc cc, Configuration config) {
+ private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
exists(ReturnPosition pos |
fwdFlowReturnPosition(pos, cc, config) and
- viableReturnPosOut(call, pos, out)
+ viableReturnPosOutEx(call, pos, out)
)
}
pragma[nomagic]
- private predicate fwdFlowOutFromArg(DataFlowCall call, Node out, Configuration config) {
+ private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out, Configuration config) {
fwdFlowOut(call, out, true, config)
}
@@ -388,9 +526,9 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
- exists(ArgumentNode arg |
+ exists(ArgNodeEx arg |
fwdFlow(arg, cc, config) and
- viableParamArg(call, _, arg)
+ viableParamArgEx(call, _, arg)
)
}
@@ -402,34 +540,34 @@ private module Stage1 {
* the enclosing callable in order to reach a sink.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and
fwdFlow(node, config)
}
pragma[nomagic]
- private predicate revFlow0(Node node, boolean toReturn, Configuration config) {
+ private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStep(node, mid, config) and
revFlow(mid, toReturn, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
additionalLocalFlowStep(node, mid, config) and
revFlow(mid, toReturn, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, config) and
toReturn = false
)
or
- exists(Node mid |
+ exists(NodeEx mid |
additionalJumpStep(node, mid, config) and
revFlow(mid, _, config) and
toReturn = false
@@ -442,8 +580,8 @@ private module Stage1 {
)
or
// read
- exists(Node mid, Content c |
- read(node, c, mid) and
+ exists(NodeEx mid, Content c |
+ read(node, c, mid, config) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
revFlow(mid, toReturn, pragma[only_bind_into](config))
)
@@ -460,7 +598,7 @@ private module Stage1 {
// flow out of a callable
exists(ReturnPosition pos |
revFlowOut(pos, config) and
- getReturnPosition(node) = pos and
+ node.(RetNodeEx).getReturnPosition() = pos and
toReturn = true
)
}
@@ -470,20 +608,20 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Content c, Configuration config) {
- exists(Node mid, Node node |
+ exists(NodeEx mid, NodeEx node |
fwdFlow(node, pragma[only_bind_into](config)) and
- read(node, c, mid) and
+ read(node, c, mid, config) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
revFlow(pragma[only_bind_into](mid), _, pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate revFlowStore(Content c, Node node, boolean toReturn, Configuration config) {
- exists(Node mid, TypedContent tc |
+ private predicate revFlowStore(Content c, NodeEx node, boolean toReturn, Configuration config) {
+ exists(NodeEx mid, TypedContent tc |
revFlow(mid, toReturn, pragma[only_bind_into](config)) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
- store(node, tc, mid, _) and
+ store(node, tc, mid, _, config) and
c = tc.getContent()
)
}
@@ -499,15 +637,15 @@ private module Stage1 {
pragma[nomagic]
predicate viableReturnPosOutNodeCandFwd1(
- DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+ DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
fwdFlowReturnPosition(pos, _, config) and
- viableReturnPosOut(call, pos, out)
+ viableReturnPosOutEx(call, pos, out)
}
pragma[nomagic]
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
- exists(DataFlowCall call, Node out |
+ exists(DataFlowCall call, NodeEx out |
revFlow(out, _, config) and
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
)
@@ -515,33 +653,35 @@ private module Stage1 {
pragma[nomagic]
predicate viableParamArgNodeCandFwd1(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+ DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
- viableParamArg(call, p, arg) and
+ viableParamArgEx(call, p, arg) and
fwdFlow(arg, config)
}
pragma[nomagic]
private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config
+ DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
revFlow(p, toReturn, config) and
viableParamArgNodeCandFwd1(call, p, arg, config)
)
}
pragma[nomagic]
- private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
+ private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg, Configuration config) {
revFlowIn(call, arg, true, config)
}
/**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`.
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
*/
pragma[nomagic]
private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) {
- exists(Node out |
+ exists(NodeEx out |
revFlow(out, toReturn, config) and
fwdFlowOutFromArg(call, out, config)
)
@@ -549,32 +689,33 @@ private module Stage1 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Content c |
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
revFlow(node2, pragma[only_bind_into](config)) and
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
c = tc.getContent() and
exists(ap1)
)
}
pragma[nomagic]
- predicate readStepCand(Node n1, Content c, Node n2, Configuration config) {
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) and
- read(n1, c, n2)
+ read(n1, c, n2, pragma[only_bind_into](config))
}
pragma[nomagic]
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow(node, toReturn, config) and exists(returnAp) and exists(ap)
}
- private predicate throughFlowNodeCand(Node node, Configuration config) {
+ private predicate throughFlowNodeCand(NodeEx node, Configuration config) {
revFlow(node, true, config) and
fwdFlow(node, true, config) and
not inBarrier(node, config) and
@@ -586,9 +727,9 @@ private module Stage1 {
private predicate returnFlowCallableNodeCand(
DataFlowCallable callable, ReturnKindExt kind, Configuration config
) {
- exists(ReturnNodeExt ret |
+ exists(RetNodeEx ret |
throughFlowNodeCand(ret, config) and
- callable = getNodeEnclosingCallable(ret) and
+ callable = ret.getEnclosingCallable() and
kind = ret.getKind()
)
}
@@ -597,50 +738,57 @@ private module Stage1 {
* Holds if flow may enter through `p` and reach a return node making `p` a
* candidate for the origin of a summary.
*/
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
- getNodeEnclosingCallable(p) = c and
+ p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself
- not exists(int pos |
- kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos)
- )
+ not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
+ )
+ }
+
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(ArgNodeEx arg, boolean toReturn |
+ revFlow(arg, toReturn, config) and
+ revFlowInToReturn(call, arg, config) and
+ revFlowIsReturned(call, toReturn, config)
)
}
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, config)) and
fields = count(Content f0 | fwdFlowConsCand(f0, config)) and
conscand = -1 and
- tuples = count(Node n, boolean b | fwdFlow(n, b, config))
+ tuples = count(NodeEx n, boolean b | fwdFlow(n, b, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, config)) and
fields = count(Content f0 | revFlowConsCand(f0, config)) and
conscand = -1 and
- tuples = count(Node n, boolean b | revFlow(n, b, config))
+ tuples = count(NodeEx n, boolean b | revFlow(n, b, config))
}
/* End: Stage 1 logic. */
}
pragma[noinline]
-private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) {
Stage1::revFlow(node2, config) and
localFlowStep(node1, node2, config)
}
pragma[noinline]
-private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) {
Stage1::revFlow(node2, config) and
additionalLocalFlowStep(node1, node2, config)
}
pragma[nomagic]
private predicate viableReturnPosOutNodeCand1(
- DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+ DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
Stage1::revFlow(out, config) and
Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out, config)
@@ -653,9 +801,9 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, ReturnNodeExt ret, Node out, Configuration config
+ DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
) {
- viableReturnPosOutNodeCand1(call, getReturnPosition(ret), out, config) and
+ viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
@@ -663,7 +811,7 @@ private predicate flowOutOfCallNodeCand1(
pragma[nomagic]
private predicate viableParamArgNodeCand1(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+ DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and
Stage1::revFlow(arg, config)
@@ -675,7 +823,7 @@ private predicate viableParamArgNodeCand1(
*/
pragma[nomagic]
private predicate flowIntoCallNodeCand1(
- DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, Configuration config
) {
viableParamArgNodeCand1(call, p, arg, config) and
Stage1::revFlow(p, config) and
@@ -688,9 +836,9 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
-private int branch(Node n1, Configuration conf) {
+private int branch(NodeEx n1, Configuration conf) {
result =
- strictcount(Node n |
+ strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -700,9 +848,9 @@ private int branch(Node n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
-private int join(Node n2, Configuration conf) {
+private int join(NodeEx n2, Configuration conf) {
result =
- strictcount(Node n |
+ strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -716,7 +864,7 @@ private int join(Node n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
exists(int b, int j |
@@ -735,8 +883,7 @@ private predicate flowOutOfCallNodeCand1(
*/
pragma[nomagic]
private predicate flowIntoCallNodeCand1(
- DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
exists(int b, int j |
@@ -762,7 +909,7 @@ private module Stage2 {
bindingset[result, ap]
private ApApprox getApprox(Ap ap) { any() }
- private ApNil getApNil(Node node) { PrevStage::revFlow(node, _) and exists(result) }
+ private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
@@ -805,10 +952,10 @@ private module Stage2 {
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { any() }
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
(
preservesValue = true and
@@ -829,10 +976,20 @@ private module Stage2 {
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
/* Begin: Stage 2 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -842,14 +999,14 @@ private module Stage2 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -860,7 +1017,7 @@ private module Stage2 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -868,7 +1025,7 @@ private module Stage2 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -899,19 +1056,17 @@ private module Stage2 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -935,7 +1090,7 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -944,32 +1099,29 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -979,9 +1131,15 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -992,7 +1150,7 @@ private module Stage2 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
)
@@ -1000,18 +1158,41 @@ private module Stage2 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1021,41 +1202,41 @@ private module Stage2 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -1071,20 +1252,18 @@ private module Stage2 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -1097,7 +1276,7 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -1111,7 +1290,7 @@ private module Stage2 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -1120,10 +1299,10 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -1132,13 +1311,12 @@ private module Stage2 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1146,9 +1324,14 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1160,7 +1343,7 @@ private module Stage2 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -1169,16 +1352,17 @@ private module Stage2 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -1187,7 +1371,7 @@ private module Stage2 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -1199,44 +1383,54 @@ private module Stage2 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 2 logic. */
}
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
- DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
@@ -1245,7 +1439,7 @@ private predicate flowOutOfCallNodeCand2(
pragma[nomagic]
private predicate flowIntoCallNodeCand2(
- DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
@@ -1258,10 +1452,10 @@ private module LocalFlowBigStep {
* A node where some checking is required, and hence the big-step relation
* is not allowed to step over.
*/
- private class FlowCheckNode extends Node {
+ private class FlowCheckNode extends NodeEx {
FlowCheckNode() {
- this instanceof CastNode or
- clearsContent(this, _)
+ castNode(this.asNode()) or
+ clearsContentCached(this.asNode(), _)
}
}
@@ -1269,16 +1463,16 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- predicate localFlowEntry(Node node, Configuration config) {
+ predicate localFlowEntry(NodeEx node, Configuration config) {
Stage2::revFlow(node, config) and
(
- config.isSource(node) or
+ sourceNode(node, config) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
- node instanceof ParameterNode or
- node instanceof OutNodeExt or
- store(_, _, node, _) or
- read(_, _, node) or
+ node instanceof ParamNodeEx or
+ node.asNode() instanceof OutNodeExt or
+ store(_, _, node, _, config) or
+ read(_, _, node, config) or
node instanceof FlowCheckNode
)
}
@@ -1287,23 +1481,25 @@ private module LocalFlowBigStep {
* Holds if `node` can be the last node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- private predicate localFlowExit(Node node, Configuration config) {
- exists(Node next | Stage2::revFlow(next, config) |
+ private predicate localFlowExit(NodeEx node, Configuration config) {
+ exists(NodeEx next | Stage2::revFlow(next, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
- store(node, _, next, _) or
- read(node, _, next)
+ store(node, _, next, _, config) or
+ read(node, _, next, config)
)
or
node instanceof FlowCheckNode
or
- config.isSink(node)
+ sinkNode(node, config)
}
pragma[noinline]
- private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
+ private predicate additionalLocalFlowStepNodeCand2(
+ NodeEx node1, NodeEx node2, Configuration config
+ ) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and
Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config))
@@ -1318,39 +1514,39 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
- Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config,
+ NodeEx node1, NodeEx node2, boolean preservesValue, DataFlowType t, Configuration config,
LocalCallContext cc
) {
- not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
+ not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
(
localFlowEntry(node1, pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
- t = getNodeType(node1)
+ t = node1.getDataFlowType() // irrelevant dummy value
or
additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
- t = getNodeType(node2)
+ t = node2.getDataFlowType()
) and
node1 != node2 and
- cc.relevantFor(getNodeEnclosingCallable(node1)) and
- not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
+ cc.relevantFor(node1.getEnclosingCallable()) and
+ not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
Stage2::revFlow(node2, pragma[only_bind_into](config))
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStepPlus(node1, mid, preservesValue, t, pragma[only_bind_into](config), cc) and
localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof FlowCheckNode and
Stage2::revFlow(node2, pragma[only_bind_into](config))
)
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStepPlus(node1, mid, _, _, pragma[only_bind_into](config), cc) and
additionalLocalFlowStepNodeCand2(mid, node2, config) and
not mid instanceof FlowCheckNode and
preservesValue = false and
- t = getNodeType(node2) and
+ t = node2.getDataFlowType() and
Stage2::revFlow(node2, pragma[only_bind_into](config))
)
)
@@ -1362,8 +1558,8 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, AccessPathFrontNil apf, Configuration config,
- LocalCallContext callContext
+ NodeEx node1, NodeEx node2, boolean preservesValue, AccessPathFrontNil apf,
+ Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, config)
@@ -1383,8 +1579,8 @@ private module Stage3 {
private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(Node node) {
- PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node))
+ private ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
@@ -1428,10 +1624,10 @@ private module Stage3 {
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { any() }
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, node2, preservesValue, ap, config, _) and exists(lcc)
}
@@ -1440,10 +1636,16 @@ private module Stage3 {
private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ pragma[nomagic]
+ private predicate clear(NodeEx node, Ap ap) { ap.isClearedAt(node.asNode()) }
+
+ pragma[nomagic]
+ private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
+
bindingset[node, ap]
- private predicate filter(Node node, Ap ap) {
- not ap.isClearedAt(node) and
- if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any()
+ private predicate filter(NodeEx node, Ap ap) {
+ not clear(node, ap) and
+ if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()
}
bindingset[ap, contentType]
@@ -1454,7 +1656,7 @@ private module Stage3 {
}
/* Begin: Stage 3 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
@@ -1465,6 +1667,16 @@ private module Stage3 {
)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -1474,21 +1686,21 @@ private module Stage3 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
pragma[nomagic]
- private predicate fwdFlow0(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -1499,7 +1711,7 @@ private module Stage3 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -1507,7 +1719,7 @@ private module Stage3 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -1538,19 +1750,17 @@ private module Stage3 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -1574,7 +1784,7 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -1583,32 +1793,29 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1618,9 +1825,15 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1631,7 +1844,7 @@ private module Stage3 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
@@ -1639,18 +1852,41 @@ private module Stage3 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1660,41 +1896,41 @@ private module Stage3 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -1710,20 +1946,18 @@ private module Stage3 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -1736,7 +1970,7 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -1750,7 +1984,7 @@ private module Stage3 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -1759,10 +1993,10 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -1771,13 +2005,12 @@ private module Stage3 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1785,9 +2018,14 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1799,7 +2037,7 @@ private module Stage3 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -1808,16 +2046,17 @@ private module Stage3 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -1826,7 +2065,7 @@ private module Stage3 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -1838,37 +2077,47 @@ private module Stage3 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 3 logic. */
}
@@ -1877,7 +2126,7 @@ private module Stage3 {
* Holds if `argApf` is recorded as the summary context for flow reaching `node`
* and remains relevant for the following pruning stage.
*/
-private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) {
+private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
@@ -1892,7 +2141,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
exists(int tails, int nodes, int apLimit, int tupleLimit |
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
- strictcount(Node n |
+ strictcount(NodeEx n |
Stage3::revFlow(n, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
@@ -2087,8 +2336,8 @@ private module Stage4 {
private ApApprox getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(Node node) {
- PrevStage::revFlow(node, _) and result = TNil(getNodeType(node))
+ private ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
@@ -2127,26 +2376,25 @@ private module Stage4 {
bindingset[innercc, inner, call]
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
resolveReturn(innercc, inner, call)
- or
- innercc.(CallContextCall).matchesCall(call)
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
localFlowEntry(node, config) and
- result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node))
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
}
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, node2, preservesValue, ap.getFront(), config, lcc)
}
pragma[nomagic]
private predicate flowOutOfCall(
- DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
@@ -2155,7 +2403,7 @@ private module Stage4 {
pragma[nomagic]
private predicate flowIntoCall(
- DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
@@ -2164,14 +2412,14 @@ private module Stage4 {
}
bindingset[node, ap]
- private predicate filter(Node node, Ap ap) { any() }
+ private predicate filter(NodeEx node, Ap ap) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
/* Begin: Stage 4 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
@@ -2182,6 +2430,16 @@ private module Stage4 {
)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -2191,21 +2449,21 @@ private module Stage4 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
pragma[nomagic]
- private predicate fwdFlow0(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -2216,7 +2474,7 @@ private module Stage4 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -2224,7 +2482,7 @@ private module Stage4 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -2255,19 +2513,17 @@ private module Stage4 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -2291,7 +2547,7 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -2300,32 +2556,29 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -2335,9 +2588,15 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -2348,7 +2607,7 @@ private module Stage4 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
@@ -2356,18 +2615,41 @@ private module Stage4 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -2377,41 +2659,41 @@ private module Stage4 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -2427,20 +2709,18 @@ private module Stage4 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -2453,7 +2733,7 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -2467,7 +2747,7 @@ private module Stage4 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -2476,10 +2756,10 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -2488,13 +2768,12 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -2502,9 +2781,14 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -2516,7 +2800,7 @@ private module Stage4 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -2525,16 +2809,17 @@ private module Stage4 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -2543,7 +2828,7 @@ private module Stage4 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -2555,37 +2840,47 @@ private module Stage4 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 4 logic. */
}
@@ -2595,18 +2890,18 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
}
-private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) {
+private predicate nodeMayUseSummary(NodeEx n, AccessPathApprox apa, Configuration config) {
exists(DataFlowCallable c, AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and
Stage4::revFlow(n, true, _, apa0, config) and
Stage4::fwdFlow(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
- getNodeEnclosingCallable(n) = c
+ n.getEnclosingCallable() = c
)
}
private newtype TSummaryCtx =
TSummaryCtxNone() or
- TSummaryCtxSome(ParameterNode p, AccessPath ap) {
+ TSummaryCtxSome(ParamNodeEx p, AccessPath ap) {
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _)
}
@@ -2627,7 +2922,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone {
/** A summary context from which a flow summary can be generated. */
private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
- private ParameterNode p;
+ private ParamNodeEx p;
private AccessPath ap;
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
@@ -2660,7 +2955,9 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
- strictcount(Node n | Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config))
+ strictcount(NodeEx n |
+ Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)
+ )
}
/**
@@ -2752,13 +3049,13 @@ private newtype TAccessPath =
}
private newtype TPathNode =
- TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
+ TPathNodeMid(NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
- ap = TAccessPathNil(getNodeType(node))
+ ap = TAccessPathNil(node.getDataFlowType())
or
// ... or a step from an existing PathNode to another node.
exists(PathNodeMid mid |
@@ -2767,12 +3064,12 @@ private newtype TPathNode =
Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config))
)
} or
- TPathNodeSink(Node node, Configuration config) {
- pragma[only_bind_into](config).isSink(node) and
+ TPathNodeSink(NodeEx node, Configuration config) {
+ sinkNode(node, pragma[only_bind_into](config)) and
Stage4::revFlow(node, pragma[only_bind_into](config)) and
(
// A sink that is also a source ...
- config.isSource(node)
+ sourceNode(node, config)
or
// ... or a sink that can be reached from a source
exists(PathNodeMid mid |
@@ -2973,15 +3270,17 @@ class PathNode extends TPathNode {
}
/** Gets the underlying `Node`. */
- Node getNode() { none() }
+ final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
private predicate isHidden() {
- nodeIsHidden(this.getNode()) and
+ hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
+ or
+ this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
}
private PathNode getASuccessorIfHidden() {
@@ -3003,6 +3302,8 @@ class PathNode extends TPathNode {
abstract private class PathNodeImpl extends PathNode {
abstract PathNode getASuccessorImpl();
+ abstract NodeEx getNodeEx();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3017,14 +3318,14 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
}
- override string toString() { result = this.getNode().toString() + ppAp() }
+ override string toString() { result = this.getNodeEx().toString() + ppAp() }
- override string toStringWithContext() { result = this.getNode().toString() + ppAp() + ppCtx() }
+ override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
@@ -3054,7 +3355,7 @@ module PathGraph {
* a `CallContext`, and a `Configuration`.
*/
private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
- Node node;
+ NodeEx node;
CallContext cc;
SummaryCtx sc;
AccessPath ap;
@@ -3062,7 +3363,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) }
- override Node getNode() { result = node }
+ override NodeEx getNodeEx() { result = node }
CallContext getCallContext() { result = cc }
@@ -3073,7 +3374,8 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
override Configuration getConfiguration() { result = config }
private PathNodeMid getSuccMid() {
- pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and
+ pathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx(),
+ result.getAp()) and
result.getConfiguration() = unbindConf(this.getConfiguration())
}
@@ -3084,7 +3386,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid, PathNodeSink sink |
mid = getSuccMid() and
- mid.getNode() = sink.getNode() and
+ mid.getNodeEx() = sink.getNodeEx() and
mid.getAp() instanceof AccessPathNil and
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
result = sink
@@ -3092,7 +3394,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
}
override predicate isSource() {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
@@ -3105,31 +3407,35 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
* excluding the `CallContext`.
*/
private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
- Node node;
+ NodeEx node;
Configuration config;
PathNodeSink() { this = TPathNodeSink(node, config) }
- override Node getNode() { result = node }
+ override NodeEx getNodeEx() { result = node }
override Configuration getConfiguration() { result = config }
override PathNode getASuccessorImpl() { none() }
- override predicate isSource() { config.isSource(node) }
+ override predicate isSource() { sourceNode(node, config) }
}
/**
* Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`.
*/
-private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
- exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
- midnode = mid.getNode() and
+private predicate pathStep(
+ PathNodeMid mid, NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap
+) {
+ exists(AccessPath ap0, NodeEx midnode, Configuration conf, LocalCallContext localCC |
+ midnode = mid.getNodeEx() and
conf = mid.getConfiguration() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
- localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and
+ localCC =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ midnode.getEnclosingCallable()) and
ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
@@ -3139,16 +3445,16 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
ap0 instanceof AccessPathNil
)
or
- jumpStep(mid.getNode(), node, mid.getConfiguration()) and
+ jumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
ap = mid.getAp()
or
- additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and
+ additionalJumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
mid.getAp() instanceof AccessPathNil and
- ap = TAccessPathNil(getNodeType(node))
+ ap = TAccessPathNil(node.getDataFlowType())
or
exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and
sc = mid.getSummaryCtx()
@@ -3165,20 +3471,20 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
pragma[nomagic]
private predicate pathReadStep(
- PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+ PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
- Stage4::readStepCand(mid.getNode(), tc.getContent(), node, mid.getConfiguration()) and
+ Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+ PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- Stage4::storeStepCand(mid.getNode(), _, tc, node, _, mid.getConfiguration()) and
+ Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
cc = mid.getCallContext()
}
@@ -3186,7 +3492,7 @@ private predicate pathOutOfCallable0(
PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa,
Configuration config
) {
- pos = getReturnPosition(mid.getNode()) and
+ pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and
innercc = mid.getCallContext() and
innercc instanceof CallContextNoCall and
apa = mid.getAp().getApprox() and
@@ -3209,10 +3515,10 @@ private predicate pathOutOfCallable1(
}
pragma[noinline]
-private Node getAnOutNodeFlow(
+private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
- result = kind.getAnOutNode(call) and
+ result.asNode() = kind.getAnOutNode(call) and
Stage4::revFlow(result, _, _, apa, config)
}
@@ -3221,7 +3527,7 @@ private Node getAnOutNodeFlow(
* is a return from a callable and is recorded by `cc`, if needed.
*/
pragma[noinline]
-private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
+private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) {
exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config |
pathOutOfCallable1(mid, call, kind, cc, apa, config) and
out = getAnOutNodeFlow(kind, call, apa, config)
@@ -3235,8 +3541,8 @@ pragma[noinline]
private predicate pathIntoArg(
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa
) {
- exists(ArgumentNode arg |
- arg = mid.getNode() and
+ exists(ArgNode arg |
+ arg = mid.getNodeEx().asNode() and
cc = mid.getCallContext() and
arg.argumentOf(call, i) and
ap = mid.getAp() and
@@ -3248,7 +3554,7 @@ pragma[noinline]
private predicate parameterCand(
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
Stage4::revFlow(p, _, _, apa, config) and
p.isParameterOf(callable, i)
)
@@ -3272,7 +3578,7 @@ private predicate pathIntoCallable0(
* respectively.
*/
private predicate pathIntoCallable(
- PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
+ PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
DataFlowCall call
) {
exists(int i, DataFlowCallable callable, AccessPath ap |
@@ -3297,8 +3603,8 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa,
Configuration config
) {
- exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
- mid.getNode() = ret and
+ exists(PathNodeMid mid, RetNodeEx ret, int pos |
+ mid.getNodeEx() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
@@ -3326,7 +3632,7 @@ private predicate pathThroughCallable0(
* The context `cc` is restored to its value prior to entering the callable.
*/
pragma[noinline]
-private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
+private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) {
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
pathThroughCallable0(call, mid, kind, cc, ap, apa) and
out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
@@ -3344,9 +3650,9 @@ private predicate flowsTo(
) {
flowsource.isSource() and
flowsource.getConfiguration() = configuration and
- flowsource.getNode() = source and
+ flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
- flowsink.getNode() = sink
+ flowsink.getNodeEx().asNode() = sink
}
/**
@@ -3361,13 +3667,13 @@ predicate flowsTo(Node source, Node sink, Configuration configuration) {
private predicate finalStats(boolean fwd, int nodes, int fields, int conscand, int tuples) {
fwd = true and
- nodes = count(Node n0 | exists(PathNode pn | pn.getNode() = n0)) and
+ nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
tuples = count(PathNode pn)
or
fwd = false and
- nodes = count(Node n0 | exists(PathNode pn | pn.getNode() = n0 and reach(pn))) and
+ nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
tuples = count(PathNode pn | reach(pn))
@@ -3404,19 +3710,19 @@ predicate stageStats(
private module FlowExploration {
private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2, Configuration config) {
- exists(Node node1, Node node2 |
+ exists(NodeEx node1, NodeEx node2 |
jumpStep(node1, node2, config)
or
additionalJumpStep(node1, node2, config)
or
// flow into callable
- viableParamArg(_, node2, node1)
+ viableParamArgEx(_, node2, node1)
or
// flow out of a callable
- viableReturnPosOut(_, getReturnPosition(node1), node2)
+ viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2)
|
- c1 = getNodeEnclosingCallable(node1) and
- c2 = getNodeEnclosingCallable(node2) and
+ c1 = node1.getEnclosingCallable() and
+ c2 = node2.getEnclosingCallable() and
c1 != c2
)
}
@@ -3568,7 +3874,7 @@ private module FlowExploration {
private newtype TSummaryCtx1 =
TSummaryCtx1None() or
- TSummaryCtx1Param(ParameterNode p)
+ TSummaryCtx1Param(ParamNodeEx p)
private newtype TSummaryCtx2 =
TSummaryCtx2None() or
@@ -3584,25 +3890,25 @@ private module FlowExploration {
private newtype TPartialPathNode =
TPartialPathNodeFwd(
- Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
+ NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
- distSrc(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
+ distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
- Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
+ NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
Configuration config
) {
- config.isSink(node) and
+ sinkNode(node, config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil() and
@@ -3611,23 +3917,23 @@ private module FlowExploration {
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
- not clearsContent(node, ap.getHead()) and
+ not clearsContentCached(node.asNode(), ap.getHead()) and
not fullBarrier(node, config) and
- distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
+ distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
}
pragma[nomagic]
private predicate partialPathNodeMk0(
- Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
+ NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
- not clearsContent(node, ap.getHead().getContent()) and
- if node instanceof CastingNode
- then compatibleTypes(getNodeType(node), ap.getType())
+ not clearsContentCached(node.asNode(), ap.getHead().getContent()) and
+ if node.asNode() instanceof CastingNode
+ then compatibleTypes(node.getDataFlowType(), ap.getType())
else any()
)
}
@@ -3637,13 +3943,15 @@ private module FlowExploration {
*/
class PartialPathNode extends TPartialPathNode {
/** Gets a textual representation of this element. */
- string toString() { result = this.getNode().toString() + this.ppAp() }
+ string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/**
* Gets a textual representation of this element, including a textual
* representation of the call context.
*/
- string toStringWithContext() { result = this.getNode().toString() + this.ppAp() + this.ppCtx() }
+ string toStringWithContext() {
+ result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
+ }
/**
* Holds if this element is at the specified location.
@@ -3655,11 +3963,16 @@ private module FlowExploration {
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the underlying `Node`. */
- Node getNode() { none() }
+ final Node getNode() { this.getNodeEx().projectToNode() = result }
+
+ private NodeEx getNodeEx() {
+ result = this.(PartialPathNodeFwd).getNodeEx() or
+ result = this.(PartialPathNodeRev).getNodeEx()
+ }
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
@@ -3672,7 +3985,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSourceDistance() {
- result = distSrc(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
+ result = distSrc(this.getNodeEx().getEnclosingCallable(), this.getConfiguration())
}
/**
@@ -3680,7 +3993,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSinkDistance() {
- result = distSink(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
+ result = distSink(this.getNodeEx().getEnclosingCallable(), this.getConfiguration())
}
private string ppAp() {
@@ -3712,7 +4025,7 @@ private module FlowExploration {
}
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
- Node node;
+ NodeEx node;
CallContext cc;
TSummaryCtx1 sc1;
TSummaryCtx2 sc2;
@@ -3721,7 +4034,7 @@ private module FlowExploration {
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
- override Node getNode() { result = node }
+ NodeEx getNodeEx() { result = node }
CallContext getCallContext() { result = cc }
@@ -3734,12 +4047,12 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodeFwd getASuccessor() {
- partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
+ partialPathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx1(),
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
}
predicate isSource() {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
@@ -3748,7 +4061,7 @@ private module FlowExploration {
}
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
- Node node;
+ NodeEx node;
TRevSummaryCtx1 sc1;
TRevSummaryCtx2 sc2;
RevPartialAccessPath ap;
@@ -3756,7 +4069,7 @@ private module FlowExploration {
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
- override Node getNode() { result = node }
+ NodeEx getNodeEx() { result = node }
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
@@ -3767,12 +4080,12 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodeRev getASuccessor() {
- revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
+ revPartialPathStep(result, this.getNodeEx(), this.getSummaryCtx1(), this.getSummaryCtx2(),
this.getAp(), this.getConfiguration())
}
predicate isSink() {
- config.isSink(node) and
+ sinkNode(node, config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil()
@@ -3780,40 +4093,40 @@ private module FlowExploration {
}
private predicate partialPathStep(
- PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
+ PartialPathNodeFwd mid, NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
- not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
+ not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and
(
- localFlowStep(mid.getNode(), node, config) and
+ localFlowStep(mid.getNodeEx(), node, config) and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalLocalFlowStep(mid.getNode(), node, config) and
+ additionalLocalFlowStep(mid.getNodeEx(), node, config) and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
config = mid.getConfiguration()
)
or
- jumpStep(mid.getNode(), node, config) and
+ jumpStep(mid.getNodeEx(), node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalJumpStep(mid.getNode(), node, config) and
+ additionalJumpStep(mid.getNodeEx(), node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
config = mid.getConfiguration()
or
partialPathStoreStep(mid, _, _, node, ap) and
@@ -3826,8 +4139,7 @@ private module FlowExploration {
partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, tc, ap0, config) and
- compatibleTypes(ap.getType(), getNodeType(node))
+ apConsFwd(ap, tc, ap0, config)
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -3846,12 +4158,13 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
+ PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, NodeEx node,
+ PartialAccessPath ap2
) {
- exists(Node midNode, DataFlowType contentType |
- midNode = mid.getNode() and
+ exists(NodeEx midNode, DataFlowType contentType |
+ midNode = mid.getNodeEx() and
ap1 = mid.getAp() and
- store(midNode, tc, node, contentType) and
+ store(midNode, tc, node, contentType, mid.getConfiguration()) and
ap2.getHead() = tc and
ap2.len() = unbindInt(ap1.len() + 1) and
compatibleTypes(ap1.getType(), contentType)
@@ -3870,15 +4183,15 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
+ PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, NodeEx node, CallContext cc,
Configuration config
) {
- exists(Node midNode |
- midNode = mid.getNode() and
+ exists(NodeEx midNode |
+ midNode = mid.getNodeEx() and
ap = mid.getAp() and
- read(midNode, tc.getContent(), node) and
+ read(midNode, tc.getContent(), node, pragma[only_bind_into](config)) and
ap.getHead() = tc and
- config = mid.getConfiguration() and
+ pragma[only_bind_into](config) = mid.getConfiguration() and
cc = mid.getCallContext()
)
}
@@ -3887,7 +4200,7 @@ private module FlowExploration {
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
Configuration config
) {
- pos = getReturnPosition(mid.getNode()) and
+ pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and
innercc = mid.getCallContext() and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
@@ -3910,12 +4223,12 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
- PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = kind.getAnOutNode(call)
+ out.asNode() = kind.getAnOutNode(call)
)
}
@@ -3924,8 +4237,8 @@ private module FlowExploration {
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
- exists(ArgumentNode arg |
- arg = mid.getNode() and
+ exists(ArgNode arg |
+ arg = mid.getNodeEx().asNode() and
cc = mid.getCallContext() and
arg.argumentOf(call, i) and
ap = mid.getAp() and
@@ -3943,7 +4256,7 @@ private module FlowExploration {
}
private predicate partialPathIntoCallable(
- PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
+ PartialPathNodeFwd mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc,
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
@@ -3964,8 +4277,8 @@ private module FlowExploration {
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
- exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
- mid.getNode() = ret and
+ exists(PartialPathNodeFwd mid, RetNodeEx ret |
+ mid.getNodeEx() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
@@ -3980,45 +4293,45 @@ private module FlowExploration {
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
- exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
- partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and
+ exists(CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
+ partialPathIntoCallable(mid, _, cc, innercc, sc1, sc2, call, _, config) and
paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config)
)
}
private predicate partialPathThroughCallable(
- PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
- out = kind.getAnOutNode(call)
+ out.asNode() = kind.getAnOutNode(call)
)
}
private predicate revPartialPathStep(
- PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
+ PartialPathNodeRev mid, NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
RevPartialAccessPath ap, Configuration config
) {
- localFlowStep(node, mid.getNode(), config) and
+ localFlowStep(node, mid.getNodeEx(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalLocalFlowStep(node, mid.getNode(), config) and
+ additionalLocalFlowStep(node, mid.getNodeEx(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
- jumpStep(node, mid.getNode(), config) and
+ jumpStep(node, mid.getNodeEx(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalJumpStep(node, mid.getNode(), config) and
+ additionalJumpStep(node, mid.getNodeEx(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
mid.getAp() instanceof RevPartialAccessPathNil and
@@ -4037,9 +4350,9 @@ private module FlowExploration {
apConsRev(ap, c, ap0, config)
)
or
- exists(ParameterNode p |
- mid.getNode() = p and
- viableParamArg(_, p, node) and
+ exists(ParamNodeEx p |
+ mid.getNodeEx() = p and
+ viableParamArgEx(_, p, node) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
sc1 = TRevSummaryCtx1None() and
@@ -4050,7 +4363,7 @@ private module FlowExploration {
or
exists(ReturnPosition pos |
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
- pos = getReturnPosition(node)
+ pos = getReturnPosition(node.asNode())
)
or
revPartialPathThroughCallable(mid, node, ap, config) and
@@ -4060,12 +4373,13 @@ private module FlowExploration {
pragma[inline]
private predicate revPartialPathReadStep(
- PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
+ PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, NodeEx node,
+ RevPartialAccessPath ap2
) {
- exists(Node midNode |
- midNode = mid.getNode() and
+ exists(NodeEx midNode |
+ midNode = mid.getNodeEx() and
ap1 = mid.getAp() and
- read(node, c, midNode) and
+ read(node, c, midNode, mid.getConfiguration()) and
ap2.getHead() = c and
ap2.len() = unbindInt(ap1.len() + 1)
)
@@ -4083,12 +4397,12 @@ private module FlowExploration {
pragma[nomagic]
private predicate revPartialPathStoreStep(
- PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
+ PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, NodeEx node, Configuration config
) {
- exists(Node midNode, TypedContent tc |
- midNode = mid.getNode() and
+ exists(NodeEx midNode, TypedContent tc |
+ midNode = mid.getNodeEx() and
ap = mid.getAp() and
- store(node, tc, midNode, _) and
+ store(node, tc, midNode, _, config) and
ap.getHead() = c and
config = mid.getConfiguration() and
tc.getContent() = c
@@ -4100,9 +4414,9 @@ private module FlowExploration {
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
DataFlowCall call, RevPartialAccessPath ap, Configuration config
) {
- exists(Node out |
- mid.getNode() = out and
- viableReturnPosOut(call, pos, out) and
+ exists(NodeEx out |
+ mid.getNodeEx() = out and
+ viableReturnPosOutEx(call, pos, out) and
sc1 = TRevSummaryCtx1Some(pos) and
sc2 = TRevSummaryCtx2Some(ap) and
ap = mid.getAp() and
@@ -4115,9 +4429,9 @@ private module FlowExploration {
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
Configuration config
) {
- exists(PartialPathNodeRev mid, ParameterNode p |
- mid.getNode() = p and
- p.isParameterOf(_, pos) and
+ exists(PartialPathNodeRev mid, ParamNodeEx p |
+ mid.getNodeEx() = p and
+ p.getPosition() = pos and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
@@ -4138,11 +4452,11 @@ private module FlowExploration {
pragma[nomagic]
private predicate revPartialPathThroughCallable(
- PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
+ PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, int pos |
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
- node.argumentOf(call, pos)
+ node.asNode().(ArgNode).argumentOf(call, pos)
)
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
index 9498e51e7e61..df59c128ad39 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
@@ -81,6 +81,12 @@ abstract class Configuration extends string {
*/
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
+ /**
+ * Holds if an arbitrary number of implicit read steps of content `c` may be
+ * taken at `node`.
+ */
+ predicate allowImplicitRead(Node node, Content c) { none() }
+
/**
* Gets the virtual dispatch branching limit when calculating field flow.
* This can be overridden to a smaller value to improve performance (a
@@ -182,78 +188,210 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
}
}
-private predicate inBarrier(Node node, Configuration config) {
- config.isBarrierIn(node) and
- config.isSource(node)
+private newtype TNodeEx =
+ TNodeNormal(Node n) or
+ TNodeImplicitRead(Node n, boolean hasRead) {
+ any(Configuration c).allowImplicitRead(n, _) and hasRead = [false, true]
+ }
+
+private class NodeEx extends TNodeEx {
+ string toString() {
+ result = this.asNode().toString()
+ or
+ exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]")
+ }
+
+ Node asNode() { this = TNodeNormal(result) }
+
+ predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) }
+
+ Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) }
+
+ pragma[nomagic]
+ private DataFlowCallable getEnclosingCallable0() {
+ nodeEnclosingCallable(this.projectToNode(), result)
+ }
+
+ pragma[inline]
+ DataFlowCallable getEnclosingCallable() {
+ pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result)
+ }
+
+ pragma[nomagic]
+ private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) }
+
+ pragma[inline]
+ DataFlowType getDataFlowType() {
+ pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result)
+ }
+
+ predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ }
}
-private predicate outBarrier(Node node, Configuration config) {
- config.isBarrierOut(node) and
- config.isSink(node)
+private class ArgNodeEx extends NodeEx {
+ ArgNodeEx() { this.asNode() instanceof ArgNode }
}
-private predicate fullBarrier(Node node, Configuration config) {
- config.isBarrier(node)
- or
- config.isBarrierIn(node) and
- not config.isSource(node)
- or
- config.isBarrierOut(node) and
- not config.isSink(node)
- or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- node = g.getAGuardedNode()
+private class ParamNodeEx extends NodeEx {
+ ParamNodeEx() { this.asNode() instanceof ParamNode }
+
+ predicate isParameterOf(DataFlowCallable c, int i) {
+ this.asNode().(ParamNode).isParameterOf(c, i)
+ }
+
+ int getPosition() { this.isParameterOf(_, result) }
+}
+
+private class RetNodeEx extends NodeEx {
+ RetNodeEx() { this.asNode() instanceof ReturnNodeExt }
+
+ ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) }
+
+ ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() }
+}
+
+private predicate inBarrier(NodeEx node, Configuration config) {
+ exists(Node n |
+ node.asNode() = n and
+ config.isBarrierIn(n) and
+ config.isSource(n)
+ )
+}
+
+private predicate outBarrier(NodeEx node, Configuration config) {
+ exists(Node n |
+ node.asNode() = n and
+ config.isBarrierOut(n) and
+ config.isSink(n)
+ )
+}
+
+private predicate fullBarrier(NodeEx node, Configuration config) {
+ exists(Node n | node.asNode() = n |
+ config.isBarrier(n)
+ or
+ config.isBarrierIn(n) and
+ not config.isSource(n)
+ or
+ config.isBarrierOut(n) and
+ not config.isSink(n)
+ or
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
)
}
+pragma[nomagic]
+private predicate sourceNode(NodeEx node, Configuration config) { config.isSource(node.asNode()) }
+
+pragma[nomagic]
+private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) }
+
/**
* Holds if data can flow in one local step from `node1` to `node2`.
*/
-private predicate localFlowStep(Node node1, Node node2, Configuration config) {
- (
- simpleLocalFlowStep(node1, node2) or
- reverseStepThroughInputOutputAlias(node1, node2)
- ) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ simpleLocalFlowStepExt(n1, n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+ or
+ exists(Node n |
+ config.allowImplicitRead(n, _) and
+ node1.asNode() = n and
+ node2.isImplicitReadNode(n, false)
+ )
}
/**
* Holds if the additional step from `node1` to `node2` does not jump between callables.
*/
-private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
- config.isAdditionalFlowStep(node1, node2) and
- getNodeEnclosingCallable(node1) = getNodeEnclosingCallable(node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ config.isAdditionalFlowStep(n1, n2) and
+ getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+ or
+ exists(Node n |
+ config.allowImplicitRead(n, _) and
+ node1.isImplicitReadNode(n, true) and
+ node2.asNode() = n
+ )
}
/**
* Holds if data can flow from `node1` to `node2` in a way that discards call contexts.
*/
-private predicate jumpStep(Node node1, Node node2, Configuration config) {
- jumpStep(node1, node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ jumpStepCached(n1, n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
}
/**
* Holds if the additional step from `node1` to `node2` jumps between callables.
*/
-private predicate additionalJumpStep(Node node1, Node node2, Configuration config) {
- config.isAdditionalFlowStep(node1, node2) and
- getNodeEnclosingCallable(node1) != getNodeEnclosingCallable(node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ config.isAdditionalFlowStep(n1, n2) and
+ getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+}
+
+private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ read(node1.asNode(), c, node2.asNode())
+ or
+ exists(Node n |
+ node2.isImplicitReadNode(n, true) and
+ node1.isImplicitReadNode(n, _) and
+ config.allowImplicitRead(n, c)
+ )
+}
+
+private predicate store(
+ NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
+) {
+ store(node1.asNode(), tc, node2.asNode(), contentType) and
+ read(_, tc.getContent(), _, config)
+}
+
+pragma[nomagic]
+private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) {
+ viableReturnPosOut(call, pos, out.asNode())
+}
+
+pragma[nomagic]
+private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) {
+ viableParamArg(call, p.asNode(), arg.asNode())
}
/**
@@ -277,39 +415,39 @@ private module Stage1 {
* The Boolean `cc` records whether the node is reached through an
* argument in a call.
*/
- predicate fwdFlow(Node node, Cc cc, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
not fullBarrier(node, config) and
(
- config.isSource(node) and
+ sourceNode(node, config) and
cc = false
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
localFlowStep(mid, node, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
additionalLocalFlowStep(mid, node, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, config) and
jumpStep(mid, node, config) and
cc = false
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, config) and
additionalJumpStep(mid, node, config) and
cc = false
)
or
// store
- exists(Node mid |
+ exists(NodeEx mid |
useFieldFlow(config) and
fwdFlow(mid, cc, config) and
- store(mid, _, node, _) and
+ store(mid, _, node, _, config) and
not outBarrier(mid, config)
)
or
@@ -321,9 +459,9 @@ private module Stage1 {
)
or
// flow into a callable
- exists(Node arg |
+ exists(NodeEx arg |
fwdFlow(arg, _, config) and
- viableParamArg(_, node, arg) and
+ viableParamArgEx(_, node, arg) and
cc = true
)
or
@@ -338,13 +476,13 @@ private module Stage1 {
)
}
- private predicate fwdFlow(Node node, Configuration config) { fwdFlow(node, _, config) }
+ private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
pragma[nomagic]
- private predicate fwdFlowRead(Content c, Node node, Cc cc, Configuration config) {
- exists(Node mid |
+ private predicate fwdFlowRead(Content c, NodeEx node, Cc cc, Configuration config) {
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
- read(mid, c, node)
+ read(mid, c, node, config)
)
}
@@ -353,33 +491,33 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate fwdFlowConsCand(Content c, Configuration config) {
- exists(Node mid, Node node, TypedContent tc |
+ exists(NodeEx mid, NodeEx node, TypedContent tc |
not fullBarrier(node, config) and
useFieldFlow(config) and
fwdFlow(mid, _, config) and
- store(mid, tc, node, _) and
+ store(mid, tc, node, _, config) and
c = tc.getContent()
)
}
pragma[nomagic]
private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc, Configuration config) {
- exists(ReturnNodeExt ret |
+ exists(RetNodeEx ret |
fwdFlow(ret, cc, config) and
- getReturnPosition(ret) = pos
+ ret.getReturnPosition() = pos
)
}
pragma[nomagic]
- private predicate fwdFlowOut(DataFlowCall call, Node out, Cc cc, Configuration config) {
+ private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
exists(ReturnPosition pos |
fwdFlowReturnPosition(pos, cc, config) and
- viableReturnPosOut(call, pos, out)
+ viableReturnPosOutEx(call, pos, out)
)
}
pragma[nomagic]
- private predicate fwdFlowOutFromArg(DataFlowCall call, Node out, Configuration config) {
+ private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out, Configuration config) {
fwdFlowOut(call, out, true, config)
}
@@ -388,9 +526,9 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
- exists(ArgumentNode arg |
+ exists(ArgNodeEx arg |
fwdFlow(arg, cc, config) and
- viableParamArg(call, _, arg)
+ viableParamArgEx(call, _, arg)
)
}
@@ -402,34 +540,34 @@ private module Stage1 {
* the enclosing callable in order to reach a sink.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and
fwdFlow(node, config)
}
pragma[nomagic]
- private predicate revFlow0(Node node, boolean toReturn, Configuration config) {
+ private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStep(node, mid, config) and
revFlow(mid, toReturn, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
additionalLocalFlowStep(node, mid, config) and
revFlow(mid, toReturn, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, config) and
toReturn = false
)
or
- exists(Node mid |
+ exists(NodeEx mid |
additionalJumpStep(node, mid, config) and
revFlow(mid, _, config) and
toReturn = false
@@ -442,8 +580,8 @@ private module Stage1 {
)
or
// read
- exists(Node mid, Content c |
- read(node, c, mid) and
+ exists(NodeEx mid, Content c |
+ read(node, c, mid, config) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
revFlow(mid, toReturn, pragma[only_bind_into](config))
)
@@ -460,7 +598,7 @@ private module Stage1 {
// flow out of a callable
exists(ReturnPosition pos |
revFlowOut(pos, config) and
- getReturnPosition(node) = pos and
+ node.(RetNodeEx).getReturnPosition() = pos and
toReturn = true
)
}
@@ -470,20 +608,20 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Content c, Configuration config) {
- exists(Node mid, Node node |
+ exists(NodeEx mid, NodeEx node |
fwdFlow(node, pragma[only_bind_into](config)) and
- read(node, c, mid) and
+ read(node, c, mid, config) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
revFlow(pragma[only_bind_into](mid), _, pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate revFlowStore(Content c, Node node, boolean toReturn, Configuration config) {
- exists(Node mid, TypedContent tc |
+ private predicate revFlowStore(Content c, NodeEx node, boolean toReturn, Configuration config) {
+ exists(NodeEx mid, TypedContent tc |
revFlow(mid, toReturn, pragma[only_bind_into](config)) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
- store(node, tc, mid, _) and
+ store(node, tc, mid, _, config) and
c = tc.getContent()
)
}
@@ -499,15 +637,15 @@ private module Stage1 {
pragma[nomagic]
predicate viableReturnPosOutNodeCandFwd1(
- DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+ DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
fwdFlowReturnPosition(pos, _, config) and
- viableReturnPosOut(call, pos, out)
+ viableReturnPosOutEx(call, pos, out)
}
pragma[nomagic]
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
- exists(DataFlowCall call, Node out |
+ exists(DataFlowCall call, NodeEx out |
revFlow(out, _, config) and
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
)
@@ -515,33 +653,35 @@ private module Stage1 {
pragma[nomagic]
predicate viableParamArgNodeCandFwd1(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+ DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
- viableParamArg(call, p, arg) and
+ viableParamArgEx(call, p, arg) and
fwdFlow(arg, config)
}
pragma[nomagic]
private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config
+ DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
revFlow(p, toReturn, config) and
viableParamArgNodeCandFwd1(call, p, arg, config)
)
}
pragma[nomagic]
- private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
+ private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg, Configuration config) {
revFlowIn(call, arg, true, config)
}
/**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`.
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
*/
pragma[nomagic]
private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) {
- exists(Node out |
+ exists(NodeEx out |
revFlow(out, toReturn, config) and
fwdFlowOutFromArg(call, out, config)
)
@@ -549,32 +689,33 @@ private module Stage1 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Content c |
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
revFlow(node2, pragma[only_bind_into](config)) and
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
c = tc.getContent() and
exists(ap1)
)
}
pragma[nomagic]
- predicate readStepCand(Node n1, Content c, Node n2, Configuration config) {
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) and
- read(n1, c, n2)
+ read(n1, c, n2, pragma[only_bind_into](config))
}
pragma[nomagic]
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow(node, toReturn, config) and exists(returnAp) and exists(ap)
}
- private predicate throughFlowNodeCand(Node node, Configuration config) {
+ private predicate throughFlowNodeCand(NodeEx node, Configuration config) {
revFlow(node, true, config) and
fwdFlow(node, true, config) and
not inBarrier(node, config) and
@@ -586,9 +727,9 @@ private module Stage1 {
private predicate returnFlowCallableNodeCand(
DataFlowCallable callable, ReturnKindExt kind, Configuration config
) {
- exists(ReturnNodeExt ret |
+ exists(RetNodeEx ret |
throughFlowNodeCand(ret, config) and
- callable = getNodeEnclosingCallable(ret) and
+ callable = ret.getEnclosingCallable() and
kind = ret.getKind()
)
}
@@ -597,50 +738,57 @@ private module Stage1 {
* Holds if flow may enter through `p` and reach a return node making `p` a
* candidate for the origin of a summary.
*/
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
- getNodeEnclosingCallable(p) = c and
+ p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself
- not exists(int pos |
- kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos)
- )
+ not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
+ )
+ }
+
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(ArgNodeEx arg, boolean toReturn |
+ revFlow(arg, toReturn, config) and
+ revFlowInToReturn(call, arg, config) and
+ revFlowIsReturned(call, toReturn, config)
)
}
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, config)) and
fields = count(Content f0 | fwdFlowConsCand(f0, config)) and
conscand = -1 and
- tuples = count(Node n, boolean b | fwdFlow(n, b, config))
+ tuples = count(NodeEx n, boolean b | fwdFlow(n, b, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, config)) and
fields = count(Content f0 | revFlowConsCand(f0, config)) and
conscand = -1 and
- tuples = count(Node n, boolean b | revFlow(n, b, config))
+ tuples = count(NodeEx n, boolean b | revFlow(n, b, config))
}
/* End: Stage 1 logic. */
}
pragma[noinline]
-private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) {
Stage1::revFlow(node2, config) and
localFlowStep(node1, node2, config)
}
pragma[noinline]
-private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) {
Stage1::revFlow(node2, config) and
additionalLocalFlowStep(node1, node2, config)
}
pragma[nomagic]
private predicate viableReturnPosOutNodeCand1(
- DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+ DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
Stage1::revFlow(out, config) and
Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out, config)
@@ -653,9 +801,9 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, ReturnNodeExt ret, Node out, Configuration config
+ DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
) {
- viableReturnPosOutNodeCand1(call, getReturnPosition(ret), out, config) and
+ viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
@@ -663,7 +811,7 @@ private predicate flowOutOfCallNodeCand1(
pragma[nomagic]
private predicate viableParamArgNodeCand1(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+ DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and
Stage1::revFlow(arg, config)
@@ -675,7 +823,7 @@ private predicate viableParamArgNodeCand1(
*/
pragma[nomagic]
private predicate flowIntoCallNodeCand1(
- DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, Configuration config
) {
viableParamArgNodeCand1(call, p, arg, config) and
Stage1::revFlow(p, config) and
@@ -688,9 +836,9 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
-private int branch(Node n1, Configuration conf) {
+private int branch(NodeEx n1, Configuration conf) {
result =
- strictcount(Node n |
+ strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -700,9 +848,9 @@ private int branch(Node n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
-private int join(Node n2, Configuration conf) {
+private int join(NodeEx n2, Configuration conf) {
result =
- strictcount(Node n |
+ strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -716,7 +864,7 @@ private int join(Node n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
exists(int b, int j |
@@ -735,8 +883,7 @@ private predicate flowOutOfCallNodeCand1(
*/
pragma[nomagic]
private predicate flowIntoCallNodeCand1(
- DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
exists(int b, int j |
@@ -762,7 +909,7 @@ private module Stage2 {
bindingset[result, ap]
private ApApprox getApprox(Ap ap) { any() }
- private ApNil getApNil(Node node) { PrevStage::revFlow(node, _) and exists(result) }
+ private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
@@ -805,10 +952,10 @@ private module Stage2 {
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { any() }
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
(
preservesValue = true and
@@ -829,10 +976,20 @@ private module Stage2 {
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
/* Begin: Stage 2 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -842,14 +999,14 @@ private module Stage2 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -860,7 +1017,7 @@ private module Stage2 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -868,7 +1025,7 @@ private module Stage2 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -899,19 +1056,17 @@ private module Stage2 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -935,7 +1090,7 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -944,32 +1099,29 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -979,9 +1131,15 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -992,7 +1150,7 @@ private module Stage2 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
)
@@ -1000,18 +1158,41 @@ private module Stage2 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1021,41 +1202,41 @@ private module Stage2 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -1071,20 +1252,18 @@ private module Stage2 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -1097,7 +1276,7 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -1111,7 +1290,7 @@ private module Stage2 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -1120,10 +1299,10 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -1132,13 +1311,12 @@ private module Stage2 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1146,9 +1324,14 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1160,7 +1343,7 @@ private module Stage2 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -1169,16 +1352,17 @@ private module Stage2 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -1187,7 +1371,7 @@ private module Stage2 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -1199,44 +1383,54 @@ private module Stage2 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 2 logic. */
}
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
- DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
@@ -1245,7 +1439,7 @@ private predicate flowOutOfCallNodeCand2(
pragma[nomagic]
private predicate flowIntoCallNodeCand2(
- DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
@@ -1258,10 +1452,10 @@ private module LocalFlowBigStep {
* A node where some checking is required, and hence the big-step relation
* is not allowed to step over.
*/
- private class FlowCheckNode extends Node {
+ private class FlowCheckNode extends NodeEx {
FlowCheckNode() {
- this instanceof CastNode or
- clearsContent(this, _)
+ castNode(this.asNode()) or
+ clearsContentCached(this.asNode(), _)
}
}
@@ -1269,16 +1463,16 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- predicate localFlowEntry(Node node, Configuration config) {
+ predicate localFlowEntry(NodeEx node, Configuration config) {
Stage2::revFlow(node, config) and
(
- config.isSource(node) or
+ sourceNode(node, config) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
- node instanceof ParameterNode or
- node instanceof OutNodeExt or
- store(_, _, node, _) or
- read(_, _, node) or
+ node instanceof ParamNodeEx or
+ node.asNode() instanceof OutNodeExt or
+ store(_, _, node, _, config) or
+ read(_, _, node, config) or
node instanceof FlowCheckNode
)
}
@@ -1287,23 +1481,25 @@ private module LocalFlowBigStep {
* Holds if `node` can be the last node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- private predicate localFlowExit(Node node, Configuration config) {
- exists(Node next | Stage2::revFlow(next, config) |
+ private predicate localFlowExit(NodeEx node, Configuration config) {
+ exists(NodeEx next | Stage2::revFlow(next, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
- store(node, _, next, _) or
- read(node, _, next)
+ store(node, _, next, _, config) or
+ read(node, _, next, config)
)
or
node instanceof FlowCheckNode
or
- config.isSink(node)
+ sinkNode(node, config)
}
pragma[noinline]
- private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
+ private predicate additionalLocalFlowStepNodeCand2(
+ NodeEx node1, NodeEx node2, Configuration config
+ ) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and
Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config))
@@ -1318,39 +1514,39 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
- Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config,
+ NodeEx node1, NodeEx node2, boolean preservesValue, DataFlowType t, Configuration config,
LocalCallContext cc
) {
- not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
+ not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
(
localFlowEntry(node1, pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
- t = getNodeType(node1)
+ t = node1.getDataFlowType() // irrelevant dummy value
or
additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
- t = getNodeType(node2)
+ t = node2.getDataFlowType()
) and
node1 != node2 and
- cc.relevantFor(getNodeEnclosingCallable(node1)) and
- not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
+ cc.relevantFor(node1.getEnclosingCallable()) and
+ not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
Stage2::revFlow(node2, pragma[only_bind_into](config))
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStepPlus(node1, mid, preservesValue, t, pragma[only_bind_into](config), cc) and
localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof FlowCheckNode and
Stage2::revFlow(node2, pragma[only_bind_into](config))
)
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStepPlus(node1, mid, _, _, pragma[only_bind_into](config), cc) and
additionalLocalFlowStepNodeCand2(mid, node2, config) and
not mid instanceof FlowCheckNode and
preservesValue = false and
- t = getNodeType(node2) and
+ t = node2.getDataFlowType() and
Stage2::revFlow(node2, pragma[only_bind_into](config))
)
)
@@ -1362,8 +1558,8 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, AccessPathFrontNil apf, Configuration config,
- LocalCallContext callContext
+ NodeEx node1, NodeEx node2, boolean preservesValue, AccessPathFrontNil apf,
+ Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, config)
@@ -1383,8 +1579,8 @@ private module Stage3 {
private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(Node node) {
- PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node))
+ private ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
@@ -1428,10 +1624,10 @@ private module Stage3 {
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { any() }
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, node2, preservesValue, ap, config, _) and exists(lcc)
}
@@ -1440,10 +1636,16 @@ private module Stage3 {
private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ pragma[nomagic]
+ private predicate clear(NodeEx node, Ap ap) { ap.isClearedAt(node.asNode()) }
+
+ pragma[nomagic]
+ private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
+
bindingset[node, ap]
- private predicate filter(Node node, Ap ap) {
- not ap.isClearedAt(node) and
- if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any()
+ private predicate filter(NodeEx node, Ap ap) {
+ not clear(node, ap) and
+ if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()
}
bindingset[ap, contentType]
@@ -1454,7 +1656,7 @@ private module Stage3 {
}
/* Begin: Stage 3 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
@@ -1465,6 +1667,16 @@ private module Stage3 {
)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -1474,21 +1686,21 @@ private module Stage3 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
pragma[nomagic]
- private predicate fwdFlow0(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -1499,7 +1711,7 @@ private module Stage3 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -1507,7 +1719,7 @@ private module Stage3 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -1538,19 +1750,17 @@ private module Stage3 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -1574,7 +1784,7 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -1583,32 +1793,29 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1618,9 +1825,15 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1631,7 +1844,7 @@ private module Stage3 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
@@ -1639,18 +1852,41 @@ private module Stage3 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1660,41 +1896,41 @@ private module Stage3 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -1710,20 +1946,18 @@ private module Stage3 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -1736,7 +1970,7 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -1750,7 +1984,7 @@ private module Stage3 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -1759,10 +1993,10 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -1771,13 +2005,12 @@ private module Stage3 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1785,9 +2018,14 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1799,7 +2037,7 @@ private module Stage3 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -1808,16 +2046,17 @@ private module Stage3 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -1826,7 +2065,7 @@ private module Stage3 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -1838,37 +2077,47 @@ private module Stage3 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 3 logic. */
}
@@ -1877,7 +2126,7 @@ private module Stage3 {
* Holds if `argApf` is recorded as the summary context for flow reaching `node`
* and remains relevant for the following pruning stage.
*/
-private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) {
+private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
@@ -1892,7 +2141,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
exists(int tails, int nodes, int apLimit, int tupleLimit |
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
- strictcount(Node n |
+ strictcount(NodeEx n |
Stage3::revFlow(n, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
@@ -2087,8 +2336,8 @@ private module Stage4 {
private ApApprox getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(Node node) {
- PrevStage::revFlow(node, _) and result = TNil(getNodeType(node))
+ private ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
@@ -2127,26 +2376,25 @@ private module Stage4 {
bindingset[innercc, inner, call]
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
resolveReturn(innercc, inner, call)
- or
- innercc.(CallContextCall).matchesCall(call)
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
localFlowEntry(node, config) and
- result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node))
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
}
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, node2, preservesValue, ap.getFront(), config, lcc)
}
pragma[nomagic]
private predicate flowOutOfCall(
- DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
@@ -2155,7 +2403,7 @@ private module Stage4 {
pragma[nomagic]
private predicate flowIntoCall(
- DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
@@ -2164,14 +2412,14 @@ private module Stage4 {
}
bindingset[node, ap]
- private predicate filter(Node node, Ap ap) { any() }
+ private predicate filter(NodeEx node, Ap ap) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
/* Begin: Stage 4 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
@@ -2182,6 +2430,16 @@ private module Stage4 {
)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -2191,21 +2449,21 @@ private module Stage4 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
pragma[nomagic]
- private predicate fwdFlow0(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -2216,7 +2474,7 @@ private module Stage4 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -2224,7 +2482,7 @@ private module Stage4 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -2255,19 +2513,17 @@ private module Stage4 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -2291,7 +2547,7 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -2300,32 +2556,29 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -2335,9 +2588,15 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -2348,7 +2607,7 @@ private module Stage4 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
@@ -2356,18 +2615,41 @@ private module Stage4 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -2377,41 +2659,41 @@ private module Stage4 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -2427,20 +2709,18 @@ private module Stage4 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -2453,7 +2733,7 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -2467,7 +2747,7 @@ private module Stage4 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -2476,10 +2756,10 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -2488,13 +2768,12 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -2502,9 +2781,14 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -2516,7 +2800,7 @@ private module Stage4 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -2525,16 +2809,17 @@ private module Stage4 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -2543,7 +2828,7 @@ private module Stage4 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -2555,37 +2840,47 @@ private module Stage4 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 4 logic. */
}
@@ -2595,18 +2890,18 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
}
-private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) {
+private predicate nodeMayUseSummary(NodeEx n, AccessPathApprox apa, Configuration config) {
exists(DataFlowCallable c, AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and
Stage4::revFlow(n, true, _, apa0, config) and
Stage4::fwdFlow(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
- getNodeEnclosingCallable(n) = c
+ n.getEnclosingCallable() = c
)
}
private newtype TSummaryCtx =
TSummaryCtxNone() or
- TSummaryCtxSome(ParameterNode p, AccessPath ap) {
+ TSummaryCtxSome(ParamNodeEx p, AccessPath ap) {
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _)
}
@@ -2627,7 +2922,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone {
/** A summary context from which a flow summary can be generated. */
private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
- private ParameterNode p;
+ private ParamNodeEx p;
private AccessPath ap;
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
@@ -2660,7 +2955,9 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
- strictcount(Node n | Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config))
+ strictcount(NodeEx n |
+ Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)
+ )
}
/**
@@ -2752,13 +3049,13 @@ private newtype TAccessPath =
}
private newtype TPathNode =
- TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
+ TPathNodeMid(NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
- ap = TAccessPathNil(getNodeType(node))
+ ap = TAccessPathNil(node.getDataFlowType())
or
// ... or a step from an existing PathNode to another node.
exists(PathNodeMid mid |
@@ -2767,12 +3064,12 @@ private newtype TPathNode =
Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config))
)
} or
- TPathNodeSink(Node node, Configuration config) {
- pragma[only_bind_into](config).isSink(node) and
+ TPathNodeSink(NodeEx node, Configuration config) {
+ sinkNode(node, pragma[only_bind_into](config)) and
Stage4::revFlow(node, pragma[only_bind_into](config)) and
(
// A sink that is also a source ...
- config.isSource(node)
+ sourceNode(node, config)
or
// ... or a sink that can be reached from a source
exists(PathNodeMid mid |
@@ -2973,15 +3270,17 @@ class PathNode extends TPathNode {
}
/** Gets the underlying `Node`. */
- Node getNode() { none() }
+ final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
private predicate isHidden() {
- nodeIsHidden(this.getNode()) and
+ hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
+ or
+ this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
}
private PathNode getASuccessorIfHidden() {
@@ -3003,6 +3302,8 @@ class PathNode extends TPathNode {
abstract private class PathNodeImpl extends PathNode {
abstract PathNode getASuccessorImpl();
+ abstract NodeEx getNodeEx();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3017,14 +3318,14 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
}
- override string toString() { result = this.getNode().toString() + ppAp() }
+ override string toString() { result = this.getNodeEx().toString() + ppAp() }
- override string toStringWithContext() { result = this.getNode().toString() + ppAp() + ppCtx() }
+ override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
@@ -3054,7 +3355,7 @@ module PathGraph {
* a `CallContext`, and a `Configuration`.
*/
private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
- Node node;
+ NodeEx node;
CallContext cc;
SummaryCtx sc;
AccessPath ap;
@@ -3062,7 +3363,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) }
- override Node getNode() { result = node }
+ override NodeEx getNodeEx() { result = node }
CallContext getCallContext() { result = cc }
@@ -3073,7 +3374,8 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
override Configuration getConfiguration() { result = config }
private PathNodeMid getSuccMid() {
- pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and
+ pathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx(),
+ result.getAp()) and
result.getConfiguration() = unbindConf(this.getConfiguration())
}
@@ -3084,7 +3386,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid, PathNodeSink sink |
mid = getSuccMid() and
- mid.getNode() = sink.getNode() and
+ mid.getNodeEx() = sink.getNodeEx() and
mid.getAp() instanceof AccessPathNil and
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
result = sink
@@ -3092,7 +3394,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
}
override predicate isSource() {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
@@ -3105,31 +3407,35 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
* excluding the `CallContext`.
*/
private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
- Node node;
+ NodeEx node;
Configuration config;
PathNodeSink() { this = TPathNodeSink(node, config) }
- override Node getNode() { result = node }
+ override NodeEx getNodeEx() { result = node }
override Configuration getConfiguration() { result = config }
override PathNode getASuccessorImpl() { none() }
- override predicate isSource() { config.isSource(node) }
+ override predicate isSource() { sourceNode(node, config) }
}
/**
* Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`.
*/
-private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
- exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
- midnode = mid.getNode() and
+private predicate pathStep(
+ PathNodeMid mid, NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap
+) {
+ exists(AccessPath ap0, NodeEx midnode, Configuration conf, LocalCallContext localCC |
+ midnode = mid.getNodeEx() and
conf = mid.getConfiguration() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
- localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and
+ localCC =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ midnode.getEnclosingCallable()) and
ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
@@ -3139,16 +3445,16 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
ap0 instanceof AccessPathNil
)
or
- jumpStep(mid.getNode(), node, mid.getConfiguration()) and
+ jumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
ap = mid.getAp()
or
- additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and
+ additionalJumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
mid.getAp() instanceof AccessPathNil and
- ap = TAccessPathNil(getNodeType(node))
+ ap = TAccessPathNil(node.getDataFlowType())
or
exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and
sc = mid.getSummaryCtx()
@@ -3165,20 +3471,20 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
pragma[nomagic]
private predicate pathReadStep(
- PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+ PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
- Stage4::readStepCand(mid.getNode(), tc.getContent(), node, mid.getConfiguration()) and
+ Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+ PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- Stage4::storeStepCand(mid.getNode(), _, tc, node, _, mid.getConfiguration()) and
+ Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
cc = mid.getCallContext()
}
@@ -3186,7 +3492,7 @@ private predicate pathOutOfCallable0(
PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa,
Configuration config
) {
- pos = getReturnPosition(mid.getNode()) and
+ pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and
innercc = mid.getCallContext() and
innercc instanceof CallContextNoCall and
apa = mid.getAp().getApprox() and
@@ -3209,10 +3515,10 @@ private predicate pathOutOfCallable1(
}
pragma[noinline]
-private Node getAnOutNodeFlow(
+private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
- result = kind.getAnOutNode(call) and
+ result.asNode() = kind.getAnOutNode(call) and
Stage4::revFlow(result, _, _, apa, config)
}
@@ -3221,7 +3527,7 @@ private Node getAnOutNodeFlow(
* is a return from a callable and is recorded by `cc`, if needed.
*/
pragma[noinline]
-private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
+private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) {
exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config |
pathOutOfCallable1(mid, call, kind, cc, apa, config) and
out = getAnOutNodeFlow(kind, call, apa, config)
@@ -3235,8 +3541,8 @@ pragma[noinline]
private predicate pathIntoArg(
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa
) {
- exists(ArgumentNode arg |
- arg = mid.getNode() and
+ exists(ArgNode arg |
+ arg = mid.getNodeEx().asNode() and
cc = mid.getCallContext() and
arg.argumentOf(call, i) and
ap = mid.getAp() and
@@ -3248,7 +3554,7 @@ pragma[noinline]
private predicate parameterCand(
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
Stage4::revFlow(p, _, _, apa, config) and
p.isParameterOf(callable, i)
)
@@ -3272,7 +3578,7 @@ private predicate pathIntoCallable0(
* respectively.
*/
private predicate pathIntoCallable(
- PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
+ PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
DataFlowCall call
) {
exists(int i, DataFlowCallable callable, AccessPath ap |
@@ -3297,8 +3603,8 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa,
Configuration config
) {
- exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
- mid.getNode() = ret and
+ exists(PathNodeMid mid, RetNodeEx ret, int pos |
+ mid.getNodeEx() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
@@ -3326,7 +3632,7 @@ private predicate pathThroughCallable0(
* The context `cc` is restored to its value prior to entering the callable.
*/
pragma[noinline]
-private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
+private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) {
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
pathThroughCallable0(call, mid, kind, cc, ap, apa) and
out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
@@ -3344,9 +3650,9 @@ private predicate flowsTo(
) {
flowsource.isSource() and
flowsource.getConfiguration() = configuration and
- flowsource.getNode() = source and
+ flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
- flowsink.getNode() = sink
+ flowsink.getNodeEx().asNode() = sink
}
/**
@@ -3361,13 +3667,13 @@ predicate flowsTo(Node source, Node sink, Configuration configuration) {
private predicate finalStats(boolean fwd, int nodes, int fields, int conscand, int tuples) {
fwd = true and
- nodes = count(Node n0 | exists(PathNode pn | pn.getNode() = n0)) and
+ nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
tuples = count(PathNode pn)
or
fwd = false and
- nodes = count(Node n0 | exists(PathNode pn | pn.getNode() = n0 and reach(pn))) and
+ nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
tuples = count(PathNode pn | reach(pn))
@@ -3404,19 +3710,19 @@ predicate stageStats(
private module FlowExploration {
private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2, Configuration config) {
- exists(Node node1, Node node2 |
+ exists(NodeEx node1, NodeEx node2 |
jumpStep(node1, node2, config)
or
additionalJumpStep(node1, node2, config)
or
// flow into callable
- viableParamArg(_, node2, node1)
+ viableParamArgEx(_, node2, node1)
or
// flow out of a callable
- viableReturnPosOut(_, getReturnPosition(node1), node2)
+ viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2)
|
- c1 = getNodeEnclosingCallable(node1) and
- c2 = getNodeEnclosingCallable(node2) and
+ c1 = node1.getEnclosingCallable() and
+ c2 = node2.getEnclosingCallable() and
c1 != c2
)
}
@@ -3568,7 +3874,7 @@ private module FlowExploration {
private newtype TSummaryCtx1 =
TSummaryCtx1None() or
- TSummaryCtx1Param(ParameterNode p)
+ TSummaryCtx1Param(ParamNodeEx p)
private newtype TSummaryCtx2 =
TSummaryCtx2None() or
@@ -3584,25 +3890,25 @@ private module FlowExploration {
private newtype TPartialPathNode =
TPartialPathNodeFwd(
- Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
+ NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
- distSrc(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
+ distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
- Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
+ NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
Configuration config
) {
- config.isSink(node) and
+ sinkNode(node, config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil() and
@@ -3611,23 +3917,23 @@ private module FlowExploration {
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
- not clearsContent(node, ap.getHead()) and
+ not clearsContentCached(node.asNode(), ap.getHead()) and
not fullBarrier(node, config) and
- distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
+ distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
}
pragma[nomagic]
private predicate partialPathNodeMk0(
- Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
+ NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
- not clearsContent(node, ap.getHead().getContent()) and
- if node instanceof CastingNode
- then compatibleTypes(getNodeType(node), ap.getType())
+ not clearsContentCached(node.asNode(), ap.getHead().getContent()) and
+ if node.asNode() instanceof CastingNode
+ then compatibleTypes(node.getDataFlowType(), ap.getType())
else any()
)
}
@@ -3637,13 +3943,15 @@ private module FlowExploration {
*/
class PartialPathNode extends TPartialPathNode {
/** Gets a textual representation of this element. */
- string toString() { result = this.getNode().toString() + this.ppAp() }
+ string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/**
* Gets a textual representation of this element, including a textual
* representation of the call context.
*/
- string toStringWithContext() { result = this.getNode().toString() + this.ppAp() + this.ppCtx() }
+ string toStringWithContext() {
+ result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
+ }
/**
* Holds if this element is at the specified location.
@@ -3655,11 +3963,16 @@ private module FlowExploration {
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the underlying `Node`. */
- Node getNode() { none() }
+ final Node getNode() { this.getNodeEx().projectToNode() = result }
+
+ private NodeEx getNodeEx() {
+ result = this.(PartialPathNodeFwd).getNodeEx() or
+ result = this.(PartialPathNodeRev).getNodeEx()
+ }
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
@@ -3672,7 +3985,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSourceDistance() {
- result = distSrc(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
+ result = distSrc(this.getNodeEx().getEnclosingCallable(), this.getConfiguration())
}
/**
@@ -3680,7 +3993,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSinkDistance() {
- result = distSink(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
+ result = distSink(this.getNodeEx().getEnclosingCallable(), this.getConfiguration())
}
private string ppAp() {
@@ -3712,7 +4025,7 @@ private module FlowExploration {
}
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
- Node node;
+ NodeEx node;
CallContext cc;
TSummaryCtx1 sc1;
TSummaryCtx2 sc2;
@@ -3721,7 +4034,7 @@ private module FlowExploration {
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
- override Node getNode() { result = node }
+ NodeEx getNodeEx() { result = node }
CallContext getCallContext() { result = cc }
@@ -3734,12 +4047,12 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodeFwd getASuccessor() {
- partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
+ partialPathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx1(),
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
}
predicate isSource() {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
@@ -3748,7 +4061,7 @@ private module FlowExploration {
}
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
- Node node;
+ NodeEx node;
TRevSummaryCtx1 sc1;
TRevSummaryCtx2 sc2;
RevPartialAccessPath ap;
@@ -3756,7 +4069,7 @@ private module FlowExploration {
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
- override Node getNode() { result = node }
+ NodeEx getNodeEx() { result = node }
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
@@ -3767,12 +4080,12 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodeRev getASuccessor() {
- revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
+ revPartialPathStep(result, this.getNodeEx(), this.getSummaryCtx1(), this.getSummaryCtx2(),
this.getAp(), this.getConfiguration())
}
predicate isSink() {
- config.isSink(node) and
+ sinkNode(node, config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil()
@@ -3780,40 +4093,40 @@ private module FlowExploration {
}
private predicate partialPathStep(
- PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
+ PartialPathNodeFwd mid, NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
- not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
+ not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and
(
- localFlowStep(mid.getNode(), node, config) and
+ localFlowStep(mid.getNodeEx(), node, config) and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalLocalFlowStep(mid.getNode(), node, config) and
+ additionalLocalFlowStep(mid.getNodeEx(), node, config) and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
config = mid.getConfiguration()
)
or
- jumpStep(mid.getNode(), node, config) and
+ jumpStep(mid.getNodeEx(), node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalJumpStep(mid.getNode(), node, config) and
+ additionalJumpStep(mid.getNodeEx(), node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
config = mid.getConfiguration()
or
partialPathStoreStep(mid, _, _, node, ap) and
@@ -3826,8 +4139,7 @@ private module FlowExploration {
partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, tc, ap0, config) and
- compatibleTypes(ap.getType(), getNodeType(node))
+ apConsFwd(ap, tc, ap0, config)
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -3846,12 +4158,13 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
+ PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, NodeEx node,
+ PartialAccessPath ap2
) {
- exists(Node midNode, DataFlowType contentType |
- midNode = mid.getNode() and
+ exists(NodeEx midNode, DataFlowType contentType |
+ midNode = mid.getNodeEx() and
ap1 = mid.getAp() and
- store(midNode, tc, node, contentType) and
+ store(midNode, tc, node, contentType, mid.getConfiguration()) and
ap2.getHead() = tc and
ap2.len() = unbindInt(ap1.len() + 1) and
compatibleTypes(ap1.getType(), contentType)
@@ -3870,15 +4183,15 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
+ PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, NodeEx node, CallContext cc,
Configuration config
) {
- exists(Node midNode |
- midNode = mid.getNode() and
+ exists(NodeEx midNode |
+ midNode = mid.getNodeEx() and
ap = mid.getAp() and
- read(midNode, tc.getContent(), node) and
+ read(midNode, tc.getContent(), node, pragma[only_bind_into](config)) and
ap.getHead() = tc and
- config = mid.getConfiguration() and
+ pragma[only_bind_into](config) = mid.getConfiguration() and
cc = mid.getCallContext()
)
}
@@ -3887,7 +4200,7 @@ private module FlowExploration {
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
Configuration config
) {
- pos = getReturnPosition(mid.getNode()) and
+ pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and
innercc = mid.getCallContext() and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
@@ -3910,12 +4223,12 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
- PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = kind.getAnOutNode(call)
+ out.asNode() = kind.getAnOutNode(call)
)
}
@@ -3924,8 +4237,8 @@ private module FlowExploration {
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
- exists(ArgumentNode arg |
- arg = mid.getNode() and
+ exists(ArgNode arg |
+ arg = mid.getNodeEx().asNode() and
cc = mid.getCallContext() and
arg.argumentOf(call, i) and
ap = mid.getAp() and
@@ -3943,7 +4256,7 @@ private module FlowExploration {
}
private predicate partialPathIntoCallable(
- PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
+ PartialPathNodeFwd mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc,
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
@@ -3964,8 +4277,8 @@ private module FlowExploration {
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
- exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
- mid.getNode() = ret and
+ exists(PartialPathNodeFwd mid, RetNodeEx ret |
+ mid.getNodeEx() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
@@ -3980,45 +4293,45 @@ private module FlowExploration {
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
- exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
- partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and
+ exists(CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
+ partialPathIntoCallable(mid, _, cc, innercc, sc1, sc2, call, _, config) and
paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config)
)
}
private predicate partialPathThroughCallable(
- PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
- out = kind.getAnOutNode(call)
+ out.asNode() = kind.getAnOutNode(call)
)
}
private predicate revPartialPathStep(
- PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
+ PartialPathNodeRev mid, NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
RevPartialAccessPath ap, Configuration config
) {
- localFlowStep(node, mid.getNode(), config) and
+ localFlowStep(node, mid.getNodeEx(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalLocalFlowStep(node, mid.getNode(), config) and
+ additionalLocalFlowStep(node, mid.getNodeEx(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
- jumpStep(node, mid.getNode(), config) and
+ jumpStep(node, mid.getNodeEx(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalJumpStep(node, mid.getNode(), config) and
+ additionalJumpStep(node, mid.getNodeEx(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
mid.getAp() instanceof RevPartialAccessPathNil and
@@ -4037,9 +4350,9 @@ private module FlowExploration {
apConsRev(ap, c, ap0, config)
)
or
- exists(ParameterNode p |
- mid.getNode() = p and
- viableParamArg(_, p, node) and
+ exists(ParamNodeEx p |
+ mid.getNodeEx() = p and
+ viableParamArgEx(_, p, node) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
sc1 = TRevSummaryCtx1None() and
@@ -4050,7 +4363,7 @@ private module FlowExploration {
or
exists(ReturnPosition pos |
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
- pos = getReturnPosition(node)
+ pos = getReturnPosition(node.asNode())
)
or
revPartialPathThroughCallable(mid, node, ap, config) and
@@ -4060,12 +4373,13 @@ private module FlowExploration {
pragma[inline]
private predicate revPartialPathReadStep(
- PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
+ PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, NodeEx node,
+ RevPartialAccessPath ap2
) {
- exists(Node midNode |
- midNode = mid.getNode() and
+ exists(NodeEx midNode |
+ midNode = mid.getNodeEx() and
ap1 = mid.getAp() and
- read(node, c, midNode) and
+ read(node, c, midNode, mid.getConfiguration()) and
ap2.getHead() = c and
ap2.len() = unbindInt(ap1.len() + 1)
)
@@ -4083,12 +4397,12 @@ private module FlowExploration {
pragma[nomagic]
private predicate revPartialPathStoreStep(
- PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
+ PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, NodeEx node, Configuration config
) {
- exists(Node midNode, TypedContent tc |
- midNode = mid.getNode() and
+ exists(NodeEx midNode, TypedContent tc |
+ midNode = mid.getNodeEx() and
ap = mid.getAp() and
- store(node, tc, midNode, _) and
+ store(node, tc, midNode, _, config) and
ap.getHead() = c and
config = mid.getConfiguration() and
tc.getContent() = c
@@ -4100,9 +4414,9 @@ private module FlowExploration {
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
DataFlowCall call, RevPartialAccessPath ap, Configuration config
) {
- exists(Node out |
- mid.getNode() = out and
- viableReturnPosOut(call, pos, out) and
+ exists(NodeEx out |
+ mid.getNodeEx() = out and
+ viableReturnPosOutEx(call, pos, out) and
sc1 = TRevSummaryCtx1Some(pos) and
sc2 = TRevSummaryCtx2Some(ap) and
ap = mid.getAp() and
@@ -4115,9 +4429,9 @@ private module FlowExploration {
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
Configuration config
) {
- exists(PartialPathNodeRev mid, ParameterNode p |
- mid.getNode() = p and
- p.isParameterOf(_, pos) and
+ exists(PartialPathNodeRev mid, ParamNodeEx p |
+ mid.getNodeEx() = p and
+ p.getPosition() = pos and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
@@ -4138,11 +4452,11 @@ private module FlowExploration {
pragma[nomagic]
private predicate revPartialPathThroughCallable(
- PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
+ PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, int pos |
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
- node.argumentOf(call, pos)
+ node.asNode().(ArgNode).argumentOf(call, pos)
)
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
index 9498e51e7e61..df59c128ad39 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
@@ -81,6 +81,12 @@ abstract class Configuration extends string {
*/
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
+ /**
+ * Holds if an arbitrary number of implicit read steps of content `c` may be
+ * taken at `node`.
+ */
+ predicate allowImplicitRead(Node node, Content c) { none() }
+
/**
* Gets the virtual dispatch branching limit when calculating field flow.
* This can be overridden to a smaller value to improve performance (a
@@ -182,78 +188,210 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
}
}
-private predicate inBarrier(Node node, Configuration config) {
- config.isBarrierIn(node) and
- config.isSource(node)
+private newtype TNodeEx =
+ TNodeNormal(Node n) or
+ TNodeImplicitRead(Node n, boolean hasRead) {
+ any(Configuration c).allowImplicitRead(n, _) and hasRead = [false, true]
+ }
+
+private class NodeEx extends TNodeEx {
+ string toString() {
+ result = this.asNode().toString()
+ or
+ exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]")
+ }
+
+ Node asNode() { this = TNodeNormal(result) }
+
+ predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) }
+
+ Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) }
+
+ pragma[nomagic]
+ private DataFlowCallable getEnclosingCallable0() {
+ nodeEnclosingCallable(this.projectToNode(), result)
+ }
+
+ pragma[inline]
+ DataFlowCallable getEnclosingCallable() {
+ pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result)
+ }
+
+ pragma[nomagic]
+ private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) }
+
+ pragma[inline]
+ DataFlowType getDataFlowType() {
+ pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result)
+ }
+
+ predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ }
}
-private predicate outBarrier(Node node, Configuration config) {
- config.isBarrierOut(node) and
- config.isSink(node)
+private class ArgNodeEx extends NodeEx {
+ ArgNodeEx() { this.asNode() instanceof ArgNode }
}
-private predicate fullBarrier(Node node, Configuration config) {
- config.isBarrier(node)
- or
- config.isBarrierIn(node) and
- not config.isSource(node)
- or
- config.isBarrierOut(node) and
- not config.isSink(node)
- or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- node = g.getAGuardedNode()
+private class ParamNodeEx extends NodeEx {
+ ParamNodeEx() { this.asNode() instanceof ParamNode }
+
+ predicate isParameterOf(DataFlowCallable c, int i) {
+ this.asNode().(ParamNode).isParameterOf(c, i)
+ }
+
+ int getPosition() { this.isParameterOf(_, result) }
+}
+
+private class RetNodeEx extends NodeEx {
+ RetNodeEx() { this.asNode() instanceof ReturnNodeExt }
+
+ ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) }
+
+ ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() }
+}
+
+private predicate inBarrier(NodeEx node, Configuration config) {
+ exists(Node n |
+ node.asNode() = n and
+ config.isBarrierIn(n) and
+ config.isSource(n)
+ )
+}
+
+private predicate outBarrier(NodeEx node, Configuration config) {
+ exists(Node n |
+ node.asNode() = n and
+ config.isBarrierOut(n) and
+ config.isSink(n)
+ )
+}
+
+private predicate fullBarrier(NodeEx node, Configuration config) {
+ exists(Node n | node.asNode() = n |
+ config.isBarrier(n)
+ or
+ config.isBarrierIn(n) and
+ not config.isSource(n)
+ or
+ config.isBarrierOut(n) and
+ not config.isSink(n)
+ or
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
)
}
+pragma[nomagic]
+private predicate sourceNode(NodeEx node, Configuration config) { config.isSource(node.asNode()) }
+
+pragma[nomagic]
+private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) }
+
/**
* Holds if data can flow in one local step from `node1` to `node2`.
*/
-private predicate localFlowStep(Node node1, Node node2, Configuration config) {
- (
- simpleLocalFlowStep(node1, node2) or
- reverseStepThroughInputOutputAlias(node1, node2)
- ) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ simpleLocalFlowStepExt(n1, n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+ or
+ exists(Node n |
+ config.allowImplicitRead(n, _) and
+ node1.asNode() = n and
+ node2.isImplicitReadNode(n, false)
+ )
}
/**
* Holds if the additional step from `node1` to `node2` does not jump between callables.
*/
-private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
- config.isAdditionalFlowStep(node1, node2) and
- getNodeEnclosingCallable(node1) = getNodeEnclosingCallable(node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ config.isAdditionalFlowStep(n1, n2) and
+ getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+ or
+ exists(Node n |
+ config.allowImplicitRead(n, _) and
+ node1.isImplicitReadNode(n, true) and
+ node2.asNode() = n
+ )
}
/**
* Holds if data can flow from `node1` to `node2` in a way that discards call contexts.
*/
-private predicate jumpStep(Node node1, Node node2, Configuration config) {
- jumpStep(node1, node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ jumpStepCached(n1, n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
}
/**
* Holds if the additional step from `node1` to `node2` jumps between callables.
*/
-private predicate additionalJumpStep(Node node1, Node node2, Configuration config) {
- config.isAdditionalFlowStep(node1, node2) and
- getNodeEnclosingCallable(node1) != getNodeEnclosingCallable(node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ config.isAdditionalFlowStep(n1, n2) and
+ getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+}
+
+private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ read(node1.asNode(), c, node2.asNode())
+ or
+ exists(Node n |
+ node2.isImplicitReadNode(n, true) and
+ node1.isImplicitReadNode(n, _) and
+ config.allowImplicitRead(n, c)
+ )
+}
+
+private predicate store(
+ NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
+) {
+ store(node1.asNode(), tc, node2.asNode(), contentType) and
+ read(_, tc.getContent(), _, config)
+}
+
+pragma[nomagic]
+private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) {
+ viableReturnPosOut(call, pos, out.asNode())
+}
+
+pragma[nomagic]
+private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) {
+ viableParamArg(call, p.asNode(), arg.asNode())
}
/**
@@ -277,39 +415,39 @@ private module Stage1 {
* The Boolean `cc` records whether the node is reached through an
* argument in a call.
*/
- predicate fwdFlow(Node node, Cc cc, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
not fullBarrier(node, config) and
(
- config.isSource(node) and
+ sourceNode(node, config) and
cc = false
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
localFlowStep(mid, node, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
additionalLocalFlowStep(mid, node, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, config) and
jumpStep(mid, node, config) and
cc = false
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, config) and
additionalJumpStep(mid, node, config) and
cc = false
)
or
// store
- exists(Node mid |
+ exists(NodeEx mid |
useFieldFlow(config) and
fwdFlow(mid, cc, config) and
- store(mid, _, node, _) and
+ store(mid, _, node, _, config) and
not outBarrier(mid, config)
)
or
@@ -321,9 +459,9 @@ private module Stage1 {
)
or
// flow into a callable
- exists(Node arg |
+ exists(NodeEx arg |
fwdFlow(arg, _, config) and
- viableParamArg(_, node, arg) and
+ viableParamArgEx(_, node, arg) and
cc = true
)
or
@@ -338,13 +476,13 @@ private module Stage1 {
)
}
- private predicate fwdFlow(Node node, Configuration config) { fwdFlow(node, _, config) }
+ private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
pragma[nomagic]
- private predicate fwdFlowRead(Content c, Node node, Cc cc, Configuration config) {
- exists(Node mid |
+ private predicate fwdFlowRead(Content c, NodeEx node, Cc cc, Configuration config) {
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
- read(mid, c, node)
+ read(mid, c, node, config)
)
}
@@ -353,33 +491,33 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate fwdFlowConsCand(Content c, Configuration config) {
- exists(Node mid, Node node, TypedContent tc |
+ exists(NodeEx mid, NodeEx node, TypedContent tc |
not fullBarrier(node, config) and
useFieldFlow(config) and
fwdFlow(mid, _, config) and
- store(mid, tc, node, _) and
+ store(mid, tc, node, _, config) and
c = tc.getContent()
)
}
pragma[nomagic]
private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc, Configuration config) {
- exists(ReturnNodeExt ret |
+ exists(RetNodeEx ret |
fwdFlow(ret, cc, config) and
- getReturnPosition(ret) = pos
+ ret.getReturnPosition() = pos
)
}
pragma[nomagic]
- private predicate fwdFlowOut(DataFlowCall call, Node out, Cc cc, Configuration config) {
+ private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
exists(ReturnPosition pos |
fwdFlowReturnPosition(pos, cc, config) and
- viableReturnPosOut(call, pos, out)
+ viableReturnPosOutEx(call, pos, out)
)
}
pragma[nomagic]
- private predicate fwdFlowOutFromArg(DataFlowCall call, Node out, Configuration config) {
+ private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out, Configuration config) {
fwdFlowOut(call, out, true, config)
}
@@ -388,9 +526,9 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
- exists(ArgumentNode arg |
+ exists(ArgNodeEx arg |
fwdFlow(arg, cc, config) and
- viableParamArg(call, _, arg)
+ viableParamArgEx(call, _, arg)
)
}
@@ -402,34 +540,34 @@ private module Stage1 {
* the enclosing callable in order to reach a sink.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and
fwdFlow(node, config)
}
pragma[nomagic]
- private predicate revFlow0(Node node, boolean toReturn, Configuration config) {
+ private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStep(node, mid, config) and
revFlow(mid, toReturn, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
additionalLocalFlowStep(node, mid, config) and
revFlow(mid, toReturn, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, config) and
toReturn = false
)
or
- exists(Node mid |
+ exists(NodeEx mid |
additionalJumpStep(node, mid, config) and
revFlow(mid, _, config) and
toReturn = false
@@ -442,8 +580,8 @@ private module Stage1 {
)
or
// read
- exists(Node mid, Content c |
- read(node, c, mid) and
+ exists(NodeEx mid, Content c |
+ read(node, c, mid, config) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
revFlow(mid, toReturn, pragma[only_bind_into](config))
)
@@ -460,7 +598,7 @@ private module Stage1 {
// flow out of a callable
exists(ReturnPosition pos |
revFlowOut(pos, config) and
- getReturnPosition(node) = pos and
+ node.(RetNodeEx).getReturnPosition() = pos and
toReturn = true
)
}
@@ -470,20 +608,20 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Content c, Configuration config) {
- exists(Node mid, Node node |
+ exists(NodeEx mid, NodeEx node |
fwdFlow(node, pragma[only_bind_into](config)) and
- read(node, c, mid) and
+ read(node, c, mid, config) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
revFlow(pragma[only_bind_into](mid), _, pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate revFlowStore(Content c, Node node, boolean toReturn, Configuration config) {
- exists(Node mid, TypedContent tc |
+ private predicate revFlowStore(Content c, NodeEx node, boolean toReturn, Configuration config) {
+ exists(NodeEx mid, TypedContent tc |
revFlow(mid, toReturn, pragma[only_bind_into](config)) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
- store(node, tc, mid, _) and
+ store(node, tc, mid, _, config) and
c = tc.getContent()
)
}
@@ -499,15 +637,15 @@ private module Stage1 {
pragma[nomagic]
predicate viableReturnPosOutNodeCandFwd1(
- DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+ DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
fwdFlowReturnPosition(pos, _, config) and
- viableReturnPosOut(call, pos, out)
+ viableReturnPosOutEx(call, pos, out)
}
pragma[nomagic]
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
- exists(DataFlowCall call, Node out |
+ exists(DataFlowCall call, NodeEx out |
revFlow(out, _, config) and
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
)
@@ -515,33 +653,35 @@ private module Stage1 {
pragma[nomagic]
predicate viableParamArgNodeCandFwd1(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+ DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
- viableParamArg(call, p, arg) and
+ viableParamArgEx(call, p, arg) and
fwdFlow(arg, config)
}
pragma[nomagic]
private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config
+ DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
revFlow(p, toReturn, config) and
viableParamArgNodeCandFwd1(call, p, arg, config)
)
}
pragma[nomagic]
- private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
+ private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg, Configuration config) {
revFlowIn(call, arg, true, config)
}
/**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`.
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
*/
pragma[nomagic]
private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) {
- exists(Node out |
+ exists(NodeEx out |
revFlow(out, toReturn, config) and
fwdFlowOutFromArg(call, out, config)
)
@@ -549,32 +689,33 @@ private module Stage1 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Content c |
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
revFlow(node2, pragma[only_bind_into](config)) and
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
c = tc.getContent() and
exists(ap1)
)
}
pragma[nomagic]
- predicate readStepCand(Node n1, Content c, Node n2, Configuration config) {
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) and
- read(n1, c, n2)
+ read(n1, c, n2, pragma[only_bind_into](config))
}
pragma[nomagic]
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow(node, toReturn, config) and exists(returnAp) and exists(ap)
}
- private predicate throughFlowNodeCand(Node node, Configuration config) {
+ private predicate throughFlowNodeCand(NodeEx node, Configuration config) {
revFlow(node, true, config) and
fwdFlow(node, true, config) and
not inBarrier(node, config) and
@@ -586,9 +727,9 @@ private module Stage1 {
private predicate returnFlowCallableNodeCand(
DataFlowCallable callable, ReturnKindExt kind, Configuration config
) {
- exists(ReturnNodeExt ret |
+ exists(RetNodeEx ret |
throughFlowNodeCand(ret, config) and
- callable = getNodeEnclosingCallable(ret) and
+ callable = ret.getEnclosingCallable() and
kind = ret.getKind()
)
}
@@ -597,50 +738,57 @@ private module Stage1 {
* Holds if flow may enter through `p` and reach a return node making `p` a
* candidate for the origin of a summary.
*/
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
- getNodeEnclosingCallable(p) = c and
+ p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself
- not exists(int pos |
- kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos)
- )
+ not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
+ )
+ }
+
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(ArgNodeEx arg, boolean toReturn |
+ revFlow(arg, toReturn, config) and
+ revFlowInToReturn(call, arg, config) and
+ revFlowIsReturned(call, toReturn, config)
)
}
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, config)) and
fields = count(Content f0 | fwdFlowConsCand(f0, config)) and
conscand = -1 and
- tuples = count(Node n, boolean b | fwdFlow(n, b, config))
+ tuples = count(NodeEx n, boolean b | fwdFlow(n, b, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, config)) and
fields = count(Content f0 | revFlowConsCand(f0, config)) and
conscand = -1 and
- tuples = count(Node n, boolean b | revFlow(n, b, config))
+ tuples = count(NodeEx n, boolean b | revFlow(n, b, config))
}
/* End: Stage 1 logic. */
}
pragma[noinline]
-private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) {
Stage1::revFlow(node2, config) and
localFlowStep(node1, node2, config)
}
pragma[noinline]
-private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) {
Stage1::revFlow(node2, config) and
additionalLocalFlowStep(node1, node2, config)
}
pragma[nomagic]
private predicate viableReturnPosOutNodeCand1(
- DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+ DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
Stage1::revFlow(out, config) and
Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out, config)
@@ -653,9 +801,9 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, ReturnNodeExt ret, Node out, Configuration config
+ DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
) {
- viableReturnPosOutNodeCand1(call, getReturnPosition(ret), out, config) and
+ viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
@@ -663,7 +811,7 @@ private predicate flowOutOfCallNodeCand1(
pragma[nomagic]
private predicate viableParamArgNodeCand1(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+ DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and
Stage1::revFlow(arg, config)
@@ -675,7 +823,7 @@ private predicate viableParamArgNodeCand1(
*/
pragma[nomagic]
private predicate flowIntoCallNodeCand1(
- DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, Configuration config
) {
viableParamArgNodeCand1(call, p, arg, config) and
Stage1::revFlow(p, config) and
@@ -688,9 +836,9 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
-private int branch(Node n1, Configuration conf) {
+private int branch(NodeEx n1, Configuration conf) {
result =
- strictcount(Node n |
+ strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -700,9 +848,9 @@ private int branch(Node n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
-private int join(Node n2, Configuration conf) {
+private int join(NodeEx n2, Configuration conf) {
result =
- strictcount(Node n |
+ strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -716,7 +864,7 @@ private int join(Node n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
exists(int b, int j |
@@ -735,8 +883,7 @@ private predicate flowOutOfCallNodeCand1(
*/
pragma[nomagic]
private predicate flowIntoCallNodeCand1(
- DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
exists(int b, int j |
@@ -762,7 +909,7 @@ private module Stage2 {
bindingset[result, ap]
private ApApprox getApprox(Ap ap) { any() }
- private ApNil getApNil(Node node) { PrevStage::revFlow(node, _) and exists(result) }
+ private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
@@ -805,10 +952,10 @@ private module Stage2 {
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { any() }
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
(
preservesValue = true and
@@ -829,10 +976,20 @@ private module Stage2 {
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
/* Begin: Stage 2 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -842,14 +999,14 @@ private module Stage2 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -860,7 +1017,7 @@ private module Stage2 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -868,7 +1025,7 @@ private module Stage2 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -899,19 +1056,17 @@ private module Stage2 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -935,7 +1090,7 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -944,32 +1099,29 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -979,9 +1131,15 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -992,7 +1150,7 @@ private module Stage2 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
)
@@ -1000,18 +1158,41 @@ private module Stage2 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1021,41 +1202,41 @@ private module Stage2 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -1071,20 +1252,18 @@ private module Stage2 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -1097,7 +1276,7 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -1111,7 +1290,7 @@ private module Stage2 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -1120,10 +1299,10 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -1132,13 +1311,12 @@ private module Stage2 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1146,9 +1324,14 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1160,7 +1343,7 @@ private module Stage2 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -1169,16 +1352,17 @@ private module Stage2 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -1187,7 +1371,7 @@ private module Stage2 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -1199,44 +1383,54 @@ private module Stage2 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 2 logic. */
}
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
- DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
@@ -1245,7 +1439,7 @@ private predicate flowOutOfCallNodeCand2(
pragma[nomagic]
private predicate flowIntoCallNodeCand2(
- DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
@@ -1258,10 +1452,10 @@ private module LocalFlowBigStep {
* A node where some checking is required, and hence the big-step relation
* is not allowed to step over.
*/
- private class FlowCheckNode extends Node {
+ private class FlowCheckNode extends NodeEx {
FlowCheckNode() {
- this instanceof CastNode or
- clearsContent(this, _)
+ castNode(this.asNode()) or
+ clearsContentCached(this.asNode(), _)
}
}
@@ -1269,16 +1463,16 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- predicate localFlowEntry(Node node, Configuration config) {
+ predicate localFlowEntry(NodeEx node, Configuration config) {
Stage2::revFlow(node, config) and
(
- config.isSource(node) or
+ sourceNode(node, config) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
- node instanceof ParameterNode or
- node instanceof OutNodeExt or
- store(_, _, node, _) or
- read(_, _, node) or
+ node instanceof ParamNodeEx or
+ node.asNode() instanceof OutNodeExt or
+ store(_, _, node, _, config) or
+ read(_, _, node, config) or
node instanceof FlowCheckNode
)
}
@@ -1287,23 +1481,25 @@ private module LocalFlowBigStep {
* Holds if `node` can be the last node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- private predicate localFlowExit(Node node, Configuration config) {
- exists(Node next | Stage2::revFlow(next, config) |
+ private predicate localFlowExit(NodeEx node, Configuration config) {
+ exists(NodeEx next | Stage2::revFlow(next, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
- store(node, _, next, _) or
- read(node, _, next)
+ store(node, _, next, _, config) or
+ read(node, _, next, config)
)
or
node instanceof FlowCheckNode
or
- config.isSink(node)
+ sinkNode(node, config)
}
pragma[noinline]
- private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
+ private predicate additionalLocalFlowStepNodeCand2(
+ NodeEx node1, NodeEx node2, Configuration config
+ ) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and
Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config))
@@ -1318,39 +1514,39 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
- Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config,
+ NodeEx node1, NodeEx node2, boolean preservesValue, DataFlowType t, Configuration config,
LocalCallContext cc
) {
- not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
+ not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
(
localFlowEntry(node1, pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
- t = getNodeType(node1)
+ t = node1.getDataFlowType() // irrelevant dummy value
or
additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
- t = getNodeType(node2)
+ t = node2.getDataFlowType()
) and
node1 != node2 and
- cc.relevantFor(getNodeEnclosingCallable(node1)) and
- not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
+ cc.relevantFor(node1.getEnclosingCallable()) and
+ not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
Stage2::revFlow(node2, pragma[only_bind_into](config))
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStepPlus(node1, mid, preservesValue, t, pragma[only_bind_into](config), cc) and
localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof FlowCheckNode and
Stage2::revFlow(node2, pragma[only_bind_into](config))
)
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStepPlus(node1, mid, _, _, pragma[only_bind_into](config), cc) and
additionalLocalFlowStepNodeCand2(mid, node2, config) and
not mid instanceof FlowCheckNode and
preservesValue = false and
- t = getNodeType(node2) and
+ t = node2.getDataFlowType() and
Stage2::revFlow(node2, pragma[only_bind_into](config))
)
)
@@ -1362,8 +1558,8 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, AccessPathFrontNil apf, Configuration config,
- LocalCallContext callContext
+ NodeEx node1, NodeEx node2, boolean preservesValue, AccessPathFrontNil apf,
+ Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, config)
@@ -1383,8 +1579,8 @@ private module Stage3 {
private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(Node node) {
- PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node))
+ private ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
@@ -1428,10 +1624,10 @@ private module Stage3 {
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { any() }
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, node2, preservesValue, ap, config, _) and exists(lcc)
}
@@ -1440,10 +1636,16 @@ private module Stage3 {
private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ pragma[nomagic]
+ private predicate clear(NodeEx node, Ap ap) { ap.isClearedAt(node.asNode()) }
+
+ pragma[nomagic]
+ private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
+
bindingset[node, ap]
- private predicate filter(Node node, Ap ap) {
- not ap.isClearedAt(node) and
- if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any()
+ private predicate filter(NodeEx node, Ap ap) {
+ not clear(node, ap) and
+ if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()
}
bindingset[ap, contentType]
@@ -1454,7 +1656,7 @@ private module Stage3 {
}
/* Begin: Stage 3 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
@@ -1465,6 +1667,16 @@ private module Stage3 {
)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -1474,21 +1686,21 @@ private module Stage3 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
pragma[nomagic]
- private predicate fwdFlow0(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -1499,7 +1711,7 @@ private module Stage3 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -1507,7 +1719,7 @@ private module Stage3 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -1538,19 +1750,17 @@ private module Stage3 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -1574,7 +1784,7 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -1583,32 +1793,29 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1618,9 +1825,15 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1631,7 +1844,7 @@ private module Stage3 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
@@ -1639,18 +1852,41 @@ private module Stage3 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1660,41 +1896,41 @@ private module Stage3 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -1710,20 +1946,18 @@ private module Stage3 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -1736,7 +1970,7 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -1750,7 +1984,7 @@ private module Stage3 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -1759,10 +1993,10 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -1771,13 +2005,12 @@ private module Stage3 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1785,9 +2018,14 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1799,7 +2037,7 @@ private module Stage3 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -1808,16 +2046,17 @@ private module Stage3 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -1826,7 +2065,7 @@ private module Stage3 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -1838,37 +2077,47 @@ private module Stage3 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 3 logic. */
}
@@ -1877,7 +2126,7 @@ private module Stage3 {
* Holds if `argApf` is recorded as the summary context for flow reaching `node`
* and remains relevant for the following pruning stage.
*/
-private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) {
+private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
@@ -1892,7 +2141,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
exists(int tails, int nodes, int apLimit, int tupleLimit |
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
- strictcount(Node n |
+ strictcount(NodeEx n |
Stage3::revFlow(n, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
@@ -2087,8 +2336,8 @@ private module Stage4 {
private ApApprox getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(Node node) {
- PrevStage::revFlow(node, _) and result = TNil(getNodeType(node))
+ private ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
@@ -2127,26 +2376,25 @@ private module Stage4 {
bindingset[innercc, inner, call]
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
resolveReturn(innercc, inner, call)
- or
- innercc.(CallContextCall).matchesCall(call)
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
localFlowEntry(node, config) and
- result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node))
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
}
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, node2, preservesValue, ap.getFront(), config, lcc)
}
pragma[nomagic]
private predicate flowOutOfCall(
- DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
@@ -2155,7 +2403,7 @@ private module Stage4 {
pragma[nomagic]
private predicate flowIntoCall(
- DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
@@ -2164,14 +2412,14 @@ private module Stage4 {
}
bindingset[node, ap]
- private predicate filter(Node node, Ap ap) { any() }
+ private predicate filter(NodeEx node, Ap ap) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
/* Begin: Stage 4 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
@@ -2182,6 +2430,16 @@ private module Stage4 {
)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -2191,21 +2449,21 @@ private module Stage4 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
pragma[nomagic]
- private predicate fwdFlow0(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -2216,7 +2474,7 @@ private module Stage4 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -2224,7 +2482,7 @@ private module Stage4 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -2255,19 +2513,17 @@ private module Stage4 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -2291,7 +2547,7 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -2300,32 +2556,29 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -2335,9 +2588,15 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -2348,7 +2607,7 @@ private module Stage4 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
@@ -2356,18 +2615,41 @@ private module Stage4 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -2377,41 +2659,41 @@ private module Stage4 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -2427,20 +2709,18 @@ private module Stage4 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -2453,7 +2733,7 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -2467,7 +2747,7 @@ private module Stage4 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -2476,10 +2756,10 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -2488,13 +2768,12 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -2502,9 +2781,14 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -2516,7 +2800,7 @@ private module Stage4 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -2525,16 +2809,17 @@ private module Stage4 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -2543,7 +2828,7 @@ private module Stage4 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -2555,37 +2840,47 @@ private module Stage4 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 4 logic. */
}
@@ -2595,18 +2890,18 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
}
-private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) {
+private predicate nodeMayUseSummary(NodeEx n, AccessPathApprox apa, Configuration config) {
exists(DataFlowCallable c, AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and
Stage4::revFlow(n, true, _, apa0, config) and
Stage4::fwdFlow(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
- getNodeEnclosingCallable(n) = c
+ n.getEnclosingCallable() = c
)
}
private newtype TSummaryCtx =
TSummaryCtxNone() or
- TSummaryCtxSome(ParameterNode p, AccessPath ap) {
+ TSummaryCtxSome(ParamNodeEx p, AccessPath ap) {
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _)
}
@@ -2627,7 +2922,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone {
/** A summary context from which a flow summary can be generated. */
private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
- private ParameterNode p;
+ private ParamNodeEx p;
private AccessPath ap;
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
@@ -2660,7 +2955,9 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
- strictcount(Node n | Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config))
+ strictcount(NodeEx n |
+ Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)
+ )
}
/**
@@ -2752,13 +3049,13 @@ private newtype TAccessPath =
}
private newtype TPathNode =
- TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
+ TPathNodeMid(NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
- ap = TAccessPathNil(getNodeType(node))
+ ap = TAccessPathNil(node.getDataFlowType())
or
// ... or a step from an existing PathNode to another node.
exists(PathNodeMid mid |
@@ -2767,12 +3064,12 @@ private newtype TPathNode =
Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config))
)
} or
- TPathNodeSink(Node node, Configuration config) {
- pragma[only_bind_into](config).isSink(node) and
+ TPathNodeSink(NodeEx node, Configuration config) {
+ sinkNode(node, pragma[only_bind_into](config)) and
Stage4::revFlow(node, pragma[only_bind_into](config)) and
(
// A sink that is also a source ...
- config.isSource(node)
+ sourceNode(node, config)
or
// ... or a sink that can be reached from a source
exists(PathNodeMid mid |
@@ -2973,15 +3270,17 @@ class PathNode extends TPathNode {
}
/** Gets the underlying `Node`. */
- Node getNode() { none() }
+ final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
private predicate isHidden() {
- nodeIsHidden(this.getNode()) and
+ hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
+ or
+ this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
}
private PathNode getASuccessorIfHidden() {
@@ -3003,6 +3302,8 @@ class PathNode extends TPathNode {
abstract private class PathNodeImpl extends PathNode {
abstract PathNode getASuccessorImpl();
+ abstract NodeEx getNodeEx();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3017,14 +3318,14 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
}
- override string toString() { result = this.getNode().toString() + ppAp() }
+ override string toString() { result = this.getNodeEx().toString() + ppAp() }
- override string toStringWithContext() { result = this.getNode().toString() + ppAp() + ppCtx() }
+ override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
@@ -3054,7 +3355,7 @@ module PathGraph {
* a `CallContext`, and a `Configuration`.
*/
private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
- Node node;
+ NodeEx node;
CallContext cc;
SummaryCtx sc;
AccessPath ap;
@@ -3062,7 +3363,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) }
- override Node getNode() { result = node }
+ override NodeEx getNodeEx() { result = node }
CallContext getCallContext() { result = cc }
@@ -3073,7 +3374,8 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
override Configuration getConfiguration() { result = config }
private PathNodeMid getSuccMid() {
- pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and
+ pathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx(),
+ result.getAp()) and
result.getConfiguration() = unbindConf(this.getConfiguration())
}
@@ -3084,7 +3386,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid, PathNodeSink sink |
mid = getSuccMid() and
- mid.getNode() = sink.getNode() and
+ mid.getNodeEx() = sink.getNodeEx() and
mid.getAp() instanceof AccessPathNil and
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
result = sink
@@ -3092,7 +3394,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
}
override predicate isSource() {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
@@ -3105,31 +3407,35 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
* excluding the `CallContext`.
*/
private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
- Node node;
+ NodeEx node;
Configuration config;
PathNodeSink() { this = TPathNodeSink(node, config) }
- override Node getNode() { result = node }
+ override NodeEx getNodeEx() { result = node }
override Configuration getConfiguration() { result = config }
override PathNode getASuccessorImpl() { none() }
- override predicate isSource() { config.isSource(node) }
+ override predicate isSource() { sourceNode(node, config) }
}
/**
* Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`.
*/
-private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
- exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
- midnode = mid.getNode() and
+private predicate pathStep(
+ PathNodeMid mid, NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap
+) {
+ exists(AccessPath ap0, NodeEx midnode, Configuration conf, LocalCallContext localCC |
+ midnode = mid.getNodeEx() and
conf = mid.getConfiguration() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
- localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and
+ localCC =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ midnode.getEnclosingCallable()) and
ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
@@ -3139,16 +3445,16 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
ap0 instanceof AccessPathNil
)
or
- jumpStep(mid.getNode(), node, mid.getConfiguration()) and
+ jumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
ap = mid.getAp()
or
- additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and
+ additionalJumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
mid.getAp() instanceof AccessPathNil and
- ap = TAccessPathNil(getNodeType(node))
+ ap = TAccessPathNil(node.getDataFlowType())
or
exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and
sc = mid.getSummaryCtx()
@@ -3165,20 +3471,20 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
pragma[nomagic]
private predicate pathReadStep(
- PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+ PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
- Stage4::readStepCand(mid.getNode(), tc.getContent(), node, mid.getConfiguration()) and
+ Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+ PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- Stage4::storeStepCand(mid.getNode(), _, tc, node, _, mid.getConfiguration()) and
+ Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
cc = mid.getCallContext()
}
@@ -3186,7 +3492,7 @@ private predicate pathOutOfCallable0(
PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa,
Configuration config
) {
- pos = getReturnPosition(mid.getNode()) and
+ pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and
innercc = mid.getCallContext() and
innercc instanceof CallContextNoCall and
apa = mid.getAp().getApprox() and
@@ -3209,10 +3515,10 @@ private predicate pathOutOfCallable1(
}
pragma[noinline]
-private Node getAnOutNodeFlow(
+private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
- result = kind.getAnOutNode(call) and
+ result.asNode() = kind.getAnOutNode(call) and
Stage4::revFlow(result, _, _, apa, config)
}
@@ -3221,7 +3527,7 @@ private Node getAnOutNodeFlow(
* is a return from a callable and is recorded by `cc`, if needed.
*/
pragma[noinline]
-private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
+private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) {
exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config |
pathOutOfCallable1(mid, call, kind, cc, apa, config) and
out = getAnOutNodeFlow(kind, call, apa, config)
@@ -3235,8 +3541,8 @@ pragma[noinline]
private predicate pathIntoArg(
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa
) {
- exists(ArgumentNode arg |
- arg = mid.getNode() and
+ exists(ArgNode arg |
+ arg = mid.getNodeEx().asNode() and
cc = mid.getCallContext() and
arg.argumentOf(call, i) and
ap = mid.getAp() and
@@ -3248,7 +3554,7 @@ pragma[noinline]
private predicate parameterCand(
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
Stage4::revFlow(p, _, _, apa, config) and
p.isParameterOf(callable, i)
)
@@ -3272,7 +3578,7 @@ private predicate pathIntoCallable0(
* respectively.
*/
private predicate pathIntoCallable(
- PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
+ PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
DataFlowCall call
) {
exists(int i, DataFlowCallable callable, AccessPath ap |
@@ -3297,8 +3603,8 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa,
Configuration config
) {
- exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
- mid.getNode() = ret and
+ exists(PathNodeMid mid, RetNodeEx ret, int pos |
+ mid.getNodeEx() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
@@ -3326,7 +3632,7 @@ private predicate pathThroughCallable0(
* The context `cc` is restored to its value prior to entering the callable.
*/
pragma[noinline]
-private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
+private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) {
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
pathThroughCallable0(call, mid, kind, cc, ap, apa) and
out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
@@ -3344,9 +3650,9 @@ private predicate flowsTo(
) {
flowsource.isSource() and
flowsource.getConfiguration() = configuration and
- flowsource.getNode() = source and
+ flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
- flowsink.getNode() = sink
+ flowsink.getNodeEx().asNode() = sink
}
/**
@@ -3361,13 +3667,13 @@ predicate flowsTo(Node source, Node sink, Configuration configuration) {
private predicate finalStats(boolean fwd, int nodes, int fields, int conscand, int tuples) {
fwd = true and
- nodes = count(Node n0 | exists(PathNode pn | pn.getNode() = n0)) and
+ nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
tuples = count(PathNode pn)
or
fwd = false and
- nodes = count(Node n0 | exists(PathNode pn | pn.getNode() = n0 and reach(pn))) and
+ nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
tuples = count(PathNode pn | reach(pn))
@@ -3404,19 +3710,19 @@ predicate stageStats(
private module FlowExploration {
private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2, Configuration config) {
- exists(Node node1, Node node2 |
+ exists(NodeEx node1, NodeEx node2 |
jumpStep(node1, node2, config)
or
additionalJumpStep(node1, node2, config)
or
// flow into callable
- viableParamArg(_, node2, node1)
+ viableParamArgEx(_, node2, node1)
or
// flow out of a callable
- viableReturnPosOut(_, getReturnPosition(node1), node2)
+ viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2)
|
- c1 = getNodeEnclosingCallable(node1) and
- c2 = getNodeEnclosingCallable(node2) and
+ c1 = node1.getEnclosingCallable() and
+ c2 = node2.getEnclosingCallable() and
c1 != c2
)
}
@@ -3568,7 +3874,7 @@ private module FlowExploration {
private newtype TSummaryCtx1 =
TSummaryCtx1None() or
- TSummaryCtx1Param(ParameterNode p)
+ TSummaryCtx1Param(ParamNodeEx p)
private newtype TSummaryCtx2 =
TSummaryCtx2None() or
@@ -3584,25 +3890,25 @@ private module FlowExploration {
private newtype TPartialPathNode =
TPartialPathNodeFwd(
- Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
+ NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
- distSrc(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
+ distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
- Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
+ NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
Configuration config
) {
- config.isSink(node) and
+ sinkNode(node, config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil() and
@@ -3611,23 +3917,23 @@ private module FlowExploration {
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
- not clearsContent(node, ap.getHead()) and
+ not clearsContentCached(node.asNode(), ap.getHead()) and
not fullBarrier(node, config) and
- distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
+ distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
}
pragma[nomagic]
private predicate partialPathNodeMk0(
- Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
+ NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
- not clearsContent(node, ap.getHead().getContent()) and
- if node instanceof CastingNode
- then compatibleTypes(getNodeType(node), ap.getType())
+ not clearsContentCached(node.asNode(), ap.getHead().getContent()) and
+ if node.asNode() instanceof CastingNode
+ then compatibleTypes(node.getDataFlowType(), ap.getType())
else any()
)
}
@@ -3637,13 +3943,15 @@ private module FlowExploration {
*/
class PartialPathNode extends TPartialPathNode {
/** Gets a textual representation of this element. */
- string toString() { result = this.getNode().toString() + this.ppAp() }
+ string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/**
* Gets a textual representation of this element, including a textual
* representation of the call context.
*/
- string toStringWithContext() { result = this.getNode().toString() + this.ppAp() + this.ppCtx() }
+ string toStringWithContext() {
+ result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
+ }
/**
* Holds if this element is at the specified location.
@@ -3655,11 +3963,16 @@ private module FlowExploration {
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the underlying `Node`. */
- Node getNode() { none() }
+ final Node getNode() { this.getNodeEx().projectToNode() = result }
+
+ private NodeEx getNodeEx() {
+ result = this.(PartialPathNodeFwd).getNodeEx() or
+ result = this.(PartialPathNodeRev).getNodeEx()
+ }
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
@@ -3672,7 +3985,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSourceDistance() {
- result = distSrc(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
+ result = distSrc(this.getNodeEx().getEnclosingCallable(), this.getConfiguration())
}
/**
@@ -3680,7 +3993,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSinkDistance() {
- result = distSink(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
+ result = distSink(this.getNodeEx().getEnclosingCallable(), this.getConfiguration())
}
private string ppAp() {
@@ -3712,7 +4025,7 @@ private module FlowExploration {
}
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
- Node node;
+ NodeEx node;
CallContext cc;
TSummaryCtx1 sc1;
TSummaryCtx2 sc2;
@@ -3721,7 +4034,7 @@ private module FlowExploration {
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
- override Node getNode() { result = node }
+ NodeEx getNodeEx() { result = node }
CallContext getCallContext() { result = cc }
@@ -3734,12 +4047,12 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodeFwd getASuccessor() {
- partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
+ partialPathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx1(),
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
}
predicate isSource() {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
@@ -3748,7 +4061,7 @@ private module FlowExploration {
}
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
- Node node;
+ NodeEx node;
TRevSummaryCtx1 sc1;
TRevSummaryCtx2 sc2;
RevPartialAccessPath ap;
@@ -3756,7 +4069,7 @@ private module FlowExploration {
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
- override Node getNode() { result = node }
+ NodeEx getNodeEx() { result = node }
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
@@ -3767,12 +4080,12 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodeRev getASuccessor() {
- revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
+ revPartialPathStep(result, this.getNodeEx(), this.getSummaryCtx1(), this.getSummaryCtx2(),
this.getAp(), this.getConfiguration())
}
predicate isSink() {
- config.isSink(node) and
+ sinkNode(node, config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil()
@@ -3780,40 +4093,40 @@ private module FlowExploration {
}
private predicate partialPathStep(
- PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
+ PartialPathNodeFwd mid, NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
- not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
+ not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and
(
- localFlowStep(mid.getNode(), node, config) and
+ localFlowStep(mid.getNodeEx(), node, config) and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalLocalFlowStep(mid.getNode(), node, config) and
+ additionalLocalFlowStep(mid.getNodeEx(), node, config) and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
config = mid.getConfiguration()
)
or
- jumpStep(mid.getNode(), node, config) and
+ jumpStep(mid.getNodeEx(), node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalJumpStep(mid.getNode(), node, config) and
+ additionalJumpStep(mid.getNodeEx(), node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
config = mid.getConfiguration()
or
partialPathStoreStep(mid, _, _, node, ap) and
@@ -3826,8 +4139,7 @@ private module FlowExploration {
partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, tc, ap0, config) and
- compatibleTypes(ap.getType(), getNodeType(node))
+ apConsFwd(ap, tc, ap0, config)
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -3846,12 +4158,13 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
+ PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, NodeEx node,
+ PartialAccessPath ap2
) {
- exists(Node midNode, DataFlowType contentType |
- midNode = mid.getNode() and
+ exists(NodeEx midNode, DataFlowType contentType |
+ midNode = mid.getNodeEx() and
ap1 = mid.getAp() and
- store(midNode, tc, node, contentType) and
+ store(midNode, tc, node, contentType, mid.getConfiguration()) and
ap2.getHead() = tc and
ap2.len() = unbindInt(ap1.len() + 1) and
compatibleTypes(ap1.getType(), contentType)
@@ -3870,15 +4183,15 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
+ PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, NodeEx node, CallContext cc,
Configuration config
) {
- exists(Node midNode |
- midNode = mid.getNode() and
+ exists(NodeEx midNode |
+ midNode = mid.getNodeEx() and
ap = mid.getAp() and
- read(midNode, tc.getContent(), node) and
+ read(midNode, tc.getContent(), node, pragma[only_bind_into](config)) and
ap.getHead() = tc and
- config = mid.getConfiguration() and
+ pragma[only_bind_into](config) = mid.getConfiguration() and
cc = mid.getCallContext()
)
}
@@ -3887,7 +4200,7 @@ private module FlowExploration {
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
Configuration config
) {
- pos = getReturnPosition(mid.getNode()) and
+ pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and
innercc = mid.getCallContext() and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
@@ -3910,12 +4223,12 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
- PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = kind.getAnOutNode(call)
+ out.asNode() = kind.getAnOutNode(call)
)
}
@@ -3924,8 +4237,8 @@ private module FlowExploration {
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
- exists(ArgumentNode arg |
- arg = mid.getNode() and
+ exists(ArgNode arg |
+ arg = mid.getNodeEx().asNode() and
cc = mid.getCallContext() and
arg.argumentOf(call, i) and
ap = mid.getAp() and
@@ -3943,7 +4256,7 @@ private module FlowExploration {
}
private predicate partialPathIntoCallable(
- PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
+ PartialPathNodeFwd mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc,
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
@@ -3964,8 +4277,8 @@ private module FlowExploration {
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
- exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
- mid.getNode() = ret and
+ exists(PartialPathNodeFwd mid, RetNodeEx ret |
+ mid.getNodeEx() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
@@ -3980,45 +4293,45 @@ private module FlowExploration {
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
- exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
- partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and
+ exists(CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
+ partialPathIntoCallable(mid, _, cc, innercc, sc1, sc2, call, _, config) and
paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config)
)
}
private predicate partialPathThroughCallable(
- PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
- out = kind.getAnOutNode(call)
+ out.asNode() = kind.getAnOutNode(call)
)
}
private predicate revPartialPathStep(
- PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
+ PartialPathNodeRev mid, NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
RevPartialAccessPath ap, Configuration config
) {
- localFlowStep(node, mid.getNode(), config) and
+ localFlowStep(node, mid.getNodeEx(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalLocalFlowStep(node, mid.getNode(), config) and
+ additionalLocalFlowStep(node, mid.getNodeEx(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
- jumpStep(node, mid.getNode(), config) and
+ jumpStep(node, mid.getNodeEx(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalJumpStep(node, mid.getNode(), config) and
+ additionalJumpStep(node, mid.getNodeEx(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
mid.getAp() instanceof RevPartialAccessPathNil and
@@ -4037,9 +4350,9 @@ private module FlowExploration {
apConsRev(ap, c, ap0, config)
)
or
- exists(ParameterNode p |
- mid.getNode() = p and
- viableParamArg(_, p, node) and
+ exists(ParamNodeEx p |
+ mid.getNodeEx() = p and
+ viableParamArgEx(_, p, node) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
sc1 = TRevSummaryCtx1None() and
@@ -4050,7 +4363,7 @@ private module FlowExploration {
or
exists(ReturnPosition pos |
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
- pos = getReturnPosition(node)
+ pos = getReturnPosition(node.asNode())
)
or
revPartialPathThroughCallable(mid, node, ap, config) and
@@ -4060,12 +4373,13 @@ private module FlowExploration {
pragma[inline]
private predicate revPartialPathReadStep(
- PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
+ PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, NodeEx node,
+ RevPartialAccessPath ap2
) {
- exists(Node midNode |
- midNode = mid.getNode() and
+ exists(NodeEx midNode |
+ midNode = mid.getNodeEx() and
ap1 = mid.getAp() and
- read(node, c, midNode) and
+ read(node, c, midNode, mid.getConfiguration()) and
ap2.getHead() = c and
ap2.len() = unbindInt(ap1.len() + 1)
)
@@ -4083,12 +4397,12 @@ private module FlowExploration {
pragma[nomagic]
private predicate revPartialPathStoreStep(
- PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
+ PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, NodeEx node, Configuration config
) {
- exists(Node midNode, TypedContent tc |
- midNode = mid.getNode() and
+ exists(NodeEx midNode, TypedContent tc |
+ midNode = mid.getNodeEx() and
ap = mid.getAp() and
- store(node, tc, midNode, _) and
+ store(node, tc, midNode, _, config) and
ap.getHead() = c and
config = mid.getConfiguration() and
tc.getContent() = c
@@ -4100,9 +4414,9 @@ private module FlowExploration {
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
DataFlowCall call, RevPartialAccessPath ap, Configuration config
) {
- exists(Node out |
- mid.getNode() = out and
- viableReturnPosOut(call, pos, out) and
+ exists(NodeEx out |
+ mid.getNodeEx() = out and
+ viableReturnPosOutEx(call, pos, out) and
sc1 = TRevSummaryCtx1Some(pos) and
sc2 = TRevSummaryCtx2Some(ap) and
ap = mid.getAp() and
@@ -4115,9 +4429,9 @@ private module FlowExploration {
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
Configuration config
) {
- exists(PartialPathNodeRev mid, ParameterNode p |
- mid.getNode() = p and
- p.isParameterOf(_, pos) and
+ exists(PartialPathNodeRev mid, ParamNodeEx p |
+ mid.getNodeEx() = p and
+ p.getPosition() = pos and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
@@ -4138,11 +4452,11 @@ private module FlowExploration {
pragma[nomagic]
private predicate revPartialPathThroughCallable(
- PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
+ PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, int pos |
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
- node.argumentOf(call, pos)
+ node.asNode().(ArgNode).argumentOf(call, pos)
)
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
index 9498e51e7e61..df59c128ad39 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
@@ -81,6 +81,12 @@ abstract class Configuration extends string {
*/
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
+ /**
+ * Holds if an arbitrary number of implicit read steps of content `c` may be
+ * taken at `node`.
+ */
+ predicate allowImplicitRead(Node node, Content c) { none() }
+
/**
* Gets the virtual dispatch branching limit when calculating field flow.
* This can be overridden to a smaller value to improve performance (a
@@ -182,78 +188,210 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
}
}
-private predicate inBarrier(Node node, Configuration config) {
- config.isBarrierIn(node) and
- config.isSource(node)
+private newtype TNodeEx =
+ TNodeNormal(Node n) or
+ TNodeImplicitRead(Node n, boolean hasRead) {
+ any(Configuration c).allowImplicitRead(n, _) and hasRead = [false, true]
+ }
+
+private class NodeEx extends TNodeEx {
+ string toString() {
+ result = this.asNode().toString()
+ or
+ exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]")
+ }
+
+ Node asNode() { this = TNodeNormal(result) }
+
+ predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) }
+
+ Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) }
+
+ pragma[nomagic]
+ private DataFlowCallable getEnclosingCallable0() {
+ nodeEnclosingCallable(this.projectToNode(), result)
+ }
+
+ pragma[inline]
+ DataFlowCallable getEnclosingCallable() {
+ pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result)
+ }
+
+ pragma[nomagic]
+ private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) }
+
+ pragma[inline]
+ DataFlowType getDataFlowType() {
+ pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result)
+ }
+
+ predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ }
}
-private predicate outBarrier(Node node, Configuration config) {
- config.isBarrierOut(node) and
- config.isSink(node)
+private class ArgNodeEx extends NodeEx {
+ ArgNodeEx() { this.asNode() instanceof ArgNode }
}
-private predicate fullBarrier(Node node, Configuration config) {
- config.isBarrier(node)
- or
- config.isBarrierIn(node) and
- not config.isSource(node)
- or
- config.isBarrierOut(node) and
- not config.isSink(node)
- or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- node = g.getAGuardedNode()
+private class ParamNodeEx extends NodeEx {
+ ParamNodeEx() { this.asNode() instanceof ParamNode }
+
+ predicate isParameterOf(DataFlowCallable c, int i) {
+ this.asNode().(ParamNode).isParameterOf(c, i)
+ }
+
+ int getPosition() { this.isParameterOf(_, result) }
+}
+
+private class RetNodeEx extends NodeEx {
+ RetNodeEx() { this.asNode() instanceof ReturnNodeExt }
+
+ ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) }
+
+ ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() }
+}
+
+private predicate inBarrier(NodeEx node, Configuration config) {
+ exists(Node n |
+ node.asNode() = n and
+ config.isBarrierIn(n) and
+ config.isSource(n)
+ )
+}
+
+private predicate outBarrier(NodeEx node, Configuration config) {
+ exists(Node n |
+ node.asNode() = n and
+ config.isBarrierOut(n) and
+ config.isSink(n)
+ )
+}
+
+private predicate fullBarrier(NodeEx node, Configuration config) {
+ exists(Node n | node.asNode() = n |
+ config.isBarrier(n)
+ or
+ config.isBarrierIn(n) and
+ not config.isSource(n)
+ or
+ config.isBarrierOut(n) and
+ not config.isSink(n)
+ or
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
)
}
+pragma[nomagic]
+private predicate sourceNode(NodeEx node, Configuration config) { config.isSource(node.asNode()) }
+
+pragma[nomagic]
+private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) }
+
/**
* Holds if data can flow in one local step from `node1` to `node2`.
*/
-private predicate localFlowStep(Node node1, Node node2, Configuration config) {
- (
- simpleLocalFlowStep(node1, node2) or
- reverseStepThroughInputOutputAlias(node1, node2)
- ) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ simpleLocalFlowStepExt(n1, n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+ or
+ exists(Node n |
+ config.allowImplicitRead(n, _) and
+ node1.asNode() = n and
+ node2.isImplicitReadNode(n, false)
+ )
}
/**
* Holds if the additional step from `node1` to `node2` does not jump between callables.
*/
-private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
- config.isAdditionalFlowStep(node1, node2) and
- getNodeEnclosingCallable(node1) = getNodeEnclosingCallable(node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ config.isAdditionalFlowStep(n1, n2) and
+ getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+ or
+ exists(Node n |
+ config.allowImplicitRead(n, _) and
+ node1.isImplicitReadNode(n, true) and
+ node2.asNode() = n
+ )
}
/**
* Holds if data can flow from `node1` to `node2` in a way that discards call contexts.
*/
-private predicate jumpStep(Node node1, Node node2, Configuration config) {
- jumpStep(node1, node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ jumpStepCached(n1, n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
}
/**
* Holds if the additional step from `node1` to `node2` jumps between callables.
*/
-private predicate additionalJumpStep(Node node1, Node node2, Configuration config) {
- config.isAdditionalFlowStep(node1, node2) and
- getNodeEnclosingCallable(node1) != getNodeEnclosingCallable(node2) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, config)
+private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration config) {
+ exists(Node n1, Node n2 |
+ node1.asNode() = n1 and
+ node2.asNode() = n2 and
+ config.isAdditionalFlowStep(n1, n2) and
+ getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
+ not outBarrier(node1, config) and
+ not inBarrier(node2, config) and
+ not fullBarrier(node1, config) and
+ not fullBarrier(node2, config)
+ )
+}
+
+private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ read(node1.asNode(), c, node2.asNode())
+ or
+ exists(Node n |
+ node2.isImplicitReadNode(n, true) and
+ node1.isImplicitReadNode(n, _) and
+ config.allowImplicitRead(n, c)
+ )
+}
+
+private predicate store(
+ NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
+) {
+ store(node1.asNode(), tc, node2.asNode(), contentType) and
+ read(_, tc.getContent(), _, config)
+}
+
+pragma[nomagic]
+private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) {
+ viableReturnPosOut(call, pos, out.asNode())
+}
+
+pragma[nomagic]
+private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) {
+ viableParamArg(call, p.asNode(), arg.asNode())
}
/**
@@ -277,39 +415,39 @@ private module Stage1 {
* The Boolean `cc` records whether the node is reached through an
* argument in a call.
*/
- predicate fwdFlow(Node node, Cc cc, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
not fullBarrier(node, config) and
(
- config.isSource(node) and
+ sourceNode(node, config) and
cc = false
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
localFlowStep(mid, node, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
additionalLocalFlowStep(mid, node, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, config) and
jumpStep(mid, node, config) and
cc = false
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, config) and
additionalJumpStep(mid, node, config) and
cc = false
)
or
// store
- exists(Node mid |
+ exists(NodeEx mid |
useFieldFlow(config) and
fwdFlow(mid, cc, config) and
- store(mid, _, node, _) and
+ store(mid, _, node, _, config) and
not outBarrier(mid, config)
)
or
@@ -321,9 +459,9 @@ private module Stage1 {
)
or
// flow into a callable
- exists(Node arg |
+ exists(NodeEx arg |
fwdFlow(arg, _, config) and
- viableParamArg(_, node, arg) and
+ viableParamArgEx(_, node, arg) and
cc = true
)
or
@@ -338,13 +476,13 @@ private module Stage1 {
)
}
- private predicate fwdFlow(Node node, Configuration config) { fwdFlow(node, _, config) }
+ private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
pragma[nomagic]
- private predicate fwdFlowRead(Content c, Node node, Cc cc, Configuration config) {
- exists(Node mid |
+ private predicate fwdFlowRead(Content c, NodeEx node, Cc cc, Configuration config) {
+ exists(NodeEx mid |
fwdFlow(mid, cc, config) and
- read(mid, c, node)
+ read(mid, c, node, config)
)
}
@@ -353,33 +491,33 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate fwdFlowConsCand(Content c, Configuration config) {
- exists(Node mid, Node node, TypedContent tc |
+ exists(NodeEx mid, NodeEx node, TypedContent tc |
not fullBarrier(node, config) and
useFieldFlow(config) and
fwdFlow(mid, _, config) and
- store(mid, tc, node, _) and
+ store(mid, tc, node, _, config) and
c = tc.getContent()
)
}
pragma[nomagic]
private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc, Configuration config) {
- exists(ReturnNodeExt ret |
+ exists(RetNodeEx ret |
fwdFlow(ret, cc, config) and
- getReturnPosition(ret) = pos
+ ret.getReturnPosition() = pos
)
}
pragma[nomagic]
- private predicate fwdFlowOut(DataFlowCall call, Node out, Cc cc, Configuration config) {
+ private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
exists(ReturnPosition pos |
fwdFlowReturnPosition(pos, cc, config) and
- viableReturnPosOut(call, pos, out)
+ viableReturnPosOutEx(call, pos, out)
)
}
pragma[nomagic]
- private predicate fwdFlowOutFromArg(DataFlowCall call, Node out, Configuration config) {
+ private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out, Configuration config) {
fwdFlowOut(call, out, true, config)
}
@@ -388,9 +526,9 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
- exists(ArgumentNode arg |
+ exists(ArgNodeEx arg |
fwdFlow(arg, cc, config) and
- viableParamArg(call, _, arg)
+ viableParamArgEx(call, _, arg)
)
}
@@ -402,34 +540,34 @@ private module Stage1 {
* the enclosing callable in order to reach a sink.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and
fwdFlow(node, config)
}
pragma[nomagic]
- private predicate revFlow0(Node node, boolean toReturn, Configuration config) {
+ private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
fwdFlow(node, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStep(node, mid, config) and
revFlow(mid, toReturn, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
additionalLocalFlowStep(node, mid, config) and
revFlow(mid, toReturn, config)
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, config) and
toReturn = false
)
or
- exists(Node mid |
+ exists(NodeEx mid |
additionalJumpStep(node, mid, config) and
revFlow(mid, _, config) and
toReturn = false
@@ -442,8 +580,8 @@ private module Stage1 {
)
or
// read
- exists(Node mid, Content c |
- read(node, c, mid) and
+ exists(NodeEx mid, Content c |
+ read(node, c, mid, config) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
revFlow(mid, toReturn, pragma[only_bind_into](config))
)
@@ -460,7 +598,7 @@ private module Stage1 {
// flow out of a callable
exists(ReturnPosition pos |
revFlowOut(pos, config) and
- getReturnPosition(node) = pos and
+ node.(RetNodeEx).getReturnPosition() = pos and
toReturn = true
)
}
@@ -470,20 +608,20 @@ private module Stage1 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Content c, Configuration config) {
- exists(Node mid, Node node |
+ exists(NodeEx mid, NodeEx node |
fwdFlow(node, pragma[only_bind_into](config)) and
- read(node, c, mid) and
+ read(node, c, mid, config) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
revFlow(pragma[only_bind_into](mid), _, pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate revFlowStore(Content c, Node node, boolean toReturn, Configuration config) {
- exists(Node mid, TypedContent tc |
+ private predicate revFlowStore(Content c, NodeEx node, boolean toReturn, Configuration config) {
+ exists(NodeEx mid, TypedContent tc |
revFlow(mid, toReturn, pragma[only_bind_into](config)) and
fwdFlowConsCand(c, pragma[only_bind_into](config)) and
- store(node, tc, mid, _) and
+ store(node, tc, mid, _, config) and
c = tc.getContent()
)
}
@@ -499,15 +637,15 @@ private module Stage1 {
pragma[nomagic]
predicate viableReturnPosOutNodeCandFwd1(
- DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+ DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
fwdFlowReturnPosition(pos, _, config) and
- viableReturnPosOut(call, pos, out)
+ viableReturnPosOutEx(call, pos, out)
}
pragma[nomagic]
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
- exists(DataFlowCall call, Node out |
+ exists(DataFlowCall call, NodeEx out |
revFlow(out, _, config) and
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
)
@@ -515,33 +653,35 @@ private module Stage1 {
pragma[nomagic]
predicate viableParamArgNodeCandFwd1(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+ DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
- viableParamArg(call, p, arg) and
+ viableParamArgEx(call, p, arg) and
fwdFlow(arg, config)
}
pragma[nomagic]
private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config
+ DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
revFlow(p, toReturn, config) and
viableParamArgNodeCandFwd1(call, p, arg, config)
)
}
pragma[nomagic]
- private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
+ private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg, Configuration config) {
revFlowIn(call, arg, true, config)
}
/**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`.
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
*/
pragma[nomagic]
private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) {
- exists(Node out |
+ exists(NodeEx out |
revFlow(out, toReturn, config) and
fwdFlowOutFromArg(call, out, config)
)
@@ -549,32 +689,33 @@ private module Stage1 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Content c |
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
revFlow(node2, pragma[only_bind_into](config)) and
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
c = tc.getContent() and
exists(ap1)
)
}
pragma[nomagic]
- predicate readStepCand(Node n1, Content c, Node n2, Configuration config) {
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) and
- read(n1, c, n2)
+ read(n1, c, n2, pragma[only_bind_into](config))
}
pragma[nomagic]
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow(node, toReturn, config) and exists(returnAp) and exists(ap)
}
- private predicate throughFlowNodeCand(Node node, Configuration config) {
+ private predicate throughFlowNodeCand(NodeEx node, Configuration config) {
revFlow(node, true, config) and
fwdFlow(node, true, config) and
not inBarrier(node, config) and
@@ -586,9 +727,9 @@ private module Stage1 {
private predicate returnFlowCallableNodeCand(
DataFlowCallable callable, ReturnKindExt kind, Configuration config
) {
- exists(ReturnNodeExt ret |
+ exists(RetNodeEx ret |
throughFlowNodeCand(ret, config) and
- callable = getNodeEnclosingCallable(ret) and
+ callable = ret.getEnclosingCallable() and
kind = ret.getKind()
)
}
@@ -597,50 +738,57 @@ private module Stage1 {
* Holds if flow may enter through `p` and reach a return node making `p` a
* candidate for the origin of a summary.
*/
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
- getNodeEnclosingCallable(p) = c and
+ p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself
- not exists(int pos |
- kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos)
- )
+ not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
+ )
+ }
+
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(ArgNodeEx arg, boolean toReturn |
+ revFlow(arg, toReturn, config) and
+ revFlowInToReturn(call, arg, config) and
+ revFlowIsReturned(call, toReturn, config)
)
}
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, config)) and
fields = count(Content f0 | fwdFlowConsCand(f0, config)) and
conscand = -1 and
- tuples = count(Node n, boolean b | fwdFlow(n, b, config))
+ tuples = count(NodeEx n, boolean b | fwdFlow(n, b, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, config)) and
fields = count(Content f0 | revFlowConsCand(f0, config)) and
conscand = -1 and
- tuples = count(Node n, boolean b | revFlow(n, b, config))
+ tuples = count(NodeEx n, boolean b | revFlow(n, b, config))
}
/* End: Stage 1 logic. */
}
pragma[noinline]
-private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) {
Stage1::revFlow(node2, config) and
localFlowStep(node1, node2, config)
}
pragma[noinline]
-private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) {
Stage1::revFlow(node2, config) and
additionalLocalFlowStep(node1, node2, config)
}
pragma[nomagic]
private predicate viableReturnPosOutNodeCand1(
- DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+ DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) {
Stage1::revFlow(out, config) and
Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out, config)
@@ -653,9 +801,9 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, ReturnNodeExt ret, Node out, Configuration config
+ DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
) {
- viableReturnPosOutNodeCand1(call, getReturnPosition(ret), out, config) and
+ viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
@@ -663,7 +811,7 @@ private predicate flowOutOfCallNodeCand1(
pragma[nomagic]
private predicate viableParamArgNodeCand1(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+ DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) {
Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and
Stage1::revFlow(arg, config)
@@ -675,7 +823,7 @@ private predicate viableParamArgNodeCand1(
*/
pragma[nomagic]
private predicate flowIntoCallNodeCand1(
- DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, Configuration config
) {
viableParamArgNodeCand1(call, p, arg, config) and
Stage1::revFlow(p, config) and
@@ -688,9 +836,9 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
-private int branch(Node n1, Configuration conf) {
+private int branch(NodeEx n1, Configuration conf) {
result =
- strictcount(Node n |
+ strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -700,9 +848,9 @@ private int branch(Node n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
-private int join(Node n2, Configuration conf) {
+private int join(NodeEx n2, Configuration conf) {
result =
- strictcount(Node n |
+ strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -716,7 +864,7 @@ private int join(Node n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
exists(int b, int j |
@@ -735,8 +883,7 @@ private predicate flowOutOfCallNodeCand1(
*/
pragma[nomagic]
private predicate flowIntoCallNodeCand1(
- DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
exists(int b, int j |
@@ -762,7 +909,7 @@ private module Stage2 {
bindingset[result, ap]
private ApApprox getApprox(Ap ap) { any() }
- private ApNil getApNil(Node node) { PrevStage::revFlow(node, _) and exists(result) }
+ private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
@@ -805,10 +952,10 @@ private module Stage2 {
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { any() }
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
(
preservesValue = true and
@@ -829,10 +976,20 @@ private module Stage2 {
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
/* Begin: Stage 2 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -842,14 +999,14 @@ private module Stage2 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -860,7 +1017,7 @@ private module Stage2 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -868,7 +1025,7 @@ private module Stage2 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -899,19 +1056,17 @@ private module Stage2 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -935,7 +1090,7 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -944,32 +1099,29 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -979,9 +1131,15 @@ private module Stage2 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -992,7 +1150,7 @@ private module Stage2 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
)
@@ -1000,18 +1158,41 @@ private module Stage2 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1021,41 +1202,41 @@ private module Stage2 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -1071,20 +1252,18 @@ private module Stage2 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -1097,7 +1276,7 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -1111,7 +1290,7 @@ private module Stage2 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -1120,10 +1299,10 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -1132,13 +1311,12 @@ private module Stage2 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1146,9 +1324,14 @@ private module Stage2 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1160,7 +1343,7 @@ private module Stage2 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -1169,16 +1352,17 @@ private module Stage2 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -1187,7 +1371,7 @@ private module Stage2 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -1199,44 +1383,54 @@ private module Stage2 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 2 logic. */
}
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
- DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
@@ -1245,7 +1439,7 @@ private predicate flowOutOfCallNodeCand2(
pragma[nomagic]
private predicate flowIntoCallNodeCand2(
- DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
@@ -1258,10 +1452,10 @@ private module LocalFlowBigStep {
* A node where some checking is required, and hence the big-step relation
* is not allowed to step over.
*/
- private class FlowCheckNode extends Node {
+ private class FlowCheckNode extends NodeEx {
FlowCheckNode() {
- this instanceof CastNode or
- clearsContent(this, _)
+ castNode(this.asNode()) or
+ clearsContentCached(this.asNode(), _)
}
}
@@ -1269,16 +1463,16 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- predicate localFlowEntry(Node node, Configuration config) {
+ predicate localFlowEntry(NodeEx node, Configuration config) {
Stage2::revFlow(node, config) and
(
- config.isSource(node) or
+ sourceNode(node, config) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
- node instanceof ParameterNode or
- node instanceof OutNodeExt or
- store(_, _, node, _) or
- read(_, _, node) or
+ node instanceof ParamNodeEx or
+ node.asNode() instanceof OutNodeExt or
+ store(_, _, node, _, config) or
+ read(_, _, node, config) or
node instanceof FlowCheckNode
)
}
@@ -1287,23 +1481,25 @@ private module LocalFlowBigStep {
* Holds if `node` can be the last node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- private predicate localFlowExit(Node node, Configuration config) {
- exists(Node next | Stage2::revFlow(next, config) |
+ private predicate localFlowExit(NodeEx node, Configuration config) {
+ exists(NodeEx next | Stage2::revFlow(next, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
- store(node, _, next, _) or
- read(node, _, next)
+ store(node, _, next, _, config) or
+ read(node, _, next, config)
)
or
node instanceof FlowCheckNode
or
- config.isSink(node)
+ sinkNode(node, config)
}
pragma[noinline]
- private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
+ private predicate additionalLocalFlowStepNodeCand2(
+ NodeEx node1, NodeEx node2, Configuration config
+ ) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and
Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config))
@@ -1318,39 +1514,39 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
- Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config,
+ NodeEx node1, NodeEx node2, boolean preservesValue, DataFlowType t, Configuration config,
LocalCallContext cc
) {
- not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
+ not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
(
localFlowEntry(node1, pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
- t = getNodeType(node1)
+ t = node1.getDataFlowType() // irrelevant dummy value
or
additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
- t = getNodeType(node2)
+ t = node2.getDataFlowType()
) and
node1 != node2 and
- cc.relevantFor(getNodeEnclosingCallable(node1)) and
- not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
+ cc.relevantFor(node1.getEnclosingCallable()) and
+ not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
Stage2::revFlow(node2, pragma[only_bind_into](config))
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStepPlus(node1, mid, preservesValue, t, pragma[only_bind_into](config), cc) and
localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof FlowCheckNode and
Stage2::revFlow(node2, pragma[only_bind_into](config))
)
or
- exists(Node mid |
+ exists(NodeEx mid |
localFlowStepPlus(node1, mid, _, _, pragma[only_bind_into](config), cc) and
additionalLocalFlowStepNodeCand2(mid, node2, config) and
not mid instanceof FlowCheckNode and
preservesValue = false and
- t = getNodeType(node2) and
+ t = node2.getDataFlowType() and
Stage2::revFlow(node2, pragma[only_bind_into](config))
)
)
@@ -1362,8 +1558,8 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, AccessPathFrontNil apf, Configuration config,
- LocalCallContext callContext
+ NodeEx node1, NodeEx node2, boolean preservesValue, AccessPathFrontNil apf,
+ Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, config)
@@ -1383,8 +1579,8 @@ private module Stage3 {
private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(Node node) {
- PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node))
+ private ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
@@ -1428,10 +1624,10 @@ private module Stage3 {
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { any() }
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, node2, preservesValue, ap, config, _) and exists(lcc)
}
@@ -1440,10 +1636,16 @@ private module Stage3 {
private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ pragma[nomagic]
+ private predicate clear(NodeEx node, Ap ap) { ap.isClearedAt(node.asNode()) }
+
+ pragma[nomagic]
+ private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
+
bindingset[node, ap]
- private predicate filter(Node node, Ap ap) {
- not ap.isClearedAt(node) and
- if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any()
+ private predicate filter(NodeEx node, Ap ap) {
+ not clear(node, ap) and
+ if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()
}
bindingset[ap, contentType]
@@ -1454,7 +1656,7 @@ private module Stage3 {
}
/* Begin: Stage 3 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
@@ -1465,6 +1667,16 @@ private module Stage3 {
)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -1474,21 +1686,21 @@ private module Stage3 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
pragma[nomagic]
- private predicate fwdFlow0(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -1499,7 +1711,7 @@ private module Stage3 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -1507,7 +1719,7 @@ private module Stage3 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -1538,19 +1750,17 @@ private module Stage3 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -1574,7 +1784,7 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -1583,32 +1793,29 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1618,9 +1825,15 @@ private module Stage3 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1631,7 +1844,7 @@ private module Stage3 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
@@ -1639,18 +1852,41 @@ private module Stage3 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -1660,41 +1896,41 @@ private module Stage3 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -1710,20 +1946,18 @@ private module Stage3 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -1736,7 +1970,7 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -1750,7 +1984,7 @@ private module Stage3 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -1759,10 +1993,10 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -1771,13 +2005,12 @@ private module Stage3 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1785,9 +2018,14 @@ private module Stage3 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -1799,7 +2037,7 @@ private module Stage3 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -1808,16 +2046,17 @@ private module Stage3 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -1826,7 +2065,7 @@ private module Stage3 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -1838,37 +2077,47 @@ private module Stage3 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 3 logic. */
}
@@ -1877,7 +2126,7 @@ private module Stage3 {
* Holds if `argApf` is recorded as the summary context for flow reaching `node`
* and remains relevant for the following pruning stage.
*/
-private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) {
+private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
@@ -1892,7 +2141,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
exists(int tails, int nodes, int apLimit, int tupleLimit |
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
- strictcount(Node n |
+ strictcount(NodeEx n |
Stage3::revFlow(n, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
@@ -2087,8 +2336,8 @@ private module Stage4 {
private ApApprox getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(Node node) {
- PrevStage::revFlow(node, _) and result = TNil(getNodeType(node))
+ private ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
@@ -2127,26 +2376,25 @@ private module Stage4 {
bindingset[innercc, inner, call]
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
resolveReturn(innercc, inner, call)
- or
- innercc.(CallContextCall).matchesCall(call)
}
bindingset[node, cc, config]
- private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
+ private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
localFlowEntry(node, config) and
- result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node))
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
}
private predicate localStep(
- Node node1, Node node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
+ NodeEx node1, NodeEx node2, boolean preservesValue, ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, node2, preservesValue, ap.getFront(), config, lcc)
}
pragma[nomagic]
private predicate flowOutOfCall(
- DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
@@ -2155,7 +2403,7 @@ private module Stage4 {
pragma[nomagic]
private predicate flowIntoCall(
- DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
@@ -2164,14 +2412,14 @@ private module Stage4 {
}
bindingset[node, ap]
- private predicate filter(Node node, Ap ap) { any() }
+ private predicate filter(NodeEx node, Ap ap) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
/* Begin: Stage 4 logic. */
- private predicate flowCand(Node node, ApApprox apa, Configuration config) {
+ private predicate flowCand(NodeEx node, ApApprox apa, Configuration config) {
PrevStage::revFlow(node, _, _, apa, config)
}
@@ -2182,6 +2430,16 @@ private module Stage4 {
)
}
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -2191,21 +2449,21 @@ private module Stage4 {
* argument.
*/
pragma[nomagic]
- predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
pragma[nomagic]
- private predicate fwdFlow0(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
+ private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
flowCand(node, _, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc = ccNone() and
argAp = apNone() and
ap = getApNil(node)
or
- exists(Node mid, Ap ap0, LocalCc localCc |
+ exists(NodeEx mid, Ap ap0, LocalCc localCc |
fwdFlow(mid, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
|
@@ -2216,7 +2474,7 @@ private module Stage4 {
ap0 instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
@@ -2224,7 +2482,7 @@ private module Stage4 {
argAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
@@ -2255,19 +2513,17 @@ private module Stage4 {
)
or
// flow out of a callable
- exists(DataFlowCall call |
- fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config)
- or
- exists(Ap argAp0 |
- fwdFlowOutFromArg(call, node, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
+ fwdFlowOutNotFromArg(node, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- Node node1, Ap ap1, TypedContent tc, Node node2, Cc cc, ApOption argAp, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
@@ -2291,7 +2547,7 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, Node node1, Node node2, Cc cc, ApOption argAp, Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, Cc cc, ApOption argAp, Configuration config
) {
fwdFlow(node1, cc, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
@@ -2300,32 +2556,29 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
+ DataFlowCall call, ParamNodeEx p, Cc outercc, Cc innercc, ApOption argAp, Ap ap,
Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
+ innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
}
- /**
- * Holds if flow may exit from `call` at `out` with access path `ap`. The
- * inner call context is `innercc`, but `ccOut` is just the call context
- * based on the return step. In the case of through-flow `ccOut` is discarded
- * and replaced by the outer call context as tracked by `fwdFlowIsEntered`.
- */
pragma[nomagic]
- private predicate fwdFlowOut(
- DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, Cc ccOut, ApOption argAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = getNodeEnclosingCallable(ret) and
+ inner = ret.getEnclosingCallable() and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -2335,9 +2588,15 @@ private module Stage4 {
pragma[nomagic]
private predicate fwdFlowOutFromArg(
- DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config
+ DataFlowCall call, NodeEx out, Ap argAp, Ap ap, Configuration config
) {
- fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config)
+ exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
+ fwdFlow(ret, ccc, apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ ccc.matchesCall(call)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -2348,7 +2607,7 @@ private module Stage4 {
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
@@ -2356,18 +2615,41 @@ private module Stage4 {
pragma[nomagic]
private predicate storeStepFwd(
- Node node1, Ap ap1, TypedContent tc, Node node2, Ap ap2, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, config)
}
- private predicate readStepFwd(Node n1, Ap ap1, Content c, Node n2, Ap ap2, Configuration config) {
+ private predicate readStepFwd(
+ NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
+ ) {
fwdFlowRead(ap1, c, n1, n2, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, argAp0, ap, config) and
+ fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
@@ -2377,41 +2659,41 @@ private module Stage4 {
* the access path of the returned value.
*/
pragma[nomagic]
- predicate revFlow(Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
+ predicate revFlow(NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config) {
revFlow0(node, toReturn, returnAp, ap, config) and
fwdFlow(node, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- Node node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
fwdFlow(node, _, _, ap, config) and
- config.isSink(node) and
+ sinkNode(node, config) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
or
- exists(Node mid |
+ exists(NodeEx mid |
localStep(node, mid, true, _, config, _) and
revFlow(mid, toReturn, returnAp, ap, config)
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
- exists(Node mid |
+ exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, _, _, ap, config) and
toReturn = false and
returnAp = apNone()
)
or
- exists(Node mid, ApNil nil |
+ exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
@@ -2427,20 +2709,18 @@ private module Stage4 {
)
or
// read
- exists(Node mid, Ap ap0 |
+ exists(NodeEx mid, Ap ap0 |
revFlow(mid, toReturn, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- exists(DataFlowCall call |
- revFlowIn(call, node, toReturn, returnAp, ap, config) and
- toReturn = false
- or
- exists(Ap returnAp0 |
- revFlowInToReturn(call, node, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
+ revFlowInNotToReturn(node, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
)
or
// flow out of a callable
@@ -2453,7 +2733,7 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowStore(
- Ap ap0, Content c, Ap ap, Node node, TypedContent tc, Node mid, boolean toReturn,
+ Ap ap0, Content c, Ap ap, NodeEx node, TypedContent tc, NodeEx mid, boolean toReturn,
ApOption returnAp, Configuration config
) {
revFlow(mid, toReturn, returnAp, ap0, config) and
@@ -2467,7 +2747,7 @@ private module Stage4 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid, Ap tail0 |
+ exists(NodeEx mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
tail = pragma[only_bind_into](tail0) and
readStepFwd(_, cons, c, mid, tail0, config)
@@ -2476,10 +2756,10 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, ReturnNodeExt ret, boolean toReturn, ApOption returnAp, Ap ap,
+ DataFlowCall call, RetNodeEx ret, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
) {
- exists(Node out, boolean allowsFieldFlow |
+ exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
@@ -2488,13 +2768,12 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate revFlowIn(
- DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ private predicate revFlowInNotToReturn(
+ ArgNodeEx arg, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ParameterNode p, boolean allowsFieldFlow |
- revFlow(p, toReturn, returnAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, false, returnAp, ap, config) and
+ flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -2502,9 +2781,14 @@ private module Stage4 {
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, Ap returnAp, Ap ap, Configuration config
) {
- revFlowIn(call, arg, true, apSome(returnAp), ap, config)
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof ApNil or allowsFieldFlow = true
+ )
}
/**
@@ -2516,7 +2800,7 @@ private module Stage4 {
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) {
- exists(ReturnNodeExt ret, CcCall ccc |
+ exists(RetNodeEx ret, CcCall ccc |
revFlowOut(call, ret, toReturn, returnAp, ap, config) and
fwdFlow(ret, ccc, apSome(_), ap, config) and
ccc.matchesCall(call)
@@ -2525,16 +2809,17 @@ private module Stage4 {
pragma[nomagic]
predicate storeStepCand(
- Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
) {
exists(Ap ap2, Content c |
- store(node1, tc, node2, contentType) and
+ store(node1, tc, node2, contentType, config) and
revFlowStore(ap2, c, ap1, node1, tc, node2, _, _, config) and
revFlowConsCand(ap2, c, ap1, config)
)
}
- predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
@@ -2543,7 +2828,7 @@ private module Stage4 {
)
}
- predicate revFlow(Node node, Configuration config) { revFlow(node, _, _, _, config) }
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, config) }
private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepFwd(_, ap, tc, _, _, config)
@@ -2555,37 +2840,47 @@ private module Stage4 {
pragma[noinline]
private predicate parameterFlow(
- ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = getNodeEnclosingCallable(p)
+ c = p.getEnclosingCallable()
}
- predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = getNodeEnclosingCallable(ret) and
+ c = ret.getEnclosingCallable() and
revFlow(ret, true, apSome(_), ap0, config) and
- fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
+ fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
+ pragma[only_bind_into](config)) and
kind = ret.getKind() and
- p.isParameterOf(_, pos) and
+ p.getPosition() = pos and
// we don't expect a parameter to return stored in itself
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}
+ pragma[nomagic]
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(Ap returnAp0, ArgNodeEx arg, boolean toReturn, ApOption returnAp, Ap ap |
+ revFlow(arg, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) {
fwd = true and
- nodes = count(Node node | fwdFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- tuples = count(Node n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
+ tuples = count(NodeEx n, Cc cc, ApOption argAp, Ap ap | fwdFlow(n, cc, argAp, ap, config))
or
fwd = false and
- nodes = count(Node node | revFlow(node, _, _, _, config)) and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, config)) and
fields = count(TypedContent f0 | consCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
- tuples = count(Node n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
+ tuples = count(NodeEx n, boolean b, ApOption retAp, Ap ap | revFlow(n, b, retAp, ap, config))
}
/* End: Stage 4 logic. */
}
@@ -2595,18 +2890,18 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
}
-private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) {
+private predicate nodeMayUseSummary(NodeEx n, AccessPathApprox apa, Configuration config) {
exists(DataFlowCallable c, AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and
Stage4::revFlow(n, true, _, apa0, config) and
Stage4::fwdFlow(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
- getNodeEnclosingCallable(n) = c
+ n.getEnclosingCallable() = c
)
}
private newtype TSummaryCtx =
TSummaryCtxNone() or
- TSummaryCtxSome(ParameterNode p, AccessPath ap) {
+ TSummaryCtxSome(ParamNodeEx p, AccessPath ap) {
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _)
}
@@ -2627,7 +2922,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone {
/** A summary context from which a flow summary can be generated. */
private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
- private ParameterNode p;
+ private ParamNodeEx p;
private AccessPath ap;
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
@@ -2660,7 +2955,9 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
- strictcount(Node n | Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config))
+ strictcount(NodeEx n |
+ Stage4::revFlow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)
+ )
}
/**
@@ -2752,13 +3049,13 @@ private newtype TAccessPath =
}
private newtype TPathNode =
- TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
+ TPathNodeMid(NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
// A PathNode is introduced by a source ...
Stage4::revFlow(node, config) and
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
- ap = TAccessPathNil(getNodeType(node))
+ ap = TAccessPathNil(node.getDataFlowType())
or
// ... or a step from an existing PathNode to another node.
exists(PathNodeMid mid |
@@ -2767,12 +3064,12 @@ private newtype TPathNode =
Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config))
)
} or
- TPathNodeSink(Node node, Configuration config) {
- pragma[only_bind_into](config).isSink(node) and
+ TPathNodeSink(NodeEx node, Configuration config) {
+ sinkNode(node, pragma[only_bind_into](config)) and
Stage4::revFlow(node, pragma[only_bind_into](config)) and
(
// A sink that is also a source ...
- config.isSource(node)
+ sourceNode(node, config)
or
// ... or a sink that can be reached from a source
exists(PathNodeMid mid |
@@ -2973,15 +3270,17 @@ class PathNode extends TPathNode {
}
/** Gets the underlying `Node`. */
- Node getNode() { none() }
+ final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
private predicate isHidden() {
- nodeIsHidden(this.getNode()) and
+ hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
not this.isSource() and
not this instanceof PathNodeSink
+ or
+ this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
}
private PathNode getASuccessorIfHidden() {
@@ -3003,6 +3302,8 @@ class PathNode extends TPathNode {
abstract private class PathNodeImpl extends PathNode {
abstract PathNode getASuccessorImpl();
+ abstract NodeEx getNodeEx();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3017,14 +3318,14 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
}
- override string toString() { result = this.getNode().toString() + ppAp() }
+ override string toString() { result = this.getNodeEx().toString() + ppAp() }
- override string toStringWithContext() { result = this.getNode().toString() + ppAp() + ppCtx() }
+ override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
@@ -3054,7 +3355,7 @@ module PathGraph {
* a `CallContext`, and a `Configuration`.
*/
private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
- Node node;
+ NodeEx node;
CallContext cc;
SummaryCtx sc;
AccessPath ap;
@@ -3062,7 +3363,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) }
- override Node getNode() { result = node }
+ override NodeEx getNodeEx() { result = node }
CallContext getCallContext() { result = cc }
@@ -3073,7 +3374,8 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
override Configuration getConfiguration() { result = config }
private PathNodeMid getSuccMid() {
- pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and
+ pathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx(),
+ result.getAp()) and
result.getConfiguration() = unbindConf(this.getConfiguration())
}
@@ -3084,7 +3386,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid, PathNodeSink sink |
mid = getSuccMid() and
- mid.getNode() = sink.getNode() and
+ mid.getNodeEx() = sink.getNodeEx() and
mid.getAp() instanceof AccessPathNil and
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
result = sink
@@ -3092,7 +3394,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
}
override predicate isSource() {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
ap instanceof AccessPathNil
@@ -3105,31 +3407,35 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
* excluding the `CallContext`.
*/
private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
- Node node;
+ NodeEx node;
Configuration config;
PathNodeSink() { this = TPathNodeSink(node, config) }
- override Node getNode() { result = node }
+ override NodeEx getNodeEx() { result = node }
override Configuration getConfiguration() { result = config }
override PathNode getASuccessorImpl() { none() }
- override predicate isSource() { config.isSource(node) }
+ override predicate isSource() { sourceNode(node, config) }
}
/**
* Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`.
*/
-private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
- exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
- midnode = mid.getNode() and
+private predicate pathStep(
+ PathNodeMid mid, NodeEx node, CallContext cc, SummaryCtx sc, AccessPath ap
+) {
+ exists(AccessPath ap0, NodeEx midnode, Configuration conf, LocalCallContext localCC |
+ midnode = mid.getNodeEx() and
conf = mid.getConfiguration() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
- localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and
+ localCC =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ midnode.getEnclosingCallable()) and
ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
@@ -3139,16 +3445,16 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
ap0 instanceof AccessPathNil
)
or
- jumpStep(mid.getNode(), node, mid.getConfiguration()) and
+ jumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
ap = mid.getAp()
or
- additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and
+ additionalJumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
mid.getAp() instanceof AccessPathNil and
- ap = TAccessPathNil(getNodeType(node))
+ ap = TAccessPathNil(node.getDataFlowType())
or
exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and
sc = mid.getSummaryCtx()
@@ -3165,20 +3471,20 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
pragma[nomagic]
private predicate pathReadStep(
- PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+ PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
- Stage4::readStepCand(mid.getNode(), tc.getContent(), node, mid.getConfiguration()) and
+ Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+ PathNodeMid mid, NodeEx node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- Stage4::storeStepCand(mid.getNode(), _, tc, node, _, mid.getConfiguration()) and
+ Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
cc = mid.getCallContext()
}
@@ -3186,7 +3492,7 @@ private predicate pathOutOfCallable0(
PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa,
Configuration config
) {
- pos = getReturnPosition(mid.getNode()) and
+ pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and
innercc = mid.getCallContext() and
innercc instanceof CallContextNoCall and
apa = mid.getAp().getApprox() and
@@ -3209,10 +3515,10 @@ private predicate pathOutOfCallable1(
}
pragma[noinline]
-private Node getAnOutNodeFlow(
+private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
- result = kind.getAnOutNode(call) and
+ result.asNode() = kind.getAnOutNode(call) and
Stage4::revFlow(result, _, _, apa, config)
}
@@ -3221,7 +3527,7 @@ private Node getAnOutNodeFlow(
* is a return from a callable and is recorded by `cc`, if needed.
*/
pragma[noinline]
-private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
+private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc) {
exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config |
pathOutOfCallable1(mid, call, kind, cc, apa, config) and
out = getAnOutNodeFlow(kind, call, apa, config)
@@ -3235,8 +3541,8 @@ pragma[noinline]
private predicate pathIntoArg(
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa
) {
- exists(ArgumentNode arg |
- arg = mid.getNode() and
+ exists(ArgNode arg |
+ arg = mid.getNodeEx().asNode() and
cc = mid.getCallContext() and
arg.argumentOf(call, i) and
ap = mid.getAp() and
@@ -3248,7 +3554,7 @@ pragma[noinline]
private predicate parameterCand(
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
) {
- exists(ParameterNode p |
+ exists(ParamNodeEx p |
Stage4::revFlow(p, _, _, apa, config) and
p.isParameterOf(callable, i)
)
@@ -3272,7 +3578,7 @@ private predicate pathIntoCallable0(
* respectively.
*/
private predicate pathIntoCallable(
- PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
+ PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
DataFlowCall call
) {
exists(int i, DataFlowCallable callable, AccessPath ap |
@@ -3297,8 +3603,8 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa,
Configuration config
) {
- exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
- mid.getNode() = ret and
+ exists(PathNodeMid mid, RetNodeEx ret, int pos |
+ mid.getNodeEx() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
@@ -3326,7 +3632,7 @@ private predicate pathThroughCallable0(
* The context `cc` is restored to its value prior to entering the callable.
*/
pragma[noinline]
-private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
+private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) {
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
pathThroughCallable0(call, mid, kind, cc, ap, apa) and
out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
@@ -3344,9 +3650,9 @@ private predicate flowsTo(
) {
flowsource.isSource() and
flowsource.getConfiguration() = configuration and
- flowsource.getNode() = source and
+ flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
- flowsink.getNode() = sink
+ flowsink.getNodeEx().asNode() = sink
}
/**
@@ -3361,13 +3667,13 @@ predicate flowsTo(Node source, Node sink, Configuration configuration) {
private predicate finalStats(boolean fwd, int nodes, int fields, int conscand, int tuples) {
fwd = true and
- nodes = count(Node n0 | exists(PathNode pn | pn.getNode() = n0)) and
+ nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
tuples = count(PathNode pn)
or
fwd = false and
- nodes = count(Node n0 | exists(PathNode pn | pn.getNode() = n0 and reach(pn))) and
+ nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
tuples = count(PathNode pn | reach(pn))
@@ -3404,19 +3710,19 @@ predicate stageStats(
private module FlowExploration {
private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2, Configuration config) {
- exists(Node node1, Node node2 |
+ exists(NodeEx node1, NodeEx node2 |
jumpStep(node1, node2, config)
or
additionalJumpStep(node1, node2, config)
or
// flow into callable
- viableParamArg(_, node2, node1)
+ viableParamArgEx(_, node2, node1)
or
// flow out of a callable
- viableReturnPosOut(_, getReturnPosition(node1), node2)
+ viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2)
|
- c1 = getNodeEnclosingCallable(node1) and
- c2 = getNodeEnclosingCallable(node2) and
+ c1 = node1.getEnclosingCallable() and
+ c2 = node2.getEnclosingCallable() and
c1 != c2
)
}
@@ -3568,7 +3874,7 @@ private module FlowExploration {
private newtype TSummaryCtx1 =
TSummaryCtx1None() or
- TSummaryCtx1Param(ParameterNode p)
+ TSummaryCtx1Param(ParamNodeEx p)
private newtype TSummaryCtx2 =
TSummaryCtx2None() or
@@ -3584,25 +3890,25 @@ private module FlowExploration {
private newtype TPartialPathNode =
TPartialPathNodeFwd(
- Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
+ NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
- distSrc(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
+ distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
- Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
+ NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
Configuration config
) {
- config.isSink(node) and
+ sinkNode(node, config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil() and
@@ -3611,23 +3917,23 @@ private module FlowExploration {
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
- not clearsContent(node, ap.getHead()) and
+ not clearsContentCached(node.asNode(), ap.getHead()) and
not fullBarrier(node, config) and
- distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
+ distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
}
pragma[nomagic]
private predicate partialPathNodeMk0(
- Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
+ NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
- not clearsContent(node, ap.getHead().getContent()) and
- if node instanceof CastingNode
- then compatibleTypes(getNodeType(node), ap.getType())
+ not clearsContentCached(node.asNode(), ap.getHead().getContent()) and
+ if node.asNode() instanceof CastingNode
+ then compatibleTypes(node.getDataFlowType(), ap.getType())
else any()
)
}
@@ -3637,13 +3943,15 @@ private module FlowExploration {
*/
class PartialPathNode extends TPartialPathNode {
/** Gets a textual representation of this element. */
- string toString() { result = this.getNode().toString() + this.ppAp() }
+ string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/**
* Gets a textual representation of this element, including a textual
* representation of the call context.
*/
- string toStringWithContext() { result = this.getNode().toString() + this.ppAp() + this.ppCtx() }
+ string toStringWithContext() {
+ result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
+ }
/**
* Holds if this element is at the specified location.
@@ -3655,11 +3963,16 @@ private module FlowExploration {
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the underlying `Node`. */
- Node getNode() { none() }
+ final Node getNode() { this.getNodeEx().projectToNode() = result }
+
+ private NodeEx getNodeEx() {
+ result = this.(PartialPathNodeFwd).getNodeEx() or
+ result = this.(PartialPathNodeRev).getNodeEx()
+ }
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
@@ -3672,7 +3985,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSourceDistance() {
- result = distSrc(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
+ result = distSrc(this.getNodeEx().getEnclosingCallable(), this.getConfiguration())
}
/**
@@ -3680,7 +3993,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSinkDistance() {
- result = distSink(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
+ result = distSink(this.getNodeEx().getEnclosingCallable(), this.getConfiguration())
}
private string ppAp() {
@@ -3712,7 +4025,7 @@ private module FlowExploration {
}
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
- Node node;
+ NodeEx node;
CallContext cc;
TSummaryCtx1 sc1;
TSummaryCtx2 sc2;
@@ -3721,7 +4034,7 @@ private module FlowExploration {
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
- override Node getNode() { result = node }
+ NodeEx getNodeEx() { result = node }
CallContext getCallContext() { result = cc }
@@ -3734,12 +4047,12 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodeFwd getASuccessor() {
- partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
+ partialPathStep(this, result.getNodeEx(), result.getCallContext(), result.getSummaryCtx1(),
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
}
predicate isSource() {
- config.isSource(node) and
+ sourceNode(node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
@@ -3748,7 +4061,7 @@ private module FlowExploration {
}
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
- Node node;
+ NodeEx node;
TRevSummaryCtx1 sc1;
TRevSummaryCtx2 sc2;
RevPartialAccessPath ap;
@@ -3756,7 +4069,7 @@ private module FlowExploration {
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
- override Node getNode() { result = node }
+ NodeEx getNodeEx() { result = node }
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
@@ -3767,12 +4080,12 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodeRev getASuccessor() {
- revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
+ revPartialPathStep(result, this.getNodeEx(), this.getSummaryCtx1(), this.getSummaryCtx2(),
this.getAp(), this.getConfiguration())
}
predicate isSink() {
- config.isSink(node) and
+ sinkNode(node, config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil()
@@ -3780,40 +4093,40 @@ private module FlowExploration {
}
private predicate partialPathStep(
- PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
+ PartialPathNodeFwd mid, NodeEx node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
- not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
+ not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and
(
- localFlowStep(mid.getNode(), node, config) and
+ localFlowStep(mid.getNodeEx(), node, config) and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalLocalFlowStep(mid.getNode(), node, config) and
+ additionalLocalFlowStep(mid.getNodeEx(), node, config) and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
config = mid.getConfiguration()
)
or
- jumpStep(mid.getNode(), node, config) and
+ jumpStep(mid.getNodeEx(), node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalJumpStep(mid.getNode(), node, config) and
+ additionalJumpStep(mid.getNodeEx(), node, config) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getNodeType(node)) and
+ ap = TPartialNil(node.getDataFlowType()) and
config = mid.getConfiguration()
or
partialPathStoreStep(mid, _, _, node, ap) and
@@ -3826,8 +4139,7 @@ private module FlowExploration {
partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, tc, ap0, config) and
- compatibleTypes(ap.getType(), getNodeType(node))
+ apConsFwd(ap, tc, ap0, config)
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -3846,12 +4158,13 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
+ PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, NodeEx node,
+ PartialAccessPath ap2
) {
- exists(Node midNode, DataFlowType contentType |
- midNode = mid.getNode() and
+ exists(NodeEx midNode, DataFlowType contentType |
+ midNode = mid.getNodeEx() and
ap1 = mid.getAp() and
- store(midNode, tc, node, contentType) and
+ store(midNode, tc, node, contentType, mid.getConfiguration()) and
ap2.getHead() = tc and
ap2.len() = unbindInt(ap1.len() + 1) and
compatibleTypes(ap1.getType(), contentType)
@@ -3870,15 +4183,15 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
+ PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, NodeEx node, CallContext cc,
Configuration config
) {
- exists(Node midNode |
- midNode = mid.getNode() and
+ exists(NodeEx midNode |
+ midNode = mid.getNodeEx() and
ap = mid.getAp() and
- read(midNode, tc.getContent(), node) and
+ read(midNode, tc.getContent(), node, pragma[only_bind_into](config)) and
ap.getHead() = tc and
- config = mid.getConfiguration() and
+ pragma[only_bind_into](config) = mid.getConfiguration() and
cc = mid.getCallContext()
)
}
@@ -3887,7 +4200,7 @@ private module FlowExploration {
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
Configuration config
) {
- pos = getReturnPosition(mid.getNode()) and
+ pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and
innercc = mid.getCallContext() and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
@@ -3910,12 +4223,12 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
- PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = kind.getAnOutNode(call)
+ out.asNode() = kind.getAnOutNode(call)
)
}
@@ -3924,8 +4237,8 @@ private module FlowExploration {
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
- exists(ArgumentNode arg |
- arg = mid.getNode() and
+ exists(ArgNode arg |
+ arg = mid.getNodeEx().asNode() and
cc = mid.getCallContext() and
arg.argumentOf(call, i) and
ap = mid.getAp() and
@@ -3943,7 +4256,7 @@ private module FlowExploration {
}
private predicate partialPathIntoCallable(
- PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
+ PartialPathNodeFwd mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc,
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
@@ -3964,8 +4277,8 @@ private module FlowExploration {
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
- exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
- mid.getNode() = ret and
+ exists(PartialPathNodeFwd mid, RetNodeEx ret |
+ mid.getNodeEx() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc1 = mid.getSummaryCtx1() and
@@ -3980,45 +4293,45 @@ private module FlowExploration {
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
- exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
- partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and
+ exists(CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
+ partialPathIntoCallable(mid, _, cc, innercc, sc1, sc2, call, _, config) and
paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config)
)
}
private predicate partialPathThroughCallable(
- PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodeFwd mid, NodeEx out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
- out = kind.getAnOutNode(call)
+ out.asNode() = kind.getAnOutNode(call)
)
}
private predicate revPartialPathStep(
- PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
+ PartialPathNodeRev mid, NodeEx node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
RevPartialAccessPath ap, Configuration config
) {
- localFlowStep(node, mid.getNode(), config) and
+ localFlowStep(node, mid.getNodeEx(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalLocalFlowStep(node, mid.getNode(), config) and
+ additionalLocalFlowStep(node, mid.getNodeEx(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
- jumpStep(node, mid.getNode(), config) and
+ jumpStep(node, mid.getNodeEx(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
- additionalJumpStep(node, mid.getNode(), config) and
+ additionalJumpStep(node, mid.getNodeEx(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
mid.getAp() instanceof RevPartialAccessPathNil and
@@ -4037,9 +4350,9 @@ private module FlowExploration {
apConsRev(ap, c, ap0, config)
)
or
- exists(ParameterNode p |
- mid.getNode() = p and
- viableParamArg(_, p, node) and
+ exists(ParamNodeEx p |
+ mid.getNodeEx() = p and
+ viableParamArgEx(_, p, node) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
sc1 = TRevSummaryCtx1None() and
@@ -4050,7 +4363,7 @@ private module FlowExploration {
or
exists(ReturnPosition pos |
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
- pos = getReturnPosition(node)
+ pos = getReturnPosition(node.asNode())
)
or
revPartialPathThroughCallable(mid, node, ap, config) and
@@ -4060,12 +4373,13 @@ private module FlowExploration {
pragma[inline]
private predicate revPartialPathReadStep(
- PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
+ PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, NodeEx node,
+ RevPartialAccessPath ap2
) {
- exists(Node midNode |
- midNode = mid.getNode() and
+ exists(NodeEx midNode |
+ midNode = mid.getNodeEx() and
ap1 = mid.getAp() and
- read(node, c, midNode) and
+ read(node, c, midNode, mid.getConfiguration()) and
ap2.getHead() = c and
ap2.len() = unbindInt(ap1.len() + 1)
)
@@ -4083,12 +4397,12 @@ private module FlowExploration {
pragma[nomagic]
private predicate revPartialPathStoreStep(
- PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
+ PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, NodeEx node, Configuration config
) {
- exists(Node midNode, TypedContent tc |
- midNode = mid.getNode() and
+ exists(NodeEx midNode, TypedContent tc |
+ midNode = mid.getNodeEx() and
ap = mid.getAp() and
- store(node, tc, midNode, _) and
+ store(node, tc, midNode, _, config) and
ap.getHead() = c and
config = mid.getConfiguration() and
tc.getContent() = c
@@ -4100,9 +4414,9 @@ private module FlowExploration {
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
DataFlowCall call, RevPartialAccessPath ap, Configuration config
) {
- exists(Node out |
- mid.getNode() = out and
- viableReturnPosOut(call, pos, out) and
+ exists(NodeEx out |
+ mid.getNodeEx() = out and
+ viableReturnPosOutEx(call, pos, out) and
sc1 = TRevSummaryCtx1Some(pos) and
sc2 = TRevSummaryCtx2Some(ap) and
ap = mid.getAp() and
@@ -4115,9 +4429,9 @@ private module FlowExploration {
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
Configuration config
) {
- exists(PartialPathNodeRev mid, ParameterNode p |
- mid.getNode() = p and
- p.isParameterOf(_, pos) and
+ exists(PartialPathNodeRev mid, ParamNodeEx p |
+ mid.getNodeEx() = p and
+ p.getPosition() = pos and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
@@ -4138,11 +4452,11 @@ private module FlowExploration {
pragma[nomagic]
private predicate revPartialPathThroughCallable(
- PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
+ PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, int pos |
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
- node.argumentOf(call, pos)
+ node.asNode().(ArgNode).argumentOf(call, pos)
)
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
index a51c20c22881..eaed77326c71 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
@@ -31,26 +31,26 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) {
* currently excludes read-steps, store-steps, and flow-through.
*
* The analysis uses non-linear recursion: When computing a flow path in or out
- * of a call, we use the results of the analysis recursively to resolve lamba
+ * of a call, we use the results of the analysis recursively to resolve lambda
* calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly.
*/
private module LambdaFlow {
- private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) {
+ private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) {
p.isParameterOf(viableCallable(call), i)
}
- private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) {
+ private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) {
p.isParameterOf(viableCallableLambda(call, _), i)
}
- private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) {
exists(int i |
viableParamNonLambda(call, i, p) and
arg.argumentOf(call, i)
)
}
- private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) {
exists(int i |
viableParamLambda(call, i, p) and
arg.argumentOf(call, i)
@@ -118,8 +118,8 @@ private module LambdaFlow {
boolean toJump, DataFlowCallOption lastCall
) {
revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and
- if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode
- then compatibleTypes(t, getNodeType(node))
+ if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode
+ then compatibleTypes(t, getNodeDataFlowType(node))
else any()
}
@@ -129,7 +129,7 @@ private module LambdaFlow {
boolean toJump, DataFlowCallOption lastCall
) {
lambdaCall(lambdaCall, kind, node) and
- t = getNodeType(node) and
+ t = getNodeDataFlowType(node) and
toReturn = false and
toJump = false and
lastCall = TDataFlowCallNone()
@@ -146,7 +146,7 @@ private module LambdaFlow {
getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid)
|
preservesValue = false and
- t = getNodeType(node)
+ t = getNodeDataFlowType(node)
or
preservesValue = true and
t = t0
@@ -160,7 +160,7 @@ private module LambdaFlow {
toJump = true and
lastCall = TDataFlowCallNone()
|
- jumpStep(node, mid) and
+ jumpStepCached(node, mid) and
t = t0
or
exists(boolean preservesValue |
@@ -168,7 +168,7 @@ private module LambdaFlow {
getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid)
|
preservesValue = false and
- t = getNodeType(node)
+ t = getNodeDataFlowType(node)
or
preservesValue = true and
t = t0
@@ -176,7 +176,7 @@ private module LambdaFlow {
)
or
// flow into a callable
- exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call |
+ exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call |
revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and
(
if lastCall0 = TDataFlowCallNone() and toJump = false
@@ -227,7 +227,7 @@ private module LambdaFlow {
pragma[nomagic]
predicate revLambdaFlowIn(
- DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump,
+ DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump,
DataFlowCallOption lastCall
) {
revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall)
@@ -242,6 +242,89 @@ private DataFlowCallable viableCallableExt(DataFlowCall call) {
cached
private module Cached {
+ /**
+ * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to
+ * force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby
+ * collapsing the two stages.
+ */
+ cached
+ predicate forceCachingInSameStage() { any() }
+
+ cached
+ predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() }
+
+ cached
+ predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) {
+ c = call.getEnclosingCallable()
+ }
+
+ cached
+ predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) }
+
+ cached
+ predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) }
+
+ cached
+ predicate clearsContentCached(Node n, Content c) { clearsContent(n, c) }
+
+ cached
+ predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) }
+
+ cached
+ predicate outNodeExt(Node n) {
+ n instanceof OutNode
+ or
+ n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode
+ }
+
+ cached
+ predicate hiddenNode(Node n) { nodeIsHidden(n) }
+
+ cached
+ OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) {
+ result = getAnOutNode(call, k.(ValueReturnKind).getKind())
+ or
+ exists(ArgNode arg |
+ result.(PostUpdateNode).getPreUpdateNode() = arg and
+ arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition())
+ )
+ }
+
+ cached
+ predicate returnNodeExt(Node n, ReturnKindExt k) {
+ k = TValueReturn(n.(ReturnNode).getKind())
+ or
+ exists(ParamNode p, int pos |
+ parameterValueFlowsToPreUpdate(p, n) and
+ p.isParameterOf(_, pos) and
+ k = TParamUpdate(pos)
+ )
+ }
+
+ cached
+ predicate castNode(Node n) { n instanceof CastNode }
+
+ cached
+ predicate castingNode(Node n) {
+ castNode(n) or
+ n instanceof ParamNode or
+ n 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`.
+ read(_, _, n)
+ }
+
+ cached
+ predicate parameterNode(Node n, DataFlowCallable c, int i) {
+ n.(ParameterNode).isParameterOf(c, i)
+ }
+
+ cached
+ predicate argumentNode(Node n, DataFlowCall call, int pos) {
+ n.(ArgumentNode).argumentOf(call, pos)
+ }
+
/**
* Gets a viable target for the lambda call `call`.
*
@@ -261,7 +344,7 @@ private module Cached {
* The instance parameter is considered to have index `-1`.
*/
pragma[nomagic]
- private predicate viableParam(DataFlowCall call, int i, ParameterNode p) {
+ private predicate viableParam(DataFlowCall call, int i, ParamNode p) {
p.isParameterOf(viableCallableExt(call), i)
}
@@ -270,11 +353,11 @@ private module Cached {
* dispatch into account.
*/
cached
- predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) {
exists(int i |
viableParam(call, i, p) and
arg.argumentOf(call, i) and
- compatibleTypes(getNodeType(arg), getNodeType(p))
+ compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p))
)
}
@@ -312,7 +395,7 @@ private module Cached {
* `read` indicates whether it is contents of `p` that can flow to `node`.
*/
pragma[nomagic]
- private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) {
+ private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) {
p = node and
read = false
or
@@ -325,30 +408,30 @@ private module Cached {
// read
exists(Node mid |
parameterValueFlowCand(p, mid, false) and
- readStep(mid, _, node) and
+ read(mid, _, node) and
read = true
)
or
// flow through: no prior read
- exists(ArgumentNode arg |
+ exists(ArgNode arg |
parameterValueFlowArgCand(p, arg, false) and
argumentValueFlowsThroughCand(arg, node, read)
)
or
// flow through: no read inside method
- exists(ArgumentNode arg |
+ exists(ArgNode arg |
parameterValueFlowArgCand(p, arg, read) and
argumentValueFlowsThroughCand(arg, node, false)
)
}
pragma[nomagic]
- private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) {
+ private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) {
parameterValueFlowCand(p, arg, read)
}
pragma[nomagic]
- predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) {
+ predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) {
parameterValueFlowCand(p, n.getPreUpdateNode(), false)
}
@@ -360,7 +443,7 @@ private module Cached {
* `read` indicates whether it is contents of `p` that can flow to the return
* node.
*/
- predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) {
+ predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) {
exists(ReturnNode ret |
parameterValueFlowCand(p, ret, read) and
kind = ret.getKind()
@@ -369,9 +452,9 @@ private module Cached {
pragma[nomagic]
private predicate argumentValueFlowsThroughCand0(
- DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read
+ DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read
) {
- exists(ParameterNode param | viableParamArg(call, param, arg) |
+ exists(ParamNode param | viableParamArg(call, param, arg) |
parameterValueFlowReturnCand(param, kind, read)
)
}
@@ -382,14 +465,14 @@ private module Cached {
*
* `read` indicates whether it is contents of `arg` that can flow to `out`.
*/
- predicate argumentValueFlowsThroughCand(ArgumentNode arg, Node out, boolean read) {
+ predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) {
exists(DataFlowCall call, ReturnKind kind |
argumentValueFlowsThroughCand0(call, arg, kind, read) and
out = getAnOutNode(call, kind)
)
}
- predicate cand(ParameterNode p, Node n) {
+ predicate cand(ParamNode p, Node n) {
parameterValueFlowCand(p, n, _) and
(
parameterValueFlowReturnCand(p, _, _)
@@ -416,21 +499,21 @@ private module Cached {
* If a read step was taken, then `read` captures the `Content`, the
* container type, and the content type.
*/
- predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) {
+ predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) {
parameterValueFlow0(p, node, read) and
if node instanceof CastingNode
then
// normal flow through
read = TReadStepTypesNone() and
- compatibleTypes(getNodeType(p), getNodeType(node))
+ compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node))
or
// getter
- compatibleTypes(read.getContentType(), getNodeType(node))
+ compatibleTypes(read.getContentType(), getNodeDataFlowType(node))
else any()
}
pragma[nomagic]
- private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) {
+ private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) {
p = node and
Cand::cand(p, _) and
read = TReadStepTypesNone()
@@ -447,7 +530,7 @@ private module Cached {
readStepWithTypes(mid, read.getContainerType(), read.getContent(), node,
read.getContentType()) and
Cand::parameterValueFlowReturnCand(p, _, true) and
- compatibleTypes(getNodeType(p), read.getContainerType())
+ compatibleTypes(getNodeDataFlowType(p), read.getContainerType())
)
or
parameterValueFlow0_0(TReadStepTypesNone(), p, node, read)
@@ -455,34 +538,32 @@ private module Cached {
pragma[nomagic]
private predicate parameterValueFlow0_0(
- ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read
+ ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read
) {
// flow through: no prior read
- exists(ArgumentNode arg |
+ exists(ArgNode arg |
parameterValueFlowArg(p, arg, mustBeNone) and
argumentValueFlowsThrough(arg, read, node)
)
or
// flow through: no read inside method
- exists(ArgumentNode arg |
+ exists(ArgNode arg |
parameterValueFlowArg(p, arg, read) and
argumentValueFlowsThrough(arg, mustBeNone, node)
)
}
pragma[nomagic]
- private predicate parameterValueFlowArg(
- ParameterNode p, ArgumentNode arg, ReadStepTypesOption read
- ) {
+ private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) {
parameterValueFlow(p, arg, read) and
Cand::argumentValueFlowsThroughCand(arg, _, _)
}
pragma[nomagic]
private predicate argumentValueFlowsThrough0(
- DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read
+ DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read
) {
- exists(ParameterNode param | viableParamArg(call, param, arg) |
+ exists(ParamNode param | viableParamArg(call, param, arg) |
parameterValueFlowReturn(param, kind, read)
)
}
@@ -496,18 +577,18 @@ private module Cached {
* container type, and the content type.
*/
pragma[nomagic]
- predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) {
+ predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) {
exists(DataFlowCall call, ReturnKind kind |
argumentValueFlowsThrough0(call, arg, kind, read) and
out = getAnOutNode(call, kind)
|
// normal flow through
read = TReadStepTypesNone() and
- compatibleTypes(getNodeType(arg), getNodeType(out))
+ compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out))
or
// getter
- compatibleTypes(getNodeType(arg), read.getContainerType()) and
- compatibleTypes(read.getContentType(), getNodeType(out))
+ compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and
+ compatibleTypes(read.getContentType(), getNodeDataFlowType(out))
)
}
@@ -516,7 +597,7 @@ private module Cached {
* 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) {
+ predicate getterStep(ArgNode arg, Content c, Node out) {
argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out)
}
@@ -529,7 +610,7 @@ private module Cached {
* container type, and the content type.
*/
private predicate parameterValueFlowReturn(
- ParameterNode p, ReturnKind kind, ReadStepTypesOption read
+ ParamNode p, ReturnKind kind, ReadStepTypesOption read
) {
exists(ReturnNode ret |
parameterValueFlow(p, ret, read) and
@@ -553,7 +634,7 @@ private module Cached {
private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) {
mayBenefitFromCallContext(call, callable)
or
- callable = call.getEnclosingCallable() and
+ callEnclosingCallable(call, callable) and
exists(viableCallableLambda(call, TDataFlowCallSome(_)))
}
@@ -611,7 +692,7 @@ private module Cached {
mayBenefitFromCallContextExt(call, _) and
c = viableCallableExt(call) and
ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and
- tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and
+ tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and
ctxtgts < tgts
)
}
@@ -635,8 +716,7 @@ private module Cached {
* Holds if `p` can flow to the pre-update node associated with post-update
* node `n`, in the same callable, using only value-preserving steps.
*/
- cached
- predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) {
+ private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) {
parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone())
}
@@ -644,9 +724,8 @@ private module Cached {
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
storeStep(node1, c, node2) and
- readStep(_, c, _) and
- contentType = getNodeType(node1) and
- containerType = getNodeType(node2)
+ contentType = getNodeDataFlowType(node1) and
+ containerType = getNodeDataFlowType(node2)
or
exists(Node n1, Node n2 |
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
@@ -654,12 +733,15 @@ private module Cached {
|
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1)
or
- readStep(n2, c, n1) and
- contentType = getNodeType(n1) and
- containerType = getNodeType(n2)
+ read(n2, c, n1) and
+ contentType = getNodeDataFlowType(n1) and
+ containerType = getNodeDataFlowType(n2)
)
}
+ cached
+ predicate read(Node node1, Content c, Node node2) { readStep(node1, c, node2) }
+
/**
* Holds if data can flow from `node1` to `node2` via a direct assignment to
* `f`.
@@ -678,8 +760,9 @@ private module Cached {
* are aliases. A typical example is a function returning `this`, implementing a fluent
* interface.
*/
- cached
- predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) {
+ private predicate reverseStepThroughInputOutputAlias(
+ PostUpdateNode fromNode, PostUpdateNode toNode
+ ) {
exists(Node fromPre, Node toPre |
fromPre = fromNode.getPreUpdateNode() and
toPre = toNode.getPreUpdateNode()
@@ -688,14 +771,20 @@ private module Cached {
// Does the language-specific simpleLocalFlowStep already model flow
// from function input to output?
fromPre = getAnOutNode(c, _) and
- toPre.(ArgumentNode).argumentOf(c, _) and
- simpleLocalFlowStep(toPre.(ArgumentNode), fromPre)
+ toPre.(ArgNode).argumentOf(c, _) and
+ simpleLocalFlowStep(toPre.(ArgNode), fromPre)
)
or
argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre)
)
}
+ cached
+ predicate simpleLocalFlowStepExt(Node node1, Node node2) {
+ simpleLocalFlowStep(node1, node2) or
+ reverseStepThroughInputOutputAlias(node1, node2)
+ }
+
/**
* Holds if the call context `call` either improves virtual dispatch in
* `callable` or if it allows us to prune unreachable nodes in `callable`.
@@ -704,7 +793,7 @@ private module Cached {
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
reducedViableImplInCallContext(_, callable, call)
or
- exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCall(n, call))
+ exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call))
}
cached
@@ -726,12 +815,12 @@ private module Cached {
cached
newtype TLocalFlowCallContext =
TAnyLocalCall() or
- TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) }
+ TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) }
cached
newtype TReturnKindExt =
TValueReturn(ReturnKind kind) or
- TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) }
+ TParamUpdate(int pos) { exists(ParamNode p | p.isParameterOf(_, pos)) }
cached
newtype TBooleanOption =
@@ -761,23 +850,15 @@ private module Cached {
* A `Node` at which a cast can occur such that the type should be checked.
*/
class CastingNode extends Node {
- CastingNode() {
- this instanceof ParameterNode or
- this instanceof CastNode or
- 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)
- }
+ CastingNode() { castingNode(this) }
}
private predicate readStepWithTypes(
Node n1, DataFlowType container, Content c, Node n2, DataFlowType content
) {
- readStep(n1, c, n2) and
- container = getNodeType(n1) and
- content = getNodeType(n2)
+ read(n1, c, n2) and
+ container = getNodeDataFlowType(n1) and
+ content = getNodeDataFlowType(n2)
}
private newtype TReadStepTypesOption =
@@ -854,7 +935,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall {
override string toString() { result = "CcSomeCall" }
override predicate relevantFor(DataFlowCallable callable) {
- exists(ParameterNode p | getNodeEnclosingCallable(p) = callable)
+ exists(ParamNode p | getNodeEnclosingCallable(p) = callable)
}
override predicate matchesCall(DataFlowCall call) { any() }
@@ -866,7 +947,7 @@ class CallContextReturn extends CallContextNoCall, TReturn {
}
override predicate relevantFor(DataFlowCallable callable) {
- exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable)
+ exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable))
}
}
@@ -899,7 +980,7 @@ class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall
}
private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) {
- exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCall(n, call))
+ exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call))
}
/**
@@ -913,26 +994,37 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable)
else result instanceof LocalCallContextAny
}
+/**
+ * The value of a parameter at function entry, viewed as a node in a data
+ * flow graph.
+ */
+class ParamNode extends Node {
+ ParamNode() { parameterNode(this, _, _) }
+
+ /**
+ * Holds if this node is the parameter of callable `c` at the specified
+ * (zero-based) position.
+ */
+ predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) }
+}
+
+/** A data-flow node that represents a call argument. */
+class ArgNode extends Node {
+ ArgNode() { argumentNode(this, _, _) }
+
+ /** Holds if this argument occurs at the given position in the given call. */
+ final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) }
+}
+
/**
* A node from which flow can return to the caller. This is either a regular
* `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter.
*/
class ReturnNodeExt extends Node {
- ReturnNodeExt() {
- this instanceof ReturnNode or
- parameterValueFlowsToPreUpdate(_, this)
- }
+ ReturnNodeExt() { returnNodeExt(this, _) }
/** Gets the kind of this returned value. */
- ReturnKindExt getKind() {
- result = TValueReturn(this.(ReturnNode).getKind())
- or
- exists(ParameterNode p, int pos |
- parameterValueFlowsToPreUpdate(p, this) and
- p.isParameterOf(_, pos) and
- result = TParamUpdate(pos)
- )
- }
+ ReturnKindExt getKind() { returnNodeExt(this, result) }
}
/**
@@ -940,11 +1032,7 @@ class ReturnNodeExt extends Node {
* or a post-update node associated with a call argument.
*/
class OutNodeExt extends Node {
- OutNodeExt() {
- this instanceof OutNode
- or
- this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode
- }
+ OutNodeExt() { outNodeExt(this) }
}
/**
@@ -957,7 +1045,7 @@ abstract class ReturnKindExt extends TReturnKindExt {
abstract string toString();
/** Gets a node corresponding to data flow out of `call`. */
- abstract OutNodeExt getAnOutNode(DataFlowCall call);
+ final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) }
}
class ValueReturnKind extends ReturnKindExt, TValueReturn {
@@ -968,10 +1056,6 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn {
ReturnKind getKind() { result = kind }
override string toString() { result = kind.toString() }
-
- override OutNodeExt getAnOutNode(DataFlowCall call) {
- result = getAnOutNode(call, this.getKind())
- }
}
class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
@@ -982,13 +1066,6 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
int getPosition() { result = pos }
override string toString() { result = "param update " + pos }
-
- override OutNodeExt getAnOutNode(DataFlowCall call) {
- exists(ArgumentNode arg |
- result.(PostUpdateNode).getPreUpdateNode() = arg and
- arg.argumentOf(call, this.getPosition())
- )
- }
}
/** A callable tagged with a relevant return kind. */
@@ -1015,10 +1092,13 @@ class ReturnPosition extends TReturnPosition0 {
*/
pragma[inline]
DataFlowCallable getNodeEnclosingCallable(Node n) {
- exists(Node n0 |
- pragma[only_bind_into](n0) = n and
- pragma[only_bind_into](result) = n0.getEnclosingCallable()
- )
+ nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result))
+}
+
+/** Gets the type of `n` used for type pruning. */
+pragma[inline]
+DataFlowType getNodeDataFlowType(Node n) {
+ nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result))
}
pragma[noinline]
@@ -1042,7 +1122,7 @@ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall
cc instanceof CallContextAny and callable = viableCallableExt(call)
or
exists(DataFlowCallable c0, DataFlowCall call0 |
- call0.getEnclosingCallable() = callable and
+ callEnclosingCallable(call0, callable) and
cc = TReturn(c0, call0) and
c0 = prunedViableImplInCallContextReverse(call0, call)
)
@@ -1063,8 +1143,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
result = viableCallableExt(call) and cc instanceof CallContextReturn
}
-predicate read = readStep/3;
-
/** An optional Boolean value. */
class BooleanOption extends TBooleanOption {
string toString() {
@@ -1116,7 +1194,7 @@ abstract class AccessPathFront extends TAccessPathFront {
TypedContent getHead() { this = TFrontHead(result) }
- predicate isClearedAt(Node n) { clearsContent(n, getHead().getContent()) }
+ predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) }
}
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll
index 4e1cd281488f..a55e65a81f69 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll
@@ -168,7 +168,13 @@ module Consistency {
msg = "ArgumentNode is missing PostUpdateNode."
}
- query predicate postWithInFlow(PostUpdateNode n, string msg) {
+ // This predicate helps the compiler forget that in some languages
+ // it is impossible for a `PostUpdateNode` to be the target of
+ // `simpleLocalFlowStep`.
+ private predicate isPostUpdateNode(Node n) { n instanceof PostUpdateNode or none() }
+
+ query predicate postWithInFlow(Node n, string msg) {
+ isPostUpdateNode(n) and
simpleLocalFlowStep(_, n) and
msg = "PostUpdateNode should not be the target of local flow."
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
index 3b94e574de02..73bf72a36434 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
@@ -184,64 +184,6 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
*/
predicate jumpStep(Node n1, Node n2) { none() }
-/**
- * Gets a field corresponding to the bit range `[startBit..endBit)` of class `c`, if any.
- */
-private Field getAField(Class c, int startBit, int endBit) {
- result.getDeclaringType() = c and
- startBit = 8 * result.getByteOffset() and
- endBit = 8 * result.getType().getSize() + startBit
- or
- exists(Field f, Class cInner |
- f = c.getAField() and
- cInner = f.getUnderlyingType() and
- result = getAField(cInner, startBit - 8 * f.getByteOffset(), endBit - 8 * f.getByteOffset())
- )
-}
-
-private newtype TContent =
- TFieldContent(Class c, int startBit, int endBit) { exists(getAField(c, startBit, endBit)) } or
- TCollectionContent() or
- TArrayContent()
-
-/**
- * A reference contained in an object. Examples include instance fields, the
- * contents of a collection object, or the contents of an array.
- */
-class Content extends TContent {
- /** Gets a textual representation of this element. */
- abstract string toString();
-
- 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
- }
-}
-
-private class FieldContent extends Content, TFieldContent {
- Class c;
- int startBit;
- int endBit;
-
- FieldContent() { this = TFieldContent(c, startBit, endBit) }
-
- // Ensure that there's just 1 result for `toString`.
- override string toString() { result = min(Field f | f = getAField() | f.toString()) }
-
- predicate hasOffset(Class cl, int start, int end) { cl = c and start = startBit and end = endBit }
-
- Field getAField() { result = getAField(c, startBit, endBit) }
-}
-
-private class CollectionContent extends Content, TCollectionContent {
- override string toString() { result = "collection" }
-}
-
-private class ArrayContent extends Content, TArrayContent {
- ArrayContent() { this = TArrayContent() }
-
- override string toString() { result = "array content" }
-}
-
private predicate fieldStoreStepNoChi(Node node1, FieldContent f, PostUpdateNode node2) {
exists(StoreInstruction store, Class c |
store = node2.asInstruction() and
@@ -288,7 +230,7 @@ private predicate fieldStoreStepChi(Node node1, FieldContent f, PostUpdateNode n
}
private predicate arrayStoreStepChi(Node node1, ArrayContent a, PostUpdateNode node2) {
- a = TArrayContent() and
+ exists(a) and
exists(ChiPartialOperand operand, ChiInstruction chi, StoreInstruction store |
chi.getPartialOperand() = operand and
store = operand.getDef() and
@@ -383,7 +325,7 @@ private predicate fieldReadStep(Node node1, FieldContent f, Node node2) {
* predicate in `storeStep` ensures that we push the right `FieldContent` onto the access path.
*/
predicate suppressArrayRead(Node node1, ArrayContent a, Node node2) {
- a = TArrayContent() and
+ exists(a) and
exists(WriteSideEffectInstruction write, ChiInstruction chi |
node1.asInstruction() = write and
node2.asInstruction() = chi and
@@ -412,7 +354,7 @@ private Instruction skipCopyValueInstructions(Operand op) {
}
private predicate arrayReadStep(Node node1, ArrayContent a, Node node2) {
- a = TArrayContent() and
+ exists(a) and
// Explicit dereferences such as `*p` or `p[i]` where `p` is a pointer or array.
exists(LoadOperand operand, Instruction address |
operand.isDefinitionInexact() and
@@ -443,7 +385,7 @@ private predicate arrayReadStep(Node node1, ArrayContent a, Node node2) {
* from the access path.
*/
private predicate exactReadStep(Node node1, ArrayContent a, Node node2) {
- a = TArrayContent() and
+ exists(a) and
exists(WriteSideEffectInstruction write, ChiInstruction chi |
not chi.isResultConflated() and
chi.getPartial() = write and
@@ -557,5 +499,5 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c)
/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() }
-/** Extra data-flow steps needed for lamba flow analysis. */
+/** Extra data-flow steps needed for lambda flow analysis. */
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
index f5fb7309cff6..bf21249d4caa 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
@@ -12,10 +12,20 @@ private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.models.interfaces.DataFlow
cached
-private newtype TIRDataFlowNode =
- TInstructionNode(Instruction i) or
- TOperandNode(Operand op) or
- TVariableNode(Variable var)
+private module Cached {
+ cached
+ newtype TIRDataFlowNode =
+ TInstructionNode(Instruction i) or
+ TOperandNode(Operand op) or
+ TVariableNode(Variable var)
+
+ cached
+ predicate localFlowStepCached(Node nodeFrom, Node nodeTo) {
+ simpleLocalFlowStep(nodeFrom, nodeTo)
+ }
+}
+
+private import Cached
/**
* A node in a data flow graph.
@@ -590,7 +600,7 @@ Node uninitializedNode(LocalVariable v) { none() }
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
* (intra-procedural) step.
*/
-predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFrom, nodeTo) }
+predicate localFlowStep = localFlowStepCached/2;
/**
* INTERNAL: do not use.
@@ -598,7 +608,6 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFr
* This is the local flow predicate that's used as a building block in global
* data flow. It may have less flow than the `localFlowStep` predicate.
*/
-cached
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
// Operand -> Instruction flow
simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction())
@@ -656,7 +665,7 @@ private predicate simpleOperandLocalFlowStep(Instruction iFrom, Operand opTo) {
exists(LoadInstruction load |
load.getSourceValueOperand() = opTo and
opTo.getAnyDef() = iFrom and
- isSingleFieldClass(iFrom.getResultType(), opTo)
+ isSingleFieldClass(pragma[only_bind_out](pragma[only_bind_out](iFrom).getResultType()), opTo)
)
}
@@ -739,16 +748,10 @@ private predicate modelFlow(Operand opFrom, Instruction iTo) {
)
or
exists(int index, ReadSideEffectInstruction read |
- modelIn.isParameterDeref(index) and
+ modelIn.isParameterDerefOrQualifierObject(index) and
read = getSideEffectFor(call, index) and
opFrom = read.getSideEffectOperand()
)
- or
- exists(ReadSideEffectInstruction read |
- modelIn.isQualifierObject() and
- read = getSideEffectFor(call, -1) and
- opFrom = read.getSideEffectOperand()
- )
)
)
}
@@ -785,6 +788,66 @@ predicate localInstructionFlow(Instruction e1, Instruction e2) {
*/
predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)) }
+/**
+ * Gets a field corresponding to the bit range `[startBit..endBit)` of class `c`, if any.
+ */
+private Field getAField(Class c, int startBit, int endBit) {
+ result.getDeclaringType() = c and
+ startBit = 8 * result.getByteOffset() and
+ endBit = 8 * result.getType().getSize() + startBit
+ or
+ exists(Field f, Class cInner |
+ f = c.getAField() and
+ cInner = f.getUnderlyingType() and
+ result = getAField(cInner, startBit - 8 * f.getByteOffset(), endBit - 8 * f.getByteOffset())
+ )
+}
+
+private newtype TContent =
+ TFieldContent(Class c, int startBit, int endBit) { exists(getAField(c, startBit, endBit)) } or
+ TCollectionContent() or
+ TArrayContent()
+
+/**
+ * A description of the way data may be stored inside an object. Examples
+ * include instance fields, the contents of a collection object, or the contents
+ * of an array.
+ */
+class Content extends TContent {
+ /** Gets a textual representation of this element. */
+ abstract string toString();
+
+ 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
+ }
+}
+
+/** A reference through an instance field. */
+class FieldContent extends Content, TFieldContent {
+ Class c;
+ int startBit;
+ int endBit;
+
+ FieldContent() { this = TFieldContent(c, startBit, endBit) }
+
+ // Ensure that there's just 1 result for `toString`.
+ override string toString() { result = min(Field f | f = getAField() | f.toString()) }
+
+ predicate hasOffset(Class cl, int start, int end) { cl = c and start = startBit and end = endBit }
+
+ Field getAField() { result = getAField(c, startBit, endBit) }
+}
+
+/** A reference through an array. */
+class ArrayContent extends Content, TArrayContent {
+ override string toString() { result = "[]" }
+}
+
+/** A reference through the contents of some collection-like container. */
+private class CollectionContent extends Content, TCollectionContent {
+ override string toString() { result = "" }
+}
+
/**
* A guard that validates some instruction.
*
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
index ab1177daea99..f563e47db9f9 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
@@ -151,6 +151,13 @@ predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
localAdditionalTaintStep(src, sink)
}
+/**
+ * Holds if default `TaintTracking::Configuration`s should allow implicit reads
+ * of `c` at sinks and inputs to additional taint steps.
+ */
+bindingset[node]
+predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::Content c) { none() }
+
/**
* Holds if `node` should be a sanitizer in all global taint flow configurations
* but not in local taint.
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
index b509fad9cd20..f4f73b8247c5 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
@@ -105,6 +105,11 @@ abstract class Configuration extends DataFlow::Configuration {
defaultAdditionalTaintStep(node1, node2)
}
+ override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) {
+ (this.isSink(node) or this.isAdditionalTaintStep(node, _)) and
+ defaultImplicitTaintRead(node, c)
+ }
+
/**
* Holds if taint may flow from `source` to `sink` for this configuration.
*/
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
index b509fad9cd20..f4f73b8247c5 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
@@ -105,6 +105,11 @@ abstract class Configuration extends DataFlow::Configuration {
defaultAdditionalTaintStep(node1, node2)
}
+ override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) {
+ (this.isSink(node) or this.isAdditionalTaintStep(node, _)) and
+ defaultImplicitTaintRead(node, c)
+ }
+
/**
* Holds if taint may flow from `source` to `sink` for this configuration.
*/
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll
index b509fad9cd20..f4f73b8247c5 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll
@@ -105,6 +105,11 @@ abstract class Configuration extends DataFlow::Configuration {
defaultAdditionalTaintStep(node1, node2)
}
+ override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) {
+ (this.isSink(node) or this.isAdditionalTaintStep(node, _)) and
+ defaultImplicitTaintRead(node, c)
+ }
+
/**
* Holds if taint may flow from `source` to `sink` for this configuration.
*/
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll
index af6f3d819824..453838215ffa 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll
@@ -297,7 +297,8 @@ class Instruction extends Construction::TStageInstruction {
/**
* Gets the opcode that specifies the operation performed by this instruction.
*/
- final Opcode getOpcode() { result = Construction::getInstructionOpcode(this) }
+ pragma[inline]
+ final Opcode getOpcode() { Construction::getInstructionOpcode(result, this) }
/**
* Gets all direct uses of the result of this instruction. The result can be
@@ -1645,6 +1646,19 @@ class CallInstruction extends Instruction {
* Gets the number of arguments of the call, including the `this` pointer, if any.
*/
final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) }
+
+ /**
+ * Holds if the result is a side effect for the argument at the specified index, or `this` if
+ * `index` is `-1`.
+ *
+ * This helper predicate makes it easy to join on both of these columns at once, avoiding
+ * pathological join orders in case the argument index should get joined first.
+ */
+ pragma[noinline]
+ final SideEffectInstruction getAParameterSideEffect(int index) {
+ this = result.getPrimaryInstruction() and
+ index = result.(IndexedInstruction).getIndex()
+ }
}
/**
@@ -1980,6 +1994,14 @@ class PhiInstruction extends Instruction {
*/
pragma[noinline]
final Instruction getAnInput() { result = this.getAnInputOperand().getDef() }
+
+ /**
+ * Gets the input operand representing the value that flows from the specified predecessor block.
+ */
+ final PhiInputOperand getInputOperand(IRBlock predecessorBlock) {
+ result = this.getAnOperand() and
+ result.getPredecessorBlock() = predecessorBlock
+ }
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll
index a2ce0662dc24..d7cf89ca9aaa 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll
@@ -28,11 +28,15 @@ class Operand extends TStageOperand {
cached
Operand() {
// Ensure that the operand does not refer to instructions from earlier stages that are unreachable here
- exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or
- exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or
+ exists(Instruction use, Instruction def | this = registerOperand(use, _, def))
+ or
+ exists(Instruction use | this = nonSSAMemoryOperand(use, _))
+ or
exists(Instruction use, Instruction def, IRBlock predecessorBlock |
- this = phiOperand(use, def, predecessorBlock, _)
- ) or
+ this = phiOperand(use, def, predecessorBlock, _) or
+ this = reusedPhiOperand(use, def, predecessorBlock, _)
+ )
+ or
exists(Instruction use | this = chiOperand(use, _))
}
@@ -431,7 +435,11 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
Overlap overlap;
cached
- PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
+ PhiInputOperand() {
+ this = phiOperand(useInstr, defInstr, predecessorBlock, overlap)
+ or
+ this = reusedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
+ }
override string toString() { result = "Phi" }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll
index 19fb0490f808..9997b5b49a78 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll
@@ -4,6 +4,71 @@ private import AliasAnalysisImports
private class IntValue = Ints::IntValue;
+/**
+ * If `instr` is a `SideEffectInstruction`, gets the primary `CallInstruction` that caused the side
+ * effect. If `instr` is a `CallInstruction`, gets that same `CallInstruction`.
+ */
+private CallInstruction getPrimaryCall(Instruction instr) {
+ result = instr
+ or
+ result = instr.(SideEffectInstruction).getPrimaryInstruction()
+}
+
+/**
+ * Holds if `operand` serves as an input argument (or indirection) to `call`, in the position
+ * specified by `input`.
+ */
+private predicate isCallInput(
+ CallInstruction call, Operand operand, AliasModels::FunctionInput input
+) {
+ call = getPrimaryCall(operand.getUse()) and
+ (
+ exists(int index |
+ input.isParameterOrQualifierAddress(index) and
+ operand = call.getArgumentOperand(index)
+ )
+ or
+ exists(int index, ReadSideEffectInstruction read |
+ input.isParameterDerefOrQualifierObject(index) and
+ read = call.getAParameterSideEffect(index) and
+ operand = read.getSideEffectOperand()
+ )
+ )
+}
+
+/**
+ * Holds if `instr` serves as a return value or output argument indirection for `call`, in the
+ * position specified by `output`.
+ */
+private predicate isCallOutput(
+ CallInstruction call, Instruction instr, AliasModels::FunctionOutput output
+) {
+ call = getPrimaryCall(instr) and
+ (
+ output.isReturnValue() and instr = call
+ or
+ exists(int index, WriteSideEffectInstruction write |
+ output.isParameterDerefOrQualifierObject(index) and
+ write = call.getAParameterSideEffect(index) and
+ instr = write
+ )
+ )
+}
+
+/**
+ * Holds if the address in `operand` flows directly to the result of `resultInstr` due to modeled
+ * address flow through a function call.
+ */
+private predicate hasAddressFlowThroughCall(Operand operand, Instruction resultInstr) {
+ exists(
+ CallInstruction call, AliasModels::FunctionInput input, AliasModels::FunctionOutput output
+ |
+ call.getStaticCallTarget().(AliasModels::AliasFunction).hasAddressFlow(input, output) and
+ isCallInput(call, operand, input) and
+ isCallOutput(call, resultInstr, output)
+ )
+}
+
/**
* Holds if the operand `tag` of instruction `instr` is used in a way that does
* not result in any address held in that operand from escaping beyond the
@@ -25,6 +90,9 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) {
or
// Converting an address to a `bool` does not escape the address.
instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType
+ or
+ instr instanceof CallInstruction and
+ not exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis())
)
)
or
@@ -34,7 +102,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) {
private predicate operandEscapesDomain(Operand operand) {
not operandIsConsumedWithoutEscaping(operand) and
- not operandIsPropagated(operand, _) and
+ not operandIsPropagated(operand, _, _) and
not isArgumentForParameter(_, operand, _) and
not isOnlyEscapesViaReturnArgument(operand) and
not operand.getUse() instanceof ReturnValueInstruction and
@@ -69,67 +137,67 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) {
}
/**
- * Holds if any address held in operand `tag` of instruction `instr` is
- * propagated to the result of `instr`, offset by the number of bits in
- * `bitOffset`. If the address is propagated, but the offset is not known to be
- * a constant, then `bitOffset` is unknown.
+ * Holds if any address held in operand `operand` is propagated to the result of `instr`, offset by
+ * the number of bits in `bitOffset`. If the address is propagated, but the offset is not known to
+ * be a constant, then `bitOffset` is `unknown()`.
*/
-private predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
- exists(Instruction instr |
- instr = operand.getUse() and
- (
- // Converting to a non-virtual base class adds the offset of the base class.
- exists(ConvertToNonVirtualBaseInstruction convert |
- convert = instr and
- bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
- )
- or
- // Conversion using dynamic_cast results in an unknown offset
- instr instanceof CheckedConvertOrNullInstruction and
- bitOffset = Ints::unknown()
- or
- // Converting to a derived class subtracts the offset of the base class.
- exists(ConvertToDerivedInstruction convert |
- convert = instr and
- bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8))
- )
- or
- // Converting to a virtual base class adds an unknown offset.
- instr instanceof ConvertToVirtualBaseInstruction and
- bitOffset = Ints::unknown()
- or
- // Conversion to another pointer type propagates the source address.
- exists(ConvertInstruction convert, IRType resultType |
- convert = instr and
- resultType = convert.getResultIRType() and
- resultType instanceof IRAddressType and
- bitOffset = 0
- )
- or
- // Adding an integer to or subtracting an integer from a pointer propagates
- // the address with an offset.
- exists(PointerOffsetInstruction ptrOffset |
- ptrOffset = instr and
- operand = ptrOffset.getLeftOperand() and
- bitOffset = getPointerBitOffset(ptrOffset)
- )
- or
- // Computing a field address from a pointer propagates the address plus the
- // offset of the field.
- bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField())
- or
- // A copy propagates the source value.
- operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0
- or
- // Some functions are known to propagate an argument
- isAlwaysReturnedArgument(operand) and bitOffset = 0
+private predicate operandIsPropagated(Operand operand, IntValue bitOffset, Instruction instr) {
+ // Some functions are known to propagate an argument
+ hasAddressFlowThroughCall(operand, instr) and
+ bitOffset = 0
+ or
+ instr = operand.getUse() and
+ (
+ // Converting to a non-virtual base class adds the offset of the base class.
+ exists(ConvertToNonVirtualBaseInstruction convert |
+ convert = instr and
+ bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
+ )
+ or
+ // Conversion using dynamic_cast results in an unknown offset
+ instr instanceof CheckedConvertOrNullInstruction and
+ bitOffset = Ints::unknown()
+ or
+ // Converting to a derived class subtracts the offset of the base class.
+ exists(ConvertToDerivedInstruction convert |
+ convert = instr and
+ bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8))
+ )
+ or
+ // Converting to a virtual base class adds an unknown offset.
+ instr instanceof ConvertToVirtualBaseInstruction and
+ bitOffset = Ints::unknown()
+ or
+ // Conversion to another pointer type propagates the source address.
+ exists(ConvertInstruction convert, IRType resultType |
+ convert = instr and
+ resultType = convert.getResultIRType() and
+ resultType instanceof IRAddressType and
+ bitOffset = 0
)
+ or
+ // Adding an integer to or subtracting an integer from a pointer propagates
+ // the address with an offset.
+ exists(PointerOffsetInstruction ptrOffset |
+ ptrOffset = instr and
+ operand = ptrOffset.getLeftOperand() and
+ bitOffset = getPointerBitOffset(ptrOffset)
+ )
+ or
+ // Computing a field address from a pointer propagates the address plus the
+ // offset of the field.
+ bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField())
+ or
+ // A copy propagates the source value.
+ operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0
)
}
private predicate operandEscapesNonReturn(Operand operand) {
- // The address is propagated to the result of the instruction, and that result itself is returned
- operandIsPropagated(operand, _) and resultEscapesNonReturn(operand.getUse())
+ exists(Instruction instr |
+ // The address is propagated to the result of the instruction, and that result itself is returned
+ operandIsPropagated(operand, _, instr) and resultEscapesNonReturn(instr)
+ )
or
// The operand is used in a function call which returns it, and the return value is then returned
exists(CallInstruction ci, Instruction init |
@@ -151,9 +219,11 @@ private predicate operandEscapesNonReturn(Operand operand) {
}
private predicate operandMayReachReturn(Operand operand) {
- // The address is propagated to the result of the instruction, and that result itself is returned
- operandIsPropagated(operand, _) and
- resultMayReachReturn(operand.getUse())
+ exists(Instruction instr |
+ // The address is propagated to the result of the instruction, and that result itself is returned
+ operandIsPropagated(operand, _, instr) and
+ resultMayReachReturn(instr)
+ )
or
// The operand is used in a function call which returns it, and the return value is then returned
exists(CallInstruction ci, Instruction init |
@@ -173,9 +243,9 @@ private predicate operandMayReachReturn(Operand operand) {
private predicate operandReturned(Operand operand, IntValue bitOffset) {
// The address is propagated to the result of the instruction, and that result itself is returned
- exists(IntValue bitOffset1, IntValue bitOffset2 |
- operandIsPropagated(operand, bitOffset1) and
- resultReturned(operand.getUse(), bitOffset2) and
+ exists(Instruction instr, IntValue bitOffset1, IntValue bitOffset2 |
+ operandIsPropagated(operand, bitOffset1, instr) and
+ resultReturned(instr, bitOffset2) and
bitOffset = Ints::add(bitOffset1, bitOffset2)
)
or
@@ -214,24 +284,27 @@ private predicate isArgumentForParameter(
)
}
-private predicate isAlwaysReturnedArgument(Operand operand) {
- exists(AliasModels::AliasFunction f |
- f = operand.getUse().(CallInstruction).getStaticCallTarget() and
- f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex())
- )
-}
-
private predicate isOnlyEscapesViaReturnArgument(Operand operand) {
exists(AliasModels::AliasFunction f |
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
- f.parameterEscapesOnlyViaReturn(operand.(PositionalArgumentOperand).getIndex())
+ (
+ f.parameterEscapesOnlyViaReturn(operand.(PositionalArgumentOperand).getIndex())
+ or
+ f.parameterEscapesOnlyViaReturn(-1) and
+ operand instanceof ThisArgumentOperand
+ )
)
}
private predicate isNeverEscapesArgument(Operand operand) {
exists(AliasModels::AliasFunction f |
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
- f.parameterNeverEscapes(operand.(PositionalArgumentOperand).getIndex())
+ (
+ f.parameterNeverEscapes(operand.(PositionalArgumentOperand).getIndex())
+ or
+ f.parameterNeverEscapes(-1) and
+ operand instanceof ThisArgumentOperand
+ )
)
}
@@ -265,17 +338,23 @@ predicate allocationEscapes(Configuration::Allocation allocation) {
exists(IREscapeAnalysisConfiguration config |
config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction())
)
+ or
+ Configuration::phaseNeedsSoundEscapeAnalysis() and
+ resultEscapesNonReturn(allocation.getABaseInstruction())
}
/**
* Equivalent to `operandIsPropagated()`, but includes interprocedural propagation.
*/
-private predicate operandIsPropagatedIncludingByCall(Operand operand, IntValue bitOffset) {
- operandIsPropagated(operand, bitOffset)
+private predicate operandIsPropagatedIncludingByCall(
+ Operand operand, IntValue bitOffset, Instruction instr
+) {
+ operandIsPropagated(operand, bitOffset, instr)
or
exists(CallInstruction call, Instruction init |
isArgumentForParameter(call, operand, init) and
- resultReturned(init, bitOffset)
+ resultReturned(init, bitOffset) and
+ instr = call
)
}
@@ -292,8 +371,7 @@ private predicate hasBaseAndOffset(AddressOperand addrOperand, Instruction base,
// We already have an offset from `middle`.
hasBaseAndOffset(addrOperand, middle, previousBitOffset) and
// `middle` is propagated from `base`.
- middleOperand = middle.getAnOperand() and
- operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset) and
+ operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset, middle) and
base = middleOperand.getDef() and
bitOffset = Ints::add(previousBitOffset, additionalBitOffset)
)
@@ -333,8 +411,51 @@ predicate addressOperandAllocationAndOffset(
allocation.getABaseInstruction() = base and
hasBaseAndOffset(addrOperand, base, bitOffset) and
not exists(Instruction previousBase |
- hasBaseAndOffset(addrOperand, previousBase, _) and
+ hasBaseAndOffset(addrOperand, pragma[only_bind_out](previousBase), _) and
previousBase = base.getAnOperand().getDef()
)
)
}
+
+/**
+ * Predicates used only for printing annotated IR dumps. These should not be used in production
+ * queries.
+ */
+module Print {
+ string getOperandProperty(Operand operand, string key) {
+ key = "alloc" and
+ result =
+ strictconcat(Configuration::Allocation allocation, IntValue bitOffset |
+ addressOperandAllocationAndOffset(operand, allocation, bitOffset)
+ |
+ allocation.toString() + Ints::getBitOffsetString(bitOffset), ", "
+ )
+ or
+ key = "prop" and
+ result =
+ strictconcat(Instruction destInstr, IntValue bitOffset, string value |
+ operandIsPropagatedIncludingByCall(operand, bitOffset, destInstr) and
+ if destInstr = operand.getUse()
+ then value = "@" + Ints::getBitOffsetString(bitOffset) + "->result"
+ else value = "@" + Ints::getBitOffsetString(bitOffset) + "->" + destInstr.getResultId()
+ |
+ value, ", "
+ )
+ }
+
+ string getInstructionProperty(Instruction instr, string key) {
+ key = "prop" and
+ result =
+ strictconcat(IntValue bitOffset, Operand sourceOperand, string value |
+ operandIsPropagatedIncludingByCall(sourceOperand, bitOffset, instr) and
+ if instr = sourceOperand.getUse()
+ then value = sourceOperand.getDumpId() + Ints::getBitOffsetString(bitOffset) + "->@"
+ else
+ value =
+ sourceOperand.getUse().getResultId() + "." + sourceOperand.getDumpId() +
+ Ints::getBitOffsetString(bitOffset) + "->@"
+ |
+ value, ", "
+ )
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll
index 69cd6e6dc291..8ba91d700877 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll
@@ -2,9 +2,13 @@ private import AliasConfigurationInternal
private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR
private import cpp
private import AliasAnalysis
+private import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SimpleSSA as UnaliasedSSA
private newtype TAllocation =
- TVariableAllocation(IRVariable var) or
+ TVariableAllocation(IRVariable var) {
+ // Only model variables that were not already handled in unaliased SSA.
+ not UnaliasedSSA::canReuseSSAForVariable(var)
+ } or
TIndirectParameterAllocation(IRAutomaticVariable var) {
exists(InitializeIndirectionInstruction instr | instr.getIRVariable() = var)
} or
@@ -105,7 +109,21 @@ class DynamicAllocation extends Allocation, TDynamicAllocation {
DynamicAllocation() { this = TDynamicAllocation(call) }
final override string toString() {
- result = call.toString() + " at " + call.getLocation() // This isn't performant, but it's only used in test/dump code right now.
+ // This isn't performant, but it's only used in test/dump code right now.
+ // Dynamic allocations within a function are numbered in the order by start
+ // line number. This keeps them stable when the function moves within the
+ // file, or when non-allocating lines are added and removed within the
+ // function.
+ exists(int i |
+ result = "dynamic{" + i.toString() + "}" and
+ call =
+ rank[i](CallInstruction rangeCall |
+ exists(TDynamicAllocation(rangeCall)) and
+ rangeCall.getEnclosingIRFunction() = call.getEnclosingIRFunction()
+ |
+ rangeCall order by rangeCall.getLocation().getStartLine()
+ )
+ )
}
final override CallInstruction getABaseInstruction() { result = call }
@@ -124,3 +142,5 @@ class DynamicAllocation extends Allocation, TDynamicAllocation {
final override predicate alwaysEscapes() { none() }
}
+
+predicate phaseNeedsSoundEscapeAnalysis() { none() }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll
index fdabee2affee..acdae2b758a5 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll
@@ -3,6 +3,7 @@ import semmle.code.cpp.ir.internal.Overlap
private import semmle.code.cpp.ir.internal.IRCppLanguage as Language
private import semmle.code.cpp.Print
private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR
+private import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction as OldSSA
private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
private import semmle.code.cpp.ir.internal.IntegerInterval as Interval
private import semmle.code.cpp.ir.implementation.internal.OperandTag
@@ -131,6 +132,8 @@ abstract class MemoryLocation extends TMemoryLocation {
* with automatic storage duration).
*/
predicate isAlwaysAllocatedOnStack() { none() }
+
+ final predicate canReuseSSA() { none() }
}
/**
@@ -562,10 +565,17 @@ private Overlap getVariableMemoryLocationOverlap(
use.getEndBitOffset())
}
+/**
+ * Holds if the def/use information for the result of `instr` can be reused from the previous
+ * iteration of the IR.
+ */
+predicate canReuseSSAForOldResult(Instruction instr) { OldSSA::canReuseSSAForMemoryResult(instr) }
+
bindingset[result, b]
private boolean unbindBool(boolean b) { result != b.booleanNot() }
MemoryLocation getResultMemoryLocation(Instruction instr) {
+ not canReuseSSAForOldResult(instr) and
exists(MemoryAccessKind kind, boolean isMayAccess |
kind = instr.getResultMemoryAccess() and
(if instr.hasResultMayMemoryAccess() then isMayAccess = true else isMayAccess = false) and
@@ -598,6 +608,7 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
}
MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
+ not canReuseSSAForOldResult(operand.getAnyDef()) and
exists(MemoryAccessKind kind, boolean isMayAccess |
kind = operand.getMemoryAccess() and
(if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasAnalysis.qll
new file mode 100644
index 000000000000..262088245e8b
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasAnalysis.qll
@@ -0,0 +1,19 @@
+/**
+ * Include this module to annotate IR dumps with information computed by `AliasAnalysis.qll`.
+ */
+
+private import AliasAnalysisInternal
+private import InputIR
+private import AliasAnalysisImports
+private import AliasAnalysis
+private import semmle.code.cpp.ir.internal.IntegerConstant
+
+private class AliasPropertyProvider extends IRPropertyProvider {
+ override string getOperandProperty(Operand operand, string key) {
+ result = Print::getOperandProperty(operand, key)
+ }
+
+ override string getInstructionProperty(Instruction instr, string key) {
+ result = Print::getInstructionProperty(instr, key)
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll
index 340f524fce8e..5092e921cb33 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll
@@ -43,24 +43,81 @@ private module Cached {
class TStageInstruction =
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction;
+ /**
+ * If `oldInstruction` is a `Phi` instruction that has exactly one reachable predecessor block,
+ * this predicate returns the `PhiInputOperand` corresponding to that predecessor block.
+ * Otherwise, this predicate does not hold.
+ */
+ private OldIR::PhiInputOperand getDegeneratePhiOperand(OldInstruction oldInstruction) {
+ result =
+ unique(OldIR::PhiInputOperand operand |
+ operand = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and
+ operand.getPredecessorBlock() instanceof OldBlock
+ )
+ }
+
cached
predicate hasInstruction(TStageInstruction instr) {
instr instanceof TRawInstruction and instr instanceof OldInstruction
or
- instr instanceof TPhiInstruction
+ instr = phiInstruction(_, _)
+ or
+ instr = reusedPhiInstruction(_) and
+ // Check that the phi instruction is *not* degenerate, but we can't use
+ // getDegeneratePhiOperand in the first stage with phi instyructions
+ not exists(
+ unique(OldIR::PhiInputOperand operand |
+ operand = instr.(OldIR::PhiInstruction).getAnInputOperand() and
+ operand.getPredecessorBlock() instanceof OldBlock
+ )
+ )
or
instr instanceof TChiInstruction
or
instr instanceof TUnreachedInstruction
}
- private IRBlock getNewBlock(OldBlock oldBlock) {
- result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
+ cached
+ IRBlock getNewBlock(OldBlock oldBlock) {
+ exists(Instruction newEnd, OldIR::Instruction oldEnd |
+ (
+ result.getLastInstruction() = newEnd and
+ not newEnd instanceof ChiInstruction
+ or
+ newEnd = result.getLastInstruction().(ChiInstruction).getAPredecessor() // does this work?
+ ) and
+ (
+ oldBlock.getLastInstruction() = oldEnd and
+ not oldEnd instanceof OldIR::ChiInstruction
+ or
+ oldEnd = oldBlock.getLastInstruction().(OldIR::ChiInstruction).getAPredecessor() // does this work?
+ ) and
+ oldEnd = getNewInstruction(newEnd)
+ )
+ }
+
+ /**
+ * Gets the block from the old IR that corresponds to `newBlock`.
+ */
+ private OldBlock getOldBlock(IRBlock newBlock) { getNewBlock(result) = newBlock }
+
+ /**
+ * Holds if this iteration of SSA can model the def/use information for the result of
+ * `oldInstruction`, either because alias analysis has determined a memory location for that
+ * result, or because a previous iteration of the IR already computed that def/use information
+ * completely.
+ */
+ private predicate canModelResultForOldInstruction(OldInstruction oldInstruction) {
+ // We're modeling the result's memory location ourselves.
+ exists(Alias::getResultMemoryLocation(oldInstruction))
+ or
+ // This result was already modeled by a previous iteration of SSA.
+ Alias::canReuseSSAForOldResult(oldInstruction)
}
cached
predicate hasModeledMemoryResult(Instruction instruction) {
- exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or
+ canModelResultForOldInstruction(getOldInstruction(instruction)) or
instruction instanceof PhiInstruction or // Phis always have modeled results
instruction instanceof ChiInstruction // Chis always have modeled results
}
@@ -117,6 +174,32 @@ private module Cached {
)
}
+ /**
+ * Gets the new definition instruction for `oldOperand` based on `oldOperand`'s definition in the
+ * old IR. Usually, this will just get the old definition of `oldOperand` and map it to the
+ * corresponding new instruction. However, if the old definition of `oldOperand` is a `Phi`
+ * instruction that is now degenerate due all but one of its predecessor branches being
+ * unreachable, this predicate will recurse through any degenerate `Phi` instructions to find the
+ * true definition.
+ */
+ private Instruction getNewDefinitionFromOldSSA(OldIR::MemoryOperand oldOperand, Overlap overlap) {
+ exists(Overlap originalOverlap |
+ originalOverlap = oldOperand.getDefinitionOverlap() and
+ (
+ result = getNewInstruction(oldOperand.getAnyDef()) and
+ overlap = originalOverlap
+ or
+ exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap |
+ phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and
+ result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and
+ overlap =
+ combineOverlap(pragma[only_bind_out](phiOperandOverlap),
+ pragma[only_bind_out](originalOverlap))
+ )
+ )
+ )
+ }
+
cached
private Instruction getMemoryOperandDefinition0(
Instruction instruction, MemoryOperandTag tag, Overlap overlap
@@ -148,6 +231,12 @@ private module Cached {
overlap instanceof MustExactlyOverlap and
exists(MustTotallyOverlap o | exists(getMemoryOperandDefinition0(instruction, tag, o)))
)
+ or
+ exists(OldIR::NonPhiMemoryOperand oldOperand |
+ result = getNewDefinitionFromOldSSA(oldOperand, overlap) and
+ oldOperand.getUse() = instruction and
+ tag = oldOperand.getOperandTag()
+ )
}
/**
@@ -214,10 +303,24 @@ private module Cached {
)
}
+ /**
+ * Gets the new definition instruction for the operand of `instr` that flows from the block
+ * `newPredecessorBlock`, based on that operand's definition in the old IR.
+ */
+ private Instruction getNewPhiOperandDefinitionFromOldSSA(
+ Instruction instr, IRBlock newPredecessorBlock, Overlap overlap
+ ) {
+ exists(OldIR::PhiInstruction oldPhi, OldIR::PhiInputOperand oldOperand |
+ oldPhi = getOldInstruction(instr) and
+ oldOperand = oldPhi.getInputOperand(getOldBlock(newPredecessorBlock)) and
+ result = getNewDefinitionFromOldSSA(oldOperand, overlap)
+ )
+ }
+
pragma[noopt]
cached
Instruction getPhiOperandDefinition(
- PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap
+ Instruction instr, IRBlock newPredecessorBlock, Overlap overlap
) {
exists(
Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock,
@@ -229,6 +332,8 @@ private module Cached {
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
overlap = Alias::getOverlap(actualDefLocation, useLocation)
)
+ or
+ result = getNewPhiOperandDefinitionFromOldSSA(instr, newPredecessorBlock, overlap)
}
cached
@@ -249,7 +354,12 @@ private module Cached {
cached
Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
exists(OldBlock oldBlock |
- instr = getPhi(oldBlock, _) and
+ (
+ instr = getPhi(oldBlock, _)
+ or
+ // Any `Phi` that we propagated from the previous iteration stays in the same block.
+ getOldInstruction(instr).getBlock() = oldBlock
+ ) and
result = getNewInstruction(oldBlock.getFirstInstruction())
)
}
@@ -335,18 +445,27 @@ private module Cached {
result = vvar.getType()
)
or
+ instr = reusedPhiInstruction(_) and
+ result = instr.(OldInstruction).getResultLanguageType()
+ or
instr = unreachedInstruction(_) and result = Language::getVoidType()
}
+ /**
+ * Holds if `opcode` is the opcode that specifies the operation performed by `instr`.
+ *
+ * The parameters are ordered such that they produce a clean join (with no need for reordering)
+ * in the characteristic predicates of the `Instruction` subclasses.
+ */
cached
- Opcode getInstructionOpcode(Instruction instr) {
- result = getOldInstruction(instr).getOpcode()
+ predicate getInstructionOpcode(Opcode opcode, Instruction instr) {
+ opcode = getOldInstruction(instr).getOpcode()
or
- instr = phiInstruction(_, _) and result instanceof Opcode::Phi
+ instr = phiInstruction(_, _) and opcode instanceof Opcode::Phi
or
- instr = chiInstruction(_) and result instanceof Opcode::Chi
+ instr = chiInstruction(_) and opcode instanceof Opcode::Chi
or
- instr = unreachedInstruction(_) and result instanceof Opcode::Unreached
+ instr = unreachedInstruction(_) and opcode instanceof Opcode::Unreached
}
cached
@@ -856,6 +975,26 @@ module DefUse {
}
}
+predicate canReuseSSAForMemoryResult(Instruction instruction) {
+ exists(OldInstruction oldInstruction |
+ oldInstruction = getOldInstruction(instruction) and
+ (
+ // The previous iteration said it was reusable, so we should mark it as reusable as well.
+ Alias::canReuseSSAForOldResult(oldInstruction)
+ or
+ // The current alias analysis says it is reusable.
+ Alias::getResultMemoryLocation(oldInstruction).canReuseSSA()
+ )
+ )
+ or
+ exists(Alias::MemoryLocation defLocation |
+ // This is a `Phi` for a reusable location, so the result of the `Phi` is reusable as well.
+ instruction = phiInstruction(_, defLocation) and
+ defLocation.canReuseSSA()
+ )
+ // We don't support reusing SSA for any location that could create a `Chi` instruction.
+}
+
/**
* Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the
* `DebugSSA` module, which is then imported by PrintSSA.
@@ -934,7 +1073,10 @@ module SSAConsistency {
locationCount > 1 and
func = operand.getEnclosingIRFunction() and
funcText = Language::getIdentityString(func.getFunction()) and
- message = "Operand has " + locationCount.toString() + " memory accesses in function '$@'."
+ message =
+ operand.getUse().toString() + " " + "Operand has " + locationCount.toString() +
+ " memory accesses in function '$@': " +
+ strictconcat(Alias::getOperandMemoryLocation(operand).toString(), ", ")
)
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll
index e16b71733b5a..4b3f19cbdde2 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll
@@ -55,6 +55,8 @@ module UnaliasedSSAInstructions {
result = TUnaliasedSSAPhiInstruction(blockStartInstr, memoryLocation)
}
+ TRawInstruction reusedPhiInstruction(TRawInstruction blockStartInstr) { none() }
+
class TChiInstruction = TUnaliasedSSAChiInstruction;
TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
@@ -75,7 +77,7 @@ module UnaliasedSSAInstructions {
* a class alias.
*/
module AliasedSSAInstructions {
- class TPhiInstruction = TAliasedSSAPhiInstruction;
+ class TPhiInstruction = TAliasedSSAPhiInstruction or TUnaliasedSSAPhiInstruction;
TPhiInstruction phiInstruction(
TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation
@@ -83,6 +85,10 @@ module AliasedSSAInstructions {
result = TAliasedSSAPhiInstruction(blockStartInstr, memoryLocation)
}
+ TPhiInstruction reusedPhiInstruction(TRawInstruction blockStartInstr) {
+ result = TUnaliasedSSAPhiInstruction(blockStartInstr, _)
+ }
+
class TChiInstruction = TAliasedSSAChiInstruction;
TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll
index 1e132463cdd0..e86494af03a0 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll
@@ -36,10 +36,9 @@ private module Internal {
useInstr.getOpcode().hasOperand(tag)
} or
TUnaliasedPhiOperand(
- Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr,
- Unaliased::IRBlock predecessorBlock, Overlap overlap
+ Unaliased::PhiInstruction useInstr, Unaliased::IRBlock predecessorBlock, Overlap overlap
) {
- defInstr = UnaliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
+ exists(UnaliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap))
} or
//// ALIASED
////
@@ -50,10 +49,9 @@ private module Internal {
// important that we use the same definition of "is variable aliased" across
// the phases.
TAliasedPhiOperand(
- TAliasedSSAPhiInstruction useInstr, Aliased::Instruction defInstr,
- Aliased::IRBlock predecessorBlock, Overlap overlap
+ TAliasedSSAPhiInstruction useInstr, Aliased::IRBlock predecessorBlock, Overlap overlap
) {
- defInstr = AliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
+ exists(AliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap))
} or
TAliasedChiOperand(TAliasedSSAChiInstruction useInstr, ChiOperandTag tag) { any() }
}
@@ -109,6 +107,13 @@ module RawOperands {
none()
}
+ TPhiOperand reusedPhiOperand(
+ Raw::PhiInstruction useInstr, Raw::Instruction defInstr, Raw::IRBlock predecessorBlock,
+ Overlap overlap
+ ) {
+ none()
+ }
+
/**
* Returns the Chi operand with the specified parameters.
*/
@@ -137,7 +142,15 @@ module UnaliasedSSAOperands {
Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr,
Unaliased::IRBlock predecessorBlock, Overlap overlap
) {
- result = Internal::TUnaliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
+ defInstr = UnaliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap) and
+ result = Internal::TUnaliasedPhiOperand(useInstr, predecessorBlock, overlap)
+ }
+
+ TPhiOperand reusedPhiOperand(
+ Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr,
+ Unaliased::IRBlock predecessorBlock, Overlap overlap
+ ) {
+ none()
}
/**
@@ -155,7 +168,7 @@ module UnaliasedSSAOperands {
module AliasedSSAOperands {
import Shared
- class TPhiOperand = Internal::TAliasedPhiOperand;
+ class TPhiOperand = Internal::TAliasedPhiOperand or Internal::TUnaliasedPhiOperand;
class TChiOperand = Internal::TAliasedChiOperand;
@@ -165,10 +178,25 @@ module AliasedSSAOperands {
* Returns the Phi operand with the specified parameters.
*/
TPhiOperand phiOperand(
- TAliasedSSAPhiInstruction useInstr, Aliased::Instruction defInstr,
+ Aliased::PhiInstruction useInstr, Aliased::Instruction defInstr,
+ Aliased::IRBlock predecessorBlock, Overlap overlap
+ ) {
+ defInstr = AliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap) and
+ result = Internal::TAliasedPhiOperand(useInstr, predecessorBlock, overlap)
+ }
+
+ /**
+ * Returns the Phi operand with the specified parameters.
+ */
+ TPhiOperand reusedPhiOperand(
+ Aliased::PhiInstruction useInstr, Aliased::Instruction defInstr,
Aliased::IRBlock predecessorBlock, Overlap overlap
) {
- result = Internal::TAliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
+ exists(Unaliased::IRBlock oldBlock |
+ predecessorBlock = AliasedConstruction::getNewBlock(oldBlock) and
+ result = Internal::TUnaliasedPhiOperand(useInstr, oldBlock, _) and
+ defInstr = AliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
+ )
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll
index af6f3d819824..453838215ffa 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll
@@ -297,7 +297,8 @@ class Instruction extends Construction::TStageInstruction {
/**
* Gets the opcode that specifies the operation performed by this instruction.
*/
- final Opcode getOpcode() { result = Construction::getInstructionOpcode(this) }
+ pragma[inline]
+ final Opcode getOpcode() { Construction::getInstructionOpcode(result, this) }
/**
* Gets all direct uses of the result of this instruction. The result can be
@@ -1645,6 +1646,19 @@ class CallInstruction extends Instruction {
* Gets the number of arguments of the call, including the `this` pointer, if any.
*/
final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) }
+
+ /**
+ * Holds if the result is a side effect for the argument at the specified index, or `this` if
+ * `index` is `-1`.
+ *
+ * This helper predicate makes it easy to join on both of these columns at once, avoiding
+ * pathological join orders in case the argument index should get joined first.
+ */
+ pragma[noinline]
+ final SideEffectInstruction getAParameterSideEffect(int index) {
+ this = result.getPrimaryInstruction() and
+ index = result.(IndexedInstruction).getIndex()
+ }
}
/**
@@ -1980,6 +1994,14 @@ class PhiInstruction extends Instruction {
*/
pragma[noinline]
final Instruction getAnInput() { result = this.getAnInputOperand().getDef() }
+
+ /**
+ * Gets the input operand representing the value that flows from the specified predecessor block.
+ */
+ final PhiInputOperand getInputOperand(IRBlock predecessorBlock) {
+ result = this.getAnOperand() and
+ result.getPredecessorBlock() = predecessorBlock
+ }
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll
index a2ce0662dc24..d7cf89ca9aaa 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll
@@ -28,11 +28,15 @@ class Operand extends TStageOperand {
cached
Operand() {
// Ensure that the operand does not refer to instructions from earlier stages that are unreachable here
- exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or
- exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or
+ exists(Instruction use, Instruction def | this = registerOperand(use, _, def))
+ or
+ exists(Instruction use | this = nonSSAMemoryOperand(use, _))
+ or
exists(Instruction use, Instruction def, IRBlock predecessorBlock |
- this = phiOperand(use, def, predecessorBlock, _)
- ) or
+ this = phiOperand(use, def, predecessorBlock, _) or
+ this = reusedPhiOperand(use, def, predecessorBlock, _)
+ )
+ or
exists(Instruction use | this = chiOperand(use, _))
}
@@ -431,7 +435,11 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
Overlap overlap;
cached
- PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
+ PhiInputOperand() {
+ this = phiOperand(useInstr, defInstr, predecessorBlock, overlap)
+ or
+ this = reusedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
+ }
override string toString() { result = "Phi" }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll
index b0faec547f3d..e8fcf3fcdf34 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll
@@ -360,8 +360,8 @@ CppType getInstructionResultType(TStageInstruction instr) {
getInstructionTranslatedElement(instr).hasInstruction(_, getInstructionTag(instr), result)
}
-Opcode getInstructionOpcode(TStageInstruction instr) {
- getInstructionTranslatedElement(instr).hasInstruction(result, getInstructionTag(instr), _)
+predicate getInstructionOpcode(Opcode opcode, TStageInstruction instr) {
+ getInstructionTranslatedElement(instr).hasInstruction(opcode, getInstructionTag(instr), _)
}
IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) {
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll
index 350127a58d17..50245fafde2e 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll
@@ -7,8 +7,67 @@
private import cpp
private import semmle.code.cpp.ir.implementation.Opcode
+private import semmle.code.cpp.models.interfaces.PointerWrapper
private import semmle.code.cpp.models.interfaces.SideEffect
+private predicate isDeeplyConst(Type t) {
+ t.isConst() and
+ isDeeplyConstBelow(t)
+ or
+ isDeeplyConst(t.(Decltype).getBaseType())
+ or
+ isDeeplyConst(t.(ReferenceType).getBaseType())
+ or
+ exists(SpecifiedType specType | specType = t |
+ specType.getASpecifier().getName() = "const" and
+ isDeeplyConstBelow(specType.getBaseType())
+ )
+ or
+ isDeeplyConst(t.(ArrayType).getBaseType())
+}
+
+private predicate isDeeplyConstBelow(Type t) {
+ t instanceof BuiltInType
+ or
+ not t instanceof PointerWrapper and
+ t instanceof Class
+ or
+ t instanceof Enum
+ or
+ isDeeplyConstBelow(t.(Decltype).getBaseType())
+ or
+ isDeeplyConst(t.(PointerType).getBaseType())
+ or
+ isDeeplyConst(t.(ReferenceType).getBaseType())
+ or
+ isDeeplyConstBelow(t.(SpecifiedType).getBaseType())
+ or
+ isDeeplyConst(t.(ArrayType).getBaseType())
+ or
+ isDeeplyConst(t.(GNUVectorType).getBaseType())
+ or
+ isDeeplyConst(t.(FunctionPointerIshType).getBaseType())
+ or
+ isDeeplyConst(t.(PointerWrapper).getTemplateArgument(0))
+ or
+ isDeeplyConst(t.(PointerToMemberType).getBaseType())
+ or
+ isDeeplyConstBelow(t.(TypedefType).getBaseType())
+}
+
+private predicate isConstPointerLike(Type t) {
+ (
+ t instanceof PointerWrapper
+ or
+ t instanceof PointerType
+ or
+ t instanceof ArrayType
+ or
+ t instanceof ReferenceType
+ ) and
+ isDeeplyConstBelow(t)
+}
+
/**
* Holds if the specified call has a side effect that does not come from a `SideEffectFunction`
* model.
@@ -39,11 +98,12 @@ private predicate hasDefaultSideEffect(Call call, ParameterIndex i, boolean buff
exists(Type t | t = expr.getUnspecifiedType() |
t instanceof ArrayType or
t instanceof PointerType or
- t instanceof ReferenceType
+ t instanceof ReferenceType or
+ t instanceof PointerWrapper
) and
(
isWrite = true and
- not call.getTarget().getParameter(i).getType().isDeeplyConstBelow()
+ not isConstPointerLike(call.getTarget().getParameter(i).getUnderlyingType())
or
isWrite = false
)
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll
index eca659914b02..81c69cf0ea22 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll
@@ -42,7 +42,8 @@ IRTempVariable getIRTempVariable(Locatable ast, TempVariableTag tag) {
*/
predicate isIRConstant(Expr expr) { exists(expr.getValue()) }
-// Pulled out to work around QL-796
+// Pulled out for performance. See
+// https://github.com/github/codeql-coreql-team/issues/1044.
private predicate isOrphan(Expr expr) { not exists(getRealParent(expr)) }
/**
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll
index f55d661b202e..2a0b58ce96a6 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll
@@ -725,9 +725,9 @@ abstract class TranslatedReadEffect extends TranslatedElement {
override Instruction getChildSuccessor(TranslatedElement child) { none() }
- override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind edge) {
+ override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = OnlyInstructionTag() and
- edge = EdgeKind::gotoEdge() and
+ kind = EdgeKind::gotoEdge() and
result = getParent().getChildSuccessor(this)
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll
index af6f3d819824..453838215ffa 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll
@@ -297,7 +297,8 @@ class Instruction extends Construction::TStageInstruction {
/**
* Gets the opcode that specifies the operation performed by this instruction.
*/
- final Opcode getOpcode() { result = Construction::getInstructionOpcode(this) }
+ pragma[inline]
+ final Opcode getOpcode() { Construction::getInstructionOpcode(result, this) }
/**
* Gets all direct uses of the result of this instruction. The result can be
@@ -1645,6 +1646,19 @@ class CallInstruction extends Instruction {
* Gets the number of arguments of the call, including the `this` pointer, if any.
*/
final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) }
+
+ /**
+ * Holds if the result is a side effect for the argument at the specified index, or `this` if
+ * `index` is `-1`.
+ *
+ * This helper predicate makes it easy to join on both of these columns at once, avoiding
+ * pathological join orders in case the argument index should get joined first.
+ */
+ pragma[noinline]
+ final SideEffectInstruction getAParameterSideEffect(int index) {
+ this = result.getPrimaryInstruction() and
+ index = result.(IndexedInstruction).getIndex()
+ }
}
/**
@@ -1980,6 +1994,14 @@ class PhiInstruction extends Instruction {
*/
pragma[noinline]
final Instruction getAnInput() { result = this.getAnInputOperand().getDef() }
+
+ /**
+ * Gets the input operand representing the value that flows from the specified predecessor block.
+ */
+ final PhiInputOperand getInputOperand(IRBlock predecessorBlock) {
+ result = this.getAnOperand() and
+ result.getPredecessorBlock() = predecessorBlock
+ }
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll
index a2ce0662dc24..d7cf89ca9aaa 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll
@@ -28,11 +28,15 @@ class Operand extends TStageOperand {
cached
Operand() {
// Ensure that the operand does not refer to instructions from earlier stages that are unreachable here
- exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or
- exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or
+ exists(Instruction use, Instruction def | this = registerOperand(use, _, def))
+ or
+ exists(Instruction use | this = nonSSAMemoryOperand(use, _))
+ or
exists(Instruction use, Instruction def, IRBlock predecessorBlock |
- this = phiOperand(use, def, predecessorBlock, _)
- ) or
+ this = phiOperand(use, def, predecessorBlock, _) or
+ this = reusedPhiOperand(use, def, predecessorBlock, _)
+ )
+ or
exists(Instruction use | this = chiOperand(use, _))
}
@@ -431,7 +435,11 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
Overlap overlap;
cached
- PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
+ PhiInputOperand() {
+ this = phiOperand(useInstr, defInstr, predecessorBlock, overlap)
+ or
+ this = reusedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
+ }
override string toString() { result = "Phi" }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll
index 19fb0490f808..9997b5b49a78 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll
@@ -4,6 +4,71 @@ private import AliasAnalysisImports
private class IntValue = Ints::IntValue;
+/**
+ * If `instr` is a `SideEffectInstruction`, gets the primary `CallInstruction` that caused the side
+ * effect. If `instr` is a `CallInstruction`, gets that same `CallInstruction`.
+ */
+private CallInstruction getPrimaryCall(Instruction instr) {
+ result = instr
+ or
+ result = instr.(SideEffectInstruction).getPrimaryInstruction()
+}
+
+/**
+ * Holds if `operand` serves as an input argument (or indirection) to `call`, in the position
+ * specified by `input`.
+ */
+private predicate isCallInput(
+ CallInstruction call, Operand operand, AliasModels::FunctionInput input
+) {
+ call = getPrimaryCall(operand.getUse()) and
+ (
+ exists(int index |
+ input.isParameterOrQualifierAddress(index) and
+ operand = call.getArgumentOperand(index)
+ )
+ or
+ exists(int index, ReadSideEffectInstruction read |
+ input.isParameterDerefOrQualifierObject(index) and
+ read = call.getAParameterSideEffect(index) and
+ operand = read.getSideEffectOperand()
+ )
+ )
+}
+
+/**
+ * Holds if `instr` serves as a return value or output argument indirection for `call`, in the
+ * position specified by `output`.
+ */
+private predicate isCallOutput(
+ CallInstruction call, Instruction instr, AliasModels::FunctionOutput output
+) {
+ call = getPrimaryCall(instr) and
+ (
+ output.isReturnValue() and instr = call
+ or
+ exists(int index, WriteSideEffectInstruction write |
+ output.isParameterDerefOrQualifierObject(index) and
+ write = call.getAParameterSideEffect(index) and
+ instr = write
+ )
+ )
+}
+
+/**
+ * Holds if the address in `operand` flows directly to the result of `resultInstr` due to modeled
+ * address flow through a function call.
+ */
+private predicate hasAddressFlowThroughCall(Operand operand, Instruction resultInstr) {
+ exists(
+ CallInstruction call, AliasModels::FunctionInput input, AliasModels::FunctionOutput output
+ |
+ call.getStaticCallTarget().(AliasModels::AliasFunction).hasAddressFlow(input, output) and
+ isCallInput(call, operand, input) and
+ isCallOutput(call, resultInstr, output)
+ )
+}
+
/**
* Holds if the operand `tag` of instruction `instr` is used in a way that does
* not result in any address held in that operand from escaping beyond the
@@ -25,6 +90,9 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) {
or
// Converting an address to a `bool` does not escape the address.
instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType
+ or
+ instr instanceof CallInstruction and
+ not exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis())
)
)
or
@@ -34,7 +102,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) {
private predicate operandEscapesDomain(Operand operand) {
not operandIsConsumedWithoutEscaping(operand) and
- not operandIsPropagated(operand, _) and
+ not operandIsPropagated(operand, _, _) and
not isArgumentForParameter(_, operand, _) and
not isOnlyEscapesViaReturnArgument(operand) and
not operand.getUse() instanceof ReturnValueInstruction and
@@ -69,67 +137,67 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) {
}
/**
- * Holds if any address held in operand `tag` of instruction `instr` is
- * propagated to the result of `instr`, offset by the number of bits in
- * `bitOffset`. If the address is propagated, but the offset is not known to be
- * a constant, then `bitOffset` is unknown.
+ * Holds if any address held in operand `operand` is propagated to the result of `instr`, offset by
+ * the number of bits in `bitOffset`. If the address is propagated, but the offset is not known to
+ * be a constant, then `bitOffset` is `unknown()`.
*/
-private predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
- exists(Instruction instr |
- instr = operand.getUse() and
- (
- // Converting to a non-virtual base class adds the offset of the base class.
- exists(ConvertToNonVirtualBaseInstruction convert |
- convert = instr and
- bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
- )
- or
- // Conversion using dynamic_cast results in an unknown offset
- instr instanceof CheckedConvertOrNullInstruction and
- bitOffset = Ints::unknown()
- or
- // Converting to a derived class subtracts the offset of the base class.
- exists(ConvertToDerivedInstruction convert |
- convert = instr and
- bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8))
- )
- or
- // Converting to a virtual base class adds an unknown offset.
- instr instanceof ConvertToVirtualBaseInstruction and
- bitOffset = Ints::unknown()
- or
- // Conversion to another pointer type propagates the source address.
- exists(ConvertInstruction convert, IRType resultType |
- convert = instr and
- resultType = convert.getResultIRType() and
- resultType instanceof IRAddressType and
- bitOffset = 0
- )
- or
- // Adding an integer to or subtracting an integer from a pointer propagates
- // the address with an offset.
- exists(PointerOffsetInstruction ptrOffset |
- ptrOffset = instr and
- operand = ptrOffset.getLeftOperand() and
- bitOffset = getPointerBitOffset(ptrOffset)
- )
- or
- // Computing a field address from a pointer propagates the address plus the
- // offset of the field.
- bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField())
- or
- // A copy propagates the source value.
- operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0
- or
- // Some functions are known to propagate an argument
- isAlwaysReturnedArgument(operand) and bitOffset = 0
+private predicate operandIsPropagated(Operand operand, IntValue bitOffset, Instruction instr) {
+ // Some functions are known to propagate an argument
+ hasAddressFlowThroughCall(operand, instr) and
+ bitOffset = 0
+ or
+ instr = operand.getUse() and
+ (
+ // Converting to a non-virtual base class adds the offset of the base class.
+ exists(ConvertToNonVirtualBaseInstruction convert |
+ convert = instr and
+ bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
+ )
+ or
+ // Conversion using dynamic_cast results in an unknown offset
+ instr instanceof CheckedConvertOrNullInstruction and
+ bitOffset = Ints::unknown()
+ or
+ // Converting to a derived class subtracts the offset of the base class.
+ exists(ConvertToDerivedInstruction convert |
+ convert = instr and
+ bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8))
+ )
+ or
+ // Converting to a virtual base class adds an unknown offset.
+ instr instanceof ConvertToVirtualBaseInstruction and
+ bitOffset = Ints::unknown()
+ or
+ // Conversion to another pointer type propagates the source address.
+ exists(ConvertInstruction convert, IRType resultType |
+ convert = instr and
+ resultType = convert.getResultIRType() and
+ resultType instanceof IRAddressType and
+ bitOffset = 0
)
+ or
+ // Adding an integer to or subtracting an integer from a pointer propagates
+ // the address with an offset.
+ exists(PointerOffsetInstruction ptrOffset |
+ ptrOffset = instr and
+ operand = ptrOffset.getLeftOperand() and
+ bitOffset = getPointerBitOffset(ptrOffset)
+ )
+ or
+ // Computing a field address from a pointer propagates the address plus the
+ // offset of the field.
+ bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField())
+ or
+ // A copy propagates the source value.
+ operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0
)
}
private predicate operandEscapesNonReturn(Operand operand) {
- // The address is propagated to the result of the instruction, and that result itself is returned
- operandIsPropagated(operand, _) and resultEscapesNonReturn(operand.getUse())
+ exists(Instruction instr |
+ // The address is propagated to the result of the instruction, and that result itself is returned
+ operandIsPropagated(operand, _, instr) and resultEscapesNonReturn(instr)
+ )
or
// The operand is used in a function call which returns it, and the return value is then returned
exists(CallInstruction ci, Instruction init |
@@ -151,9 +219,11 @@ private predicate operandEscapesNonReturn(Operand operand) {
}
private predicate operandMayReachReturn(Operand operand) {
- // The address is propagated to the result of the instruction, and that result itself is returned
- operandIsPropagated(operand, _) and
- resultMayReachReturn(operand.getUse())
+ exists(Instruction instr |
+ // The address is propagated to the result of the instruction, and that result itself is returned
+ operandIsPropagated(operand, _, instr) and
+ resultMayReachReturn(instr)
+ )
or
// The operand is used in a function call which returns it, and the return value is then returned
exists(CallInstruction ci, Instruction init |
@@ -173,9 +243,9 @@ private predicate operandMayReachReturn(Operand operand) {
private predicate operandReturned(Operand operand, IntValue bitOffset) {
// The address is propagated to the result of the instruction, and that result itself is returned
- exists(IntValue bitOffset1, IntValue bitOffset2 |
- operandIsPropagated(operand, bitOffset1) and
- resultReturned(operand.getUse(), bitOffset2) and
+ exists(Instruction instr, IntValue bitOffset1, IntValue bitOffset2 |
+ operandIsPropagated(operand, bitOffset1, instr) and
+ resultReturned(instr, bitOffset2) and
bitOffset = Ints::add(bitOffset1, bitOffset2)
)
or
@@ -214,24 +284,27 @@ private predicate isArgumentForParameter(
)
}
-private predicate isAlwaysReturnedArgument(Operand operand) {
- exists(AliasModels::AliasFunction f |
- f = operand.getUse().(CallInstruction).getStaticCallTarget() and
- f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex())
- )
-}
-
private predicate isOnlyEscapesViaReturnArgument(Operand operand) {
exists(AliasModels::AliasFunction f |
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
- f.parameterEscapesOnlyViaReturn(operand.(PositionalArgumentOperand).getIndex())
+ (
+ f.parameterEscapesOnlyViaReturn(operand.(PositionalArgumentOperand).getIndex())
+ or
+ f.parameterEscapesOnlyViaReturn(-1) and
+ operand instanceof ThisArgumentOperand
+ )
)
}
private predicate isNeverEscapesArgument(Operand operand) {
exists(AliasModels::AliasFunction f |
f = operand.getUse().(CallInstruction).getStaticCallTarget() and
- f.parameterNeverEscapes(operand.(PositionalArgumentOperand).getIndex())
+ (
+ f.parameterNeverEscapes(operand.(PositionalArgumentOperand).getIndex())
+ or
+ f.parameterNeverEscapes(-1) and
+ operand instanceof ThisArgumentOperand
+ )
)
}
@@ -265,17 +338,23 @@ predicate allocationEscapes(Configuration::Allocation allocation) {
exists(IREscapeAnalysisConfiguration config |
config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction())
)
+ or
+ Configuration::phaseNeedsSoundEscapeAnalysis() and
+ resultEscapesNonReturn(allocation.getABaseInstruction())
}
/**
* Equivalent to `operandIsPropagated()`, but includes interprocedural propagation.
*/
-private predicate operandIsPropagatedIncludingByCall(Operand operand, IntValue bitOffset) {
- operandIsPropagated(operand, bitOffset)
+private predicate operandIsPropagatedIncludingByCall(
+ Operand operand, IntValue bitOffset, Instruction instr
+) {
+ operandIsPropagated(operand, bitOffset, instr)
or
exists(CallInstruction call, Instruction init |
isArgumentForParameter(call, operand, init) and
- resultReturned(init, bitOffset)
+ resultReturned(init, bitOffset) and
+ instr = call
)
}
@@ -292,8 +371,7 @@ private predicate hasBaseAndOffset(AddressOperand addrOperand, Instruction base,
// We already have an offset from `middle`.
hasBaseAndOffset(addrOperand, middle, previousBitOffset) and
// `middle` is propagated from `base`.
- middleOperand = middle.getAnOperand() and
- operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset) and
+ operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset, middle) and
base = middleOperand.getDef() and
bitOffset = Ints::add(previousBitOffset, additionalBitOffset)
)
@@ -333,8 +411,51 @@ predicate addressOperandAllocationAndOffset(
allocation.getABaseInstruction() = base and
hasBaseAndOffset(addrOperand, base, bitOffset) and
not exists(Instruction previousBase |
- hasBaseAndOffset(addrOperand, previousBase, _) and
+ hasBaseAndOffset(addrOperand, pragma[only_bind_out](previousBase), _) and
previousBase = base.getAnOperand().getDef()
)
)
}
+
+/**
+ * Predicates used only for printing annotated IR dumps. These should not be used in production
+ * queries.
+ */
+module Print {
+ string getOperandProperty(Operand operand, string key) {
+ key = "alloc" and
+ result =
+ strictconcat(Configuration::Allocation allocation, IntValue bitOffset |
+ addressOperandAllocationAndOffset(operand, allocation, bitOffset)
+ |
+ allocation.toString() + Ints::getBitOffsetString(bitOffset), ", "
+ )
+ or
+ key = "prop" and
+ result =
+ strictconcat(Instruction destInstr, IntValue bitOffset, string value |
+ operandIsPropagatedIncludingByCall(operand, bitOffset, destInstr) and
+ if destInstr = operand.getUse()
+ then value = "@" + Ints::getBitOffsetString(bitOffset) + "->result"
+ else value = "@" + Ints::getBitOffsetString(bitOffset) + "->" + destInstr.getResultId()
+ |
+ value, ", "
+ )
+ }
+
+ string getInstructionProperty(Instruction instr, string key) {
+ key = "prop" and
+ result =
+ strictconcat(IntValue bitOffset, Operand sourceOperand, string value |
+ operandIsPropagatedIncludingByCall(sourceOperand, bitOffset, instr) and
+ if instr = sourceOperand.getUse()
+ then value = sourceOperand.getDumpId() + Ints::getBitOffsetString(bitOffset) + "->@"
+ else
+ value =
+ sourceOperand.getUse().getResultId() + "." + sourceOperand.getDumpId() +
+ Ints::getBitOffsetString(bitOffset) + "->@"
+ |
+ value, ", "
+ )
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll
index 5be476e12eed..dbdd3c14c856 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll
@@ -14,3 +14,5 @@ class Allocation extends IRAutomaticVariable {
none()
}
}
+
+predicate phaseNeedsSoundEscapeAnalysis() { any() }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintAliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintAliasAnalysis.qll
new file mode 100644
index 000000000000..262088245e8b
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintAliasAnalysis.qll
@@ -0,0 +1,19 @@
+/**
+ * Include this module to annotate IR dumps with information computed by `AliasAnalysis.qll`.
+ */
+
+private import AliasAnalysisInternal
+private import InputIR
+private import AliasAnalysisImports
+private import AliasAnalysis
+private import semmle.code.cpp.ir.internal.IntegerConstant
+
+private class AliasPropertyProvider extends IRPropertyProvider {
+ override string getOperandProperty(Operand operand, string key) {
+ result = Print::getOperandProperty(operand, key)
+ }
+
+ override string getInstructionProperty(Instruction instr, string key) {
+ result = Print::getInstructionProperty(instr, key)
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll
index 340f524fce8e..5092e921cb33 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll
@@ -43,24 +43,81 @@ private module Cached {
class TStageInstruction =
TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction;
+ /**
+ * If `oldInstruction` is a `Phi` instruction that has exactly one reachable predecessor block,
+ * this predicate returns the `PhiInputOperand` corresponding to that predecessor block.
+ * Otherwise, this predicate does not hold.
+ */
+ private OldIR::PhiInputOperand getDegeneratePhiOperand(OldInstruction oldInstruction) {
+ result =
+ unique(OldIR::PhiInputOperand operand |
+ operand = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and
+ operand.getPredecessorBlock() instanceof OldBlock
+ )
+ }
+
cached
predicate hasInstruction(TStageInstruction instr) {
instr instanceof TRawInstruction and instr instanceof OldInstruction
or
- instr instanceof TPhiInstruction
+ instr = phiInstruction(_, _)
+ or
+ instr = reusedPhiInstruction(_) and
+ // Check that the phi instruction is *not* degenerate, but we can't use
+ // getDegeneratePhiOperand in the first stage with phi instyructions
+ not exists(
+ unique(OldIR::PhiInputOperand operand |
+ operand = instr.(OldIR::PhiInstruction).getAnInputOperand() and
+ operand.getPredecessorBlock() instanceof OldBlock
+ )
+ )
or
instr instanceof TChiInstruction
or
instr instanceof TUnreachedInstruction
}
- private IRBlock getNewBlock(OldBlock oldBlock) {
- result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
+ cached
+ IRBlock getNewBlock(OldBlock oldBlock) {
+ exists(Instruction newEnd, OldIR::Instruction oldEnd |
+ (
+ result.getLastInstruction() = newEnd and
+ not newEnd instanceof ChiInstruction
+ or
+ newEnd = result.getLastInstruction().(ChiInstruction).getAPredecessor() // does this work?
+ ) and
+ (
+ oldBlock.getLastInstruction() = oldEnd and
+ not oldEnd instanceof OldIR::ChiInstruction
+ or
+ oldEnd = oldBlock.getLastInstruction().(OldIR::ChiInstruction).getAPredecessor() // does this work?
+ ) and
+ oldEnd = getNewInstruction(newEnd)
+ )
+ }
+
+ /**
+ * Gets the block from the old IR that corresponds to `newBlock`.
+ */
+ private OldBlock getOldBlock(IRBlock newBlock) { getNewBlock(result) = newBlock }
+
+ /**
+ * Holds if this iteration of SSA can model the def/use information for the result of
+ * `oldInstruction`, either because alias analysis has determined a memory location for that
+ * result, or because a previous iteration of the IR already computed that def/use information
+ * completely.
+ */
+ private predicate canModelResultForOldInstruction(OldInstruction oldInstruction) {
+ // We're modeling the result's memory location ourselves.
+ exists(Alias::getResultMemoryLocation(oldInstruction))
+ or
+ // This result was already modeled by a previous iteration of SSA.
+ Alias::canReuseSSAForOldResult(oldInstruction)
}
cached
predicate hasModeledMemoryResult(Instruction instruction) {
- exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or
+ canModelResultForOldInstruction(getOldInstruction(instruction)) or
instruction instanceof PhiInstruction or // Phis always have modeled results
instruction instanceof ChiInstruction // Chis always have modeled results
}
@@ -117,6 +174,32 @@ private module Cached {
)
}
+ /**
+ * Gets the new definition instruction for `oldOperand` based on `oldOperand`'s definition in the
+ * old IR. Usually, this will just get the old definition of `oldOperand` and map it to the
+ * corresponding new instruction. However, if the old definition of `oldOperand` is a `Phi`
+ * instruction that is now degenerate due all but one of its predecessor branches being
+ * unreachable, this predicate will recurse through any degenerate `Phi` instructions to find the
+ * true definition.
+ */
+ private Instruction getNewDefinitionFromOldSSA(OldIR::MemoryOperand oldOperand, Overlap overlap) {
+ exists(Overlap originalOverlap |
+ originalOverlap = oldOperand.getDefinitionOverlap() and
+ (
+ result = getNewInstruction(oldOperand.getAnyDef()) and
+ overlap = originalOverlap
+ or
+ exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap |
+ phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and
+ result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and
+ overlap =
+ combineOverlap(pragma[only_bind_out](phiOperandOverlap),
+ pragma[only_bind_out](originalOverlap))
+ )
+ )
+ )
+ }
+
cached
private Instruction getMemoryOperandDefinition0(
Instruction instruction, MemoryOperandTag tag, Overlap overlap
@@ -148,6 +231,12 @@ private module Cached {
overlap instanceof MustExactlyOverlap and
exists(MustTotallyOverlap o | exists(getMemoryOperandDefinition0(instruction, tag, o)))
)
+ or
+ exists(OldIR::NonPhiMemoryOperand oldOperand |
+ result = getNewDefinitionFromOldSSA(oldOperand, overlap) and
+ oldOperand.getUse() = instruction and
+ tag = oldOperand.getOperandTag()
+ )
}
/**
@@ -214,10 +303,24 @@ private module Cached {
)
}
+ /**
+ * Gets the new definition instruction for the operand of `instr` that flows from the block
+ * `newPredecessorBlock`, based on that operand's definition in the old IR.
+ */
+ private Instruction getNewPhiOperandDefinitionFromOldSSA(
+ Instruction instr, IRBlock newPredecessorBlock, Overlap overlap
+ ) {
+ exists(OldIR::PhiInstruction oldPhi, OldIR::PhiInputOperand oldOperand |
+ oldPhi = getOldInstruction(instr) and
+ oldOperand = oldPhi.getInputOperand(getOldBlock(newPredecessorBlock)) and
+ result = getNewDefinitionFromOldSSA(oldOperand, overlap)
+ )
+ }
+
pragma[noopt]
cached
Instruction getPhiOperandDefinition(
- PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap
+ Instruction instr, IRBlock newPredecessorBlock, Overlap overlap
) {
exists(
Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock,
@@ -229,6 +332,8 @@ private module Cached {
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
overlap = Alias::getOverlap(actualDefLocation, useLocation)
)
+ or
+ result = getNewPhiOperandDefinitionFromOldSSA(instr, newPredecessorBlock, overlap)
}
cached
@@ -249,7 +354,12 @@ private module Cached {
cached
Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
exists(OldBlock oldBlock |
- instr = getPhi(oldBlock, _) and
+ (
+ instr = getPhi(oldBlock, _)
+ or
+ // Any `Phi` that we propagated from the previous iteration stays in the same block.
+ getOldInstruction(instr).getBlock() = oldBlock
+ ) and
result = getNewInstruction(oldBlock.getFirstInstruction())
)
}
@@ -335,18 +445,27 @@ private module Cached {
result = vvar.getType()
)
or
+ instr = reusedPhiInstruction(_) and
+ result = instr.(OldInstruction).getResultLanguageType()
+ or
instr = unreachedInstruction(_) and result = Language::getVoidType()
}
+ /**
+ * Holds if `opcode` is the opcode that specifies the operation performed by `instr`.
+ *
+ * The parameters are ordered such that they produce a clean join (with no need for reordering)
+ * in the characteristic predicates of the `Instruction` subclasses.
+ */
cached
- Opcode getInstructionOpcode(Instruction instr) {
- result = getOldInstruction(instr).getOpcode()
+ predicate getInstructionOpcode(Opcode opcode, Instruction instr) {
+ opcode = getOldInstruction(instr).getOpcode()
or
- instr = phiInstruction(_, _) and result instanceof Opcode::Phi
+ instr = phiInstruction(_, _) and opcode instanceof Opcode::Phi
or
- instr = chiInstruction(_) and result instanceof Opcode::Chi
+ instr = chiInstruction(_) and opcode instanceof Opcode::Chi
or
- instr = unreachedInstruction(_) and result instanceof Opcode::Unreached
+ instr = unreachedInstruction(_) and opcode instanceof Opcode::Unreached
}
cached
@@ -856,6 +975,26 @@ module DefUse {
}
}
+predicate canReuseSSAForMemoryResult(Instruction instruction) {
+ exists(OldInstruction oldInstruction |
+ oldInstruction = getOldInstruction(instruction) and
+ (
+ // The previous iteration said it was reusable, so we should mark it as reusable as well.
+ Alias::canReuseSSAForOldResult(oldInstruction)
+ or
+ // The current alias analysis says it is reusable.
+ Alias::getResultMemoryLocation(oldInstruction).canReuseSSA()
+ )
+ )
+ or
+ exists(Alias::MemoryLocation defLocation |
+ // This is a `Phi` for a reusable location, so the result of the `Phi` is reusable as well.
+ instruction = phiInstruction(_, defLocation) and
+ defLocation.canReuseSSA()
+ )
+ // We don't support reusing SSA for any location that could create a `Chi` instruction.
+}
+
/**
* Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the
* `DebugSSA` module, which is then imported by PrintSSA.
@@ -934,7 +1073,10 @@ module SSAConsistency {
locationCount > 1 and
func = operand.getEnclosingIRFunction() and
funcText = Language::getIdentityString(func.getFunction()) and
- message = "Operand has " + locationCount.toString() + " memory accesses in function '$@'."
+ message =
+ operand.getUse().toString() + " " + "Operand has " + locationCount.toString() +
+ " memory accesses in function '$@': " +
+ strictconcat(Alias::getOperandMemoryLocation(operand).toString(), ", ")
)
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll
index a7b9160bdc75..f3e02c9f6a83 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll
@@ -17,7 +17,7 @@ private predicate isTotalAccess(Allocation var, AddressOperand addrOperand, IRTy
* variable if its address never escapes and all reads and writes of that variable access the entire
* variable using the original type of the variable.
*/
-private predicate isVariableModeled(Allocation var) {
+predicate isVariableModeled(Allocation var) {
not allocationEscapes(var) and
forall(Instruction instr, AddressOperand addrOperand, IRType type |
addrOperand = instr.getResultAddressOperand() and
@@ -35,6 +35,17 @@ private predicate isVariableModeled(Allocation var) {
)
}
+/**
+ * Holds if the SSA use/def chain for the specified variable can be safely reused by later
+ * iterations of SSA construction. This will hold only if we modeled the variable soundly, so that
+ * subsequent iterations will recompute SSA for any variable that we assumed did not escape, but
+ * actually would have escaped if we had used a sound escape analysis.
+ */
+predicate canReuseSSAForVariable(IRAutomaticVariable var) {
+ isVariableModeled(var) and
+ not allocationEscapes(var)
+}
+
private newtype TMemoryLocation = MkMemoryLocation(Allocation var) { isVariableModeled(var) }
private MemoryLocation getMemoryLocation(Allocation var) { result.getAllocation() = var }
@@ -57,8 +68,12 @@ class MemoryLocation extends TMemoryLocation {
final Language::LanguageType getType() { result = var.getLanguageType() }
final string getUniqueId() { result = var.getUniqueId() }
+
+ final predicate canReuseSSA() { canReuseSSAForVariable(var) }
}
+predicate canReuseSSAForOldResult(Instruction instr) { none() }
+
/**
* Represents a set of `MemoryLocation`s that cannot overlap with
* `MemoryLocation`s outside of the set. The `VirtualVariable` will be
diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll
index f9a0c574f8c3..ca643b56cbb0 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll
@@ -8,6 +8,16 @@ private newtype TOverlap =
*/
abstract class Overlap extends TOverlap {
abstract string toString();
+
+ /**
+ * Gets a value representing how precise this overlap is. The higher the value, the more precise
+ * the overlap. The precision values are ordered as
+ * follows, from most to least precise:
+ * `MustExactlyOverlap`
+ * `MustTotallyOverlap`
+ * `MayPartiallyOverlap`
+ */
+ abstract int getPrecision();
}
/**
@@ -16,6 +26,8 @@ abstract class Overlap extends TOverlap {
*/
class MayPartiallyOverlap extends Overlap, TMayPartiallyOverlap {
final override string toString() { result = "MayPartiallyOverlap" }
+
+ final override int getPrecision() { result = 0 }
}
/**
@@ -24,6 +36,8 @@ class MayPartiallyOverlap extends Overlap, TMayPartiallyOverlap {
*/
class MustTotallyOverlap extends Overlap, TMustTotallyOverlap {
final override string toString() { result = "MustTotallyOverlap" }
+
+ final override int getPrecision() { result = 1 }
}
/**
@@ -32,4 +46,25 @@ class MustTotallyOverlap extends Overlap, TMustTotallyOverlap {
*/
class MustExactlyOverlap extends Overlap, TMustExactlyOverlap {
final override string toString() { result = "MustExactlyOverlap" }
+
+ final override int getPrecision() { result = 2 }
+}
+
+/**
+ * Gets the `Overlap` that best represents the relationship between two memory locations `a` and
+ * `c`, where `getOverlap(a, b) = previousOverlap` and `getOverlap(b, c) = newOverlap`, for some
+ * intermediate memory location `b`.
+ */
+Overlap combineOverlap(Overlap previousOverlap, Overlap newOverlap) {
+ // Note that it's possible that two less precise overlaps could combine to result in a more
+ // precise overlap. For example, both `previousOverlap` and `newOverlap` could be
+ // `MustTotallyOverlap` even though the actual relationship between `a` and `c` is
+ // `MustExactlyOverlap`. We will still return `MustTotallyOverlap` as the best conservative
+ // approximation we can make without additional input information.
+ result =
+ min(Overlap overlap |
+ overlap = [previousOverlap, newOverlap]
+ |
+ overlap order by overlap.getPrecision()
+ )
}
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll
index 4b060c234f89..e249a1640614 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll
@@ -1,12 +1,14 @@
+import semmle.code.cpp.models.interfaces.Alias
+import semmle.code.cpp.models.interfaces.SideEffect
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.DataFlow
import semmle.code.cpp.models.interfaces.PointerWrapper
/**
- * The `std::shared_ptr` and `std::unique_ptr` template classes.
+ * The `std::shared_ptr`, `std::weak_ptr`, and `std::unique_ptr` template classes.
*/
-private class UniqueOrSharedPtr extends Class, PointerWrapper {
- UniqueOrSharedPtr() { this.hasQualifiedName(["std", "bsl"], ["shared_ptr", "unique_ptr"]) }
+private class SmartPtr extends Class, PointerWrapper {
+ SmartPtr() { this.hasQualifiedName(["std", "bsl"], ["shared_ptr", "weak_ptr", "unique_ptr"]) }
override MemberFunction getAnUnwrapperFunction() {
result.(OverloadedPointerDereferenceFunction).getDeclaringType() = this
@@ -17,21 +19,46 @@ private class UniqueOrSharedPtr extends Class, PointerWrapper {
override predicate pointsToConst() { this.getTemplateArgument(0).(Type).isConst() }
}
-/** Any function that unwraps a pointer wrapper class to reveal the underlying pointer. */
-private class PointerWrapperDataFlow extends DataFlowFunction {
- PointerWrapperDataFlow() {
- this = any(PointerWrapper wrapper).getAnUnwrapperFunction() and
- not this.getUnspecifiedType() instanceof ReferenceType
+/**
+ * Any function that returns the address wrapped by a `PointerWrapper`, whether as a pointer or a
+ * reference.
+ *
+ * Examples:
+ * - `std::unique_ptr::get()`
+ * - `std::shared_ptr::operator->()`
+ * - `std::weak_ptr::operator*()`
+ */
+private class PointerUnwrapperFunction extends MemberFunction, TaintFunction, DataFlowFunction,
+ SideEffectFunction, AliasFunction {
+ PointerUnwrapperFunction() {
+ exists(PointerWrapper wrapper | wrapper.getAnUnwrapperFunction() = this)
}
- override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
- input.isQualifierAddress() and output.isReturnValue()
- or
- input.isQualifierObject() and output.isReturnValueDeref()
- or
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isReturnValueDeref() and
output.isQualifierObject()
}
+
+ override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
+ input.isQualifierObject() and output.isReturnValue()
+ }
+
+ override predicate hasOnlySpecificReadSideEffects() { any() }
+
+ override predicate hasOnlySpecificWriteSideEffects() { any() }
+
+ override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
+ // Only reads from `*this`.
+ i = -1 and buffer = false
+ }
+
+ override predicate parameterNeverEscapes(int index) { index = -1 }
+
+ override predicate parameterEscapesOnlyViaReturn(int index) { none() }
+
+ override predicate hasAddressFlow(FunctionInput input, FunctionOutput output) {
+ input.isQualifierObject() and output.isReturnValue()
+ }
}
/**
@@ -62,31 +89,79 @@ private class MakeUniqueOrShared extends TaintFunction {
}
/**
- * A prefix `operator*` member function for a `shared_ptr` or `unique_ptr` type.
+ * A function that sets the value of a smart pointer.
+ *
+ * This could be a constructor, an assignment operator, or a named member function like `reset()`.
*/
-private class UniqueOrSharedDereferenceMemberOperator extends MemberFunction, TaintFunction {
- UniqueOrSharedDereferenceMemberOperator() {
- this.hasName("operator*") and
- this.getDeclaringType() instanceof UniqueOrSharedPtr
+private class SmartPtrSetterFunction extends MemberFunction, AliasFunction, SideEffectFunction {
+ SmartPtrSetterFunction() {
+ this.getDeclaringType() instanceof SmartPtr and
+ not this.isStatic() and
+ (
+ this instanceof Constructor
+ or
+ this.hasName("operator=")
+ or
+ this.hasName("reset")
+ )
}
- override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
- input.isQualifierObject() and
- output.isReturnValueDeref()
+ override predicate hasOnlySpecificReadSideEffects() { none() }
+
+ override predicate hasOnlySpecificWriteSideEffects() { none() }
+
+ override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
+ // Always write to the destination smart pointer itself.
+ i = -1 and buffer = false and mustWrite = true
+ or
+ // When taking ownership of a smart pointer via an rvalue reference, always overwrite the input
+ // smart pointer.
+ getPointerInput().isParameterDeref(i) and
+ this.getParameter(i).getUnspecifiedType() instanceof RValueReferenceType and
+ buffer = false and
+ mustWrite = true
}
-}
-/**
- * The `std::shared_ptr` or `std::unique_ptr` function `get`.
- */
-private class UniqueOrSharedGet extends TaintFunction {
- UniqueOrSharedGet() {
- this.hasName("get") and
- this.getDeclaringType() instanceof UniqueOrSharedPtr
+ override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
+ getPointerInput().isParameterDeref(i) and
+ buffer = false
+ or
+ not this instanceof Constructor and
+ i = -1 and
+ buffer = false
}
- override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
- input.isQualifierObject() and
+ override predicate parameterNeverEscapes(int index) { index = -1 }
+
+ override predicate parameterEscapesOnlyViaReturn(int index) { none() }
+
+ override predicate hasAddressFlow(FunctionInput input, FunctionOutput output) {
+ input = getPointerInput() and
+ output.isQualifierObject()
+ or
+ // Assignment operator always returns a reference to `*this`.
+ this.hasName("operator=") and
+ input.isQualifierAddress() and
output.isReturnValue()
}
+
+ private FunctionInput getPointerInput() {
+ exists(Parameter param0 | param0 = this.getParameter(0) |
+ (
+ param0.getUnspecifiedType().(ReferenceType).getBaseType() instanceof SmartPtr and
+ if this.getParameter(1).getUnspecifiedType() instanceof PointerType
+ then
+ // This is one of the constructors of `std::shared_ptr` that creates a smart pointer that
+ // wraps a raw pointer with ownership controlled by an unrelated smart pointer. We propagate
+ // the raw pointer in the second parameter, rather than the smart pointer in the first
+ // parameter.
+ result.isParameter(1)
+ else result.isParameterDeref(0)
+ or
+ // One of the functions that takes ownership of a raw pointer.
+ param0.getUnspecifiedType() instanceof PointerType and
+ result.isParameter(0)
+ )
+ )
+ }
}
diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Alias.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Alias.qll
index 083bbf55c388..e947a93fc908 100644
--- a/cpp/ql/src/semmle/code/cpp/models/interfaces/Alias.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Alias.qll
@@ -50,5 +50,16 @@ abstract class AliasFunction extends Function {
/**
* Holds if the function always returns the value of the parameter at the specified index.
*/
- abstract predicate parameterIsAlwaysReturned(int index);
+ predicate parameterIsAlwaysReturned(int index) { none() }
+
+ /**
+ * Holds if the address passed in via `input` is always propagated to `output`.
+ */
+ predicate hasAddressFlow(FunctionInput input, FunctionOutput output) {
+ exists(int index |
+ // By default, just use the old `parameterIsAlwaysReturned` predicate to detect flow from the
+ // parameter to the return value.
+ input.isParameter(index) and output.isReturnValue() and this.parameterIsAlwaysReturned(index)
+ )
+ }
}
diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll
index f3bbbddd97e4..79dbe49611e0 100644
--- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll
+++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll
@@ -1617,6 +1617,20 @@ private module SimpleRangeAnalysisCached {
defMightOverflowPositively(def, v)
}
+ /**
+ * Holds if `e` is an expression where the concept of overflow makes sense.
+ * This predicate is used to filter out some of the unanalyzable expressions
+ * from `exprMightOverflowPositively` and `exprMightOverflowNegatively`.
+ */
+ pragma[inline]
+ private predicate exprThatCanOverflow(Expr e) {
+ e instanceof UnaryArithmeticOperation or
+ e instanceof BinaryArithmeticOperation or
+ e instanceof AssignArithmeticOperation or
+ e instanceof LShiftExpr or
+ e instanceof AssignLShiftExpr
+ }
+
/**
* Holds if the expression might overflow negatively. This predicate
* does not consider the possibility that the expression might overflow
@@ -1630,6 +1644,11 @@ private module SimpleRangeAnalysisCached {
// bound of `x`, so the standard logic (above) does not work for
// detecting whether it might overflow.
getLowerBoundsImpl(expr.(PostfixDecrExpr)) = exprMinVal(expr)
+ or
+ // We can't conclude that any unanalyzable expression might overflow. This
+ // is because there are many expressions that the range analysis doesn't
+ // handle, but where the concept of overflow doesn't make sense.
+ exprThatCanOverflow(expr) and not analyzableExpr(expr)
}
/**
@@ -1657,6 +1676,11 @@ private module SimpleRangeAnalysisCached {
// bound of `x`, so the standard logic (above) does not work for
// detecting whether it might overflow.
getUpperBoundsImpl(expr.(PostfixIncrExpr)) = exprMaxVal(expr)
+ or
+ // We can't conclude that any unanalyzable expression might overflow. This
+ // is because there are many expressions that the range analysis doesn't
+ // handle, but where the concept of overflow doesn't make sense.
+ exprThatCanOverflow(expr) and not analyzableExpr(expr)
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll
index 606242e833c8..55ef606483c4 100644
--- a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll
@@ -10,10 +10,18 @@ import cpp
string getAnInsecureAlgorithmName() {
result =
[
- "DES", "RC2", "RC4", "RC5", "ARCFOUR" // ARCFOUR is a variant of RC4
+ "DES", "RC2", "RC4", "RC5", "ARCFOUR", // ARCFOUR is a variant of RC4
+ "3DES", "DES3" // also appears separated, e.g. "TRIPLE-DES", which will be matched as "DES".
]
}
+/**
+ * Gets the name of an algorithm that is known to be secure.
+ */
+string getASecureAlgorithmName() {
+ result = ["RSA", "SHA256", "CCM", "GCM", "AES", "Blowfish", "ECIES"]
+}
+
/**
* Gets the name of a hash algorithm that is insecure if it is being used for
* encryption (but it is hard to know when that is happening).
@@ -23,25 +31,40 @@ string getAnInsecureHashAlgorithmName() { result = ["SHA1", "MD5"] }
/**
* Gets the regular expression used for matching strings that look like they
* contain an algorithm that is known to be insecure.
+ *
+ * Consider using `isInsecureEncryption` rather than accessing this regular
+ * expression directly.
*/
string getInsecureAlgorithmRegex() {
result =
// algorithms usually appear in names surrounded by characters that are not
- // alphabetical characters in the same case. This handles the upper and lower
- // case cases
- "(^|.*[^A-Z])(" + strictconcat(getAnInsecureAlgorithmName(), "|") + ")([^A-Z].*|$)" + "|" +
- // for lowercase, we want to be careful to avoid being confused by camelCase
- // hence we require two preceding uppercase letters to be sure of a case switch,
- // or a preceding non-alphabetic character
- "(^|.*[A-Z]{2}|.*[^a-zA-Z])(" + strictconcat(getAnInsecureAlgorithmName().toLowerCase(), "|") +
- ")([^a-z].*|$)"
+ // alphabetical characters in the same case or numerical digits. This
+ // handles the upper case:
+ "(^|.*[^A-Z0-9])(" + strictconcat(getAnInsecureAlgorithmName(), "|") + ")([^A-Z0-9].*|$)" + "|" +
+ // for lowercase, we want to be careful to avoid being confused by
+ //camelCase, hence we require two preceding uppercase letters to be
+ // sure of a case switch (or a preceding non-alphabetic, non-numeric
+ // character).
+ "(^|.*[A-Z]{2}|.*[^a-zA-Z0-9])(" +
+ strictconcat(getAnInsecureAlgorithmName().toLowerCase(), "|") + ")([^a-z0-9].*|$)"
}
/**
- * Gets the name of an algorithm that is known to be secure.
+ * Holds if `name` looks like it might be related to operations with an
+ * insecure encyption algorithm.
*/
-string getASecureAlgorithmName() {
- result = ["RSA", "SHA256", "CCM", "GCM", "AES", "Blowfish", "ECIES"]
+bindingset[name]
+predicate isInsecureEncryption(string name) { name.regexpMatch(getInsecureAlgorithmRegex()) }
+
+/**
+ * Holds if there is additional evidence that `name` looks like it might be
+ * related to operations with an encyption algorithm, besides the name of a
+ * specific algorithm. This can be used in conjuction with
+ * `isInsecureEncryption` to produce a stronger heuristic.
+ */
+bindingset[name]
+predicate isEncryptionAdditionalEvidence(string name) {
+ name.toUpperCase().matches("%" + ["CRYPT", "CODE", "CODING", "CBC", "KEY", "CIPHER", "MAC"] + "%")
}
/**
@@ -51,14 +74,15 @@ string getASecureAlgorithmName() {
string getSecureAlgorithmRegex() {
result =
// algorithms usually appear in names surrounded by characters that are not
- // alphabetical characters in the same case. This handles the upper and lower
- // case cases
- "(^|.*[^A-Z])(" + strictconcat(getASecureAlgorithmName(), "|") + ")([^A-Z].*|$)" + "|" +
- // for lowercase, we want to be careful to avoid being confused by camelCase
- // hence we require two preceding uppercase letters to be sure of a case
- // switch, or a preceding non-alphabetic character
- "(^|.*[A-Z]{2}|.*[^a-zA-Z])(" + strictconcat(getASecureAlgorithmName().toLowerCase(), "|") +
- ")([^a-z].*|$)"
+ // alphabetical characters in the same case or numerical digits. This
+ // handles the upper case:
+ "(^|.*[^A-Z0-9])(" + strictconcat(getASecureAlgorithmName(), "|") + ")([^A-Z0-9].*|$)" + "|" +
+ // for lowercase, we want to be careful to avoid being confused by
+ //camelCase, hence we require two preceding uppercase letters to be
+ // sure of a case switch (or a preceding non-alphabetic, non-numeric
+ // character).
+ "(^|.*[A-Z]{2}|.*[^a-zA-Z0-9])(" + strictconcat(getASecureAlgorithmName().toLowerCase(), "|") +
+ ")([^a-z0-9].*|$)"
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/security/Overflow.qll b/cpp/ql/src/semmle/code/cpp/security/Overflow.qll
index e7ad1c559e63..b8ed406cb4ae 100644
--- a/cpp/ql/src/semmle/code/cpp/security/Overflow.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/Overflow.qll
@@ -5,12 +5,14 @@
import cpp
import semmle.code.cpp.controlflow.Dominance
+import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
+import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
/**
* Holds if the value of `use` is guarded using `abs`.
*/
predicate guardedAbs(Operation e, Expr use) {
- exists(FunctionCall fc | fc.getTarget().getName() = "abs" |
+ exists(FunctionCall fc | fc.getTarget().getName() = ["abs", "labs", "llabs", "imaxabs"] |
fc.getArgument(0).getAChild*() = use and
guardedLesser(e, fc)
)
@@ -94,9 +96,14 @@ predicate guardedGreater(Operation e, Expr use) {
VariableAccess varUse(LocalScopeVariable v) { result = v.getAnAccess() }
/**
- * Holds if `e` is not guarded against overflow by `use`.
+ * Holds if `e` potentially overflows and `use` is an operand of `e` that is not guarded.
*/
predicate missingGuardAgainstOverflow(Operation e, VariableAccess use) {
+ // Since `e` is guarenteed to be a `BinaryArithmeticOperation`, a `UnaryArithmeticOperation` or
+ // an `AssignArithmeticOperation` by the other constraints in this predicate, we know that
+ // `convertedExprMightOverflowPositively` will have a result even when `e` is not analyzable
+ // by `SimpleRangeAnalysis`.
+ convertedExprMightOverflowPositively(e) and
use = e.getAnOperand() and
exists(LocalScopeVariable v | use.getTarget() = v |
// overflow possible if large
@@ -115,9 +122,14 @@ predicate missingGuardAgainstOverflow(Operation e, VariableAccess use) {
}
/**
- * Holds if `e` is not guarded against underflow by `use`.
+ * Holds if `e` potentially underflows and `use` is an operand of `e` that is not guarded.
*/
predicate missingGuardAgainstUnderflow(Operation e, VariableAccess use) {
+ // Since `e` is guarenteed to be a `BinaryArithmeticOperation`, a `UnaryArithmeticOperation` or
+ // an `AssignArithmeticOperation` by the other constraints in this predicate, we know that
+ // `convertedExprMightOverflowNegatively` will have a result even when `e` is not analyzable
+ // by `SimpleRangeAnalysis`.
+ convertedExprMightOverflowNegatively(e) and
use = e.getAnOperand() and
exists(LocalScopeVariable v | use.getTarget() = v |
// underflow possible if use is left operand and small
diff --git a/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll b/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll
index 0d555a6d340f..ed1fb4fbb50d 100644
--- a/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll
+++ b/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll
@@ -1136,6 +1136,11 @@ private predicate inForUpdate(Expr forUpdate, Expr child) {
exists(Expr mid | inForUpdate(forUpdate, mid) and child.getParent() = mid)
}
+/** Gets the `rnk`'th `case` statement in `b`. */
+private int indexOfSwitchCaseRank(BlockStmt b, int rnk) {
+ result = rank[rnk](int i | b.getStmt(i) instanceof SwitchCase)
+}
+
/**
* A C/C++ 'switch case' statement.
*
@@ -1331,16 +1336,14 @@ class SwitchCase extends Stmt, @stmt_switch_case {
* `default:` has results `{ x = 3; }, `x = 4;` and `break;`.
*/
Stmt getAStmt() {
- exists(BlockStmt b, int i, int j |
+ exists(BlockStmt b, int rnk, int i |
b.getStmt(i) = this and
- b.getStmt(j) = result and
- i < j and
- not result instanceof SwitchCase and
- not exists(SwitchCase sc, int k |
- b.getStmt(k) = sc and
- i < k and
- j > k
- )
+ i = indexOfSwitchCaseRank(b, rnk)
+ |
+ pragma[only_bind_into](b).getStmt([i + 1 .. indexOfSwitchCaseRank(b, rnk + 1) - 1]) = result
+ or
+ not exists(indexOfSwitchCaseRank(b, rnk + 1)) and
+ b.getStmt([i + 1 .. b.getNumStmt() + 1]) = result
)
}
diff --git a/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll b/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll
index e086b8104787..d351bac89a82 100644
--- a/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll
+++ b/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll
@@ -181,14 +181,14 @@ private int getEndOfColumnPosition(int start, string content) {
min(string name, int cand |
exists(TNamedColumn(name)) and
cand = content.indexOf(name + ":") and
- cand > start
+ cand >= start
|
cand
)
or
not exists(string name |
exists(TNamedColumn(name)) and
- content.indexOf(name + ":") > start
+ content.indexOf(name + ":") >= start
) and
result = content.length()
}
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected
new file mode 100644
index 000000000000..244a28cf3320
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected
@@ -0,0 +1 @@
+| test.c:14:9:14:16 | intIndex | A variable with this name is used in the $@ condition. | test.c:11:3:16:3 | while (...) ... | loop |
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.qlref
new file mode 100644
index 000000000000..6da5822f7f02
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c
new file mode 100644
index 000000000000..47d89188e6b4
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c
@@ -0,0 +1,59 @@
+void workFunction_0(char *s) {
+ int intIndex = 10;
+ int intGuard;
+ char buf[80];
+ while(intIndex > 2) // GOOD
+ {
+ buf[intIndex] = 1;
+ intIndex--;
+ }
+ intIndex = 10;
+ while(intIndex > 2)
+ {
+ buf[intIndex] = 1;
+ int intIndex; // BAD
+ intIndex--;
+ }
+ intIndex = 10;
+ intGuard = 20;
+ while(intIndex < intGuard--) // GOOD
+ {
+ buf[intIndex] = 1;
+ int intIndex;
+ intIndex--;
+ }
+ intIndex = 10;
+ intGuard = 20;
+ while(intIndex < intGuard) // GOOD
+ {
+ buf[intIndex] = 1;
+ int intIndex;
+ intIndex++;
+ intGuard--;
+ }
+ intIndex = 10;
+ intGuard = 20;
+ while(intIndex < intGuard) // GOOD
+ {
+ buf[intIndex] = 1;
+ int intIndex;
+ intIndex--;
+ intGuard -= 4;
+ }
+ intIndex = 10;
+ while(intIndex > 2) // GOOD
+ {
+ buf[intIndex] = 1;
+ intIndex -= 2;
+ int intIndex;
+ intIndex--;
+ }
+ intIndex = 10;
+ while(intIndex > 2) // GOOD
+ {
+ buf[intIndex] = 1;
+ --intIndex;
+ int intIndex;
+ intIndex--;
+ }
+}
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.expected
new file mode 100644
index 000000000000..0ab66e5b26c0
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.expected
@@ -0,0 +1,4 @@
+| test.c:11:16:11:18 | buf | This pointer may have already been cleared in the line 10. |
+| test.c:18:8:18:10 | buf | This pointer may have already been cleared in the line 17. |
+| test.c:57:8:57:10 | buf | This pointer may have already been cleared in the line 55. |
+| test.c:78:8:78:10 | buf | This pointer may have already been cleared in the line 77. |
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.qlref
new file mode 100644
index 000000000000..242beb593f8a
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-415/DoubleFree.ql
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/test.c
new file mode 100644
index 000000000000..1c154c03094c
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/test.c
@@ -0,0 +1,96 @@
+typedef unsigned long size_t;
+void *malloc(size_t size);
+void free(void *ptr);
+#define NULL 0
+
+void workFunction_0(char *s) {
+ int intSize = 10;
+ char *buf;
+ buf = (char *) malloc(intSize);
+ free(buf); // GOOD
+ if(buf) free(buf); // BAD
+}
+void workFunction_1(char *s) {
+ int intSize = 10;
+ char *buf;
+ buf = (char *) malloc(intSize);
+ free(buf); // GOOD
+ free(buf); // BAD
+}
+void workFunction_2(char *s) {
+ int intSize = 10;
+ char *buf;
+ buf = (char *) malloc(intSize);
+ free(buf); // GOOD
+ buf = NULL;
+ free(buf); // GOOD
+}
+void workFunction_3(char *s) {
+ int intSize = 10;
+ char *buf;
+ int intFlag;
+ buf = (char *) malloc(intSize);
+ if(buf[1]%5) {
+ free(buf); // GOOD
+ buf = NULL;
+ }
+ free(buf); // GOOD
+}
+void workFunction_4(char *s) {
+ int intSize = 10;
+ char *buf;
+ char *tmpbuf;
+ tmpbuf = (char *) malloc(intSize);
+ buf = (char *) malloc(intSize);
+ free(buf); // GOOD
+ buf = tmpbuf;
+ free(buf); // GOOD
+}
+void workFunction_5(char *s, int intFlag) {
+ int intSize = 10;
+ char *buf;
+
+ buf = (char *) malloc(intSize);
+ if(intFlag) {
+ free(buf); // GOOD
+ }
+ free(buf); // BAD
+}
+void workFunction_6(char *s, int intFlag) {
+ int intSize = 10;
+ char *buf;
+ char *tmpbuf;
+
+ tmpbuf = (char *) malloc(intSize);
+ buf = (char *) malloc(intSize);
+ if(intFlag) {
+ free(buf); // GOOD
+ buf = tmpbuf;
+ }
+ free(buf); // GOOD
+}
+void workFunction_7(char *s) {
+ int intSize = 10;
+ char *buf;
+ char *buf1;
+ buf = (char *) malloc(intSize);
+ buf1 = (char *) realloc(buf,intSize*4);
+ free(buf); // BAD
+}
+void workFunction_8(char *s) {
+ int intSize = 10;
+ char *buf;
+ char *buf1;
+ buf = (char *) malloc(intSize);
+ buf1 = (char *) realloc(buf,intSize*4);
+ if(!buf1)
+ free(buf); // GOOD
+}
+void workFunction_9(char *s) {
+ int intSize = 10;
+ char *buf;
+ char *buf1;
+ buf = (char *) malloc(intSize);
+ if(!(buf1 = (char *) realloc(buf,intSize*4)))
+ free(buf); // GOOD
+}
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.expected
deleted file mode 100644
index 80e82cff2129..000000000000
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.expected
+++ /dev/null
@@ -1,5 +0,0 @@
-| test.cpp:30:15:30:26 | call to operator new[] | memory allocation error check is incorrect or missing |
-| test.cpp:38:9:38:20 | call to operator new[] | memory allocation error check is incorrect or missing |
-| test.cpp:50:13:50:38 | call to operator new[] | memory allocation error check is incorrect or missing |
-| test.cpp:51:22:51:47 | call to operator new[] | memory allocation error check is incorrect or missing |
-| test.cpp:53:18:53:43 | call to operator new[] | memory allocation error check is incorrect or missing |
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.qlref
deleted file mode 100644
index fc3252ef1220..000000000000
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.qlref
+++ /dev/null
@@ -1 +0,0 @@
-experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.ql
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp
deleted file mode 100644
index 4fc12d9ccbfe..000000000000
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-#define NULL ((void*)0)
-class exception {};
-
-namespace std{
- struct nothrow_t {};
- typedef unsigned long size_t;
- class bad_alloc{
- const char* what() const throw();
- };
- extern const std::nothrow_t nothrow;
-}
-
-using namespace std;
-
-void* operator new(std::size_t _Size);
-void* operator new[](std::size_t _Size);
-void* operator new( std::size_t count, const std::nothrow_t& tag ) noexcept;
-void* operator new[]( std::size_t count, const std::nothrow_t& tag ) noexcept;
-
-void badNew_0_0()
-{
- while (true) {
- new int[100]; // BAD [NOT DETECTED]
- if(!(new int[100])) // BAD [NOT DETECTED]
- return;
- }
-}
-void badNew_0_1()
-{
- int * i = new int[100]; // BAD
- if(i == 0)
- return;
- if(!i)
- return;
- if(i == NULL)
- return;
- int * j;
- j = new int[100]; // BAD
- if(j == 0)
- return;
- if(!j)
- return;
- if(j == NULL)
- return;
-}
-void badNew_1_0()
-{
- try {
- while (true) {
- new(std::nothrow) int[100]; // BAD
- int* p = new(std::nothrow) int[100]; // BAD
- int* p1;
- p1 = new(std::nothrow) int[100]; // BAD
- }
- } catch (const exception &){//const std::bad_alloc& e) {
-// std::cout << e.what() << '\n';
- }
-}
-void badNew_1_1()
-{
- while (true) {
- int* p = new(std::nothrow) int[100]; // BAD [NOT DETECTED]
- new(std::nothrow) int[100]; // BAD [NOT DETECTED]
- }
-}
-
-void goodNew_0_0()
-{
- try {
- while (true) {
- new int[100]; // GOOD
- }
- } catch (const exception &){//const std::bad_alloc& e) {
-// std::cout << e.what() << '\n';
- }
-}
-
-void goodNew_1_0()
-{
- while (true) {
- int* p = new(std::nothrow) int[100]; // GOOD
- if (p == nullptr) {
-// std::cout << "Allocation returned nullptr\n";
- break;
- }
- int* p1;
- p1 = new(std::nothrow) int[100]; // GOOD
- if (p1 == nullptr) {
-// std::cout << "Allocation returned nullptr\n";
- break;
- }
- if (new(std::nothrow) int[100] == nullptr) { // GOOD
-// std::cout << "Allocation returned nullptr\n";
- break;
- }
- }
-}
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementAfterRefactoringTheCode.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementAfterRefactoringTheCode.expected
new file mode 100644
index 000000000000..1eca77a526f1
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementAfterRefactoringTheCode.expected
@@ -0,0 +1,4 @@
+| test.c:15:6:15:16 | ... + ... | this expression needs your attention |
+| test.c:17:17:17:27 | ... + ... | this expression needs your attention |
+| test.c:22:10:22:15 | ... > ... | this expression needs your attention |
+| test.c:26:10:26:15 | ... > ... | this expression needs your attention |
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementAfterRefactoringTheCode.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementAfterRefactoringTheCode.qlref
new file mode 100644
index 000000000000..496d5f1b7be6
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementAfterRefactoringTheCode.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.ql
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/test.c
index 1db60826b60a..1f41f499dede 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/test.c
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/test.c
@@ -12,14 +12,18 @@ void workFunction_0(char *s) {
void workFunction_1(char *s) {
int intA,intB;
- if(intA + intB) return; // BAD [NOT DETECTED]
+ if(intA + intB) return; // BAD
if(intA + intB>4) return; // GOOD
- if(intA>0 && (intA + intB)) return; // BAD [NOT DETECTED]
+ if(intA>0 && (intA + intB)) return; // BAD
while(intA>0)
{
if(intB - intA<10) break;
intA--;
- }while(intA>0); // BAD [NOT DETECTED]
+ }while(intA>0); // BAD
+ for(intA=100; intA>0; intA--)
+ {
+ if(intB - intA<10) break;
+ }while(intA>0); // BAD
while(intA>0)
{
if(intB - intA<10) break;
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.expected
index 103afd8ffd94..75e40fb96b35 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.expected
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.expected
@@ -1,9 +1,9 @@
-| test.c:42:3:42:24 | ... = ... | potential unsafe or redundant assignment. |
-| test.c:43:3:43:40 | ... = ... | potential unsafe or redundant assignment. |
-| test.c:44:3:44:40 | ... = ... | potential unsafe or redundant assignment. |
-| test.c:45:3:45:44 | ... = ... | potential unsafe or redundant assignment. |
-| test.c:46:3:46:44 | ... = ... | potential unsafe or redundant assignment. |
-| test.c:47:3:47:48 | ... = ... | potential unsafe or redundant assignment. |
-| test.c:48:3:48:48 | ... = ... | potential unsafe or redundant assignment. |
-| test.c:49:3:49:50 | ... = ... | potential unsafe or redundant assignment. |
-| test.c:50:3:50:50 | ... = ... | potential unsafe or redundant assignment. |
+| test.c:16:3:16:24 | ... = ... | potential unsafe or redundant assignment. |
+| test.c:17:3:17:40 | ... = ... | potential unsafe or redundant assignment. |
+| test.c:18:3:18:44 | ... = ... | potential unsafe or redundant assignment. |
+| test.c:19:3:19:44 | ... = ... | potential unsafe or redundant assignment. |
+| test.c:20:3:20:48 | ... = ... | potential unsafe or redundant assignment. |
+| test.c:21:3:21:48 | ... = ... | potential unsafe or redundant assignment. |
+| test.c:22:3:22:52 | ... = ... | potential unsafe or redundant assignment. |
+| test.c:23:3:23:50 | ... = ... | potential unsafe or redundant assignment. |
+| test.c:24:3:24:54 | ... = ... | potential unsafe or redundant assignment. |
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.expected
deleted file mode 100644
index af52dac01447..000000000000
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.expected
+++ /dev/null
@@ -1,3 +0,0 @@
-| test.c:4:3:4:9 | call to strncat | if the used buffer is full, writing out of the buffer is possible |
-| test.c:11:3:11:9 | call to strncat | if the used buffer is full, writing out of the buffer is possible |
-| test.c:19:3:19:9 | call to strncat | if the used buffer is full, writing out of the buffer is possible |
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.qlref
deleted file mode 100644
index 8fd8b1b32175..000000000000
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.qlref
+++ /dev/null
@@ -1 +0,0 @@
-experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.ql
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/test.c
index d986bb3b13c6..a204aa4db29e 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/test.c
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/test.c
@@ -1,70 +1,46 @@
-void workFunction_0(char *s) {
- char buf[80];
- strncat(buf, s, sizeof(buf)-strlen(buf)-1); // GOOD
- strncat(buf, s, sizeof(buf)-strlen(buf)); // BAD
- strncat(buf, "fix", sizeof(buf)-strlen(buf)); // BAD [NOT DETECTED]
-}
-void workFunction_1(char *s) {
-#define MAX_SIZE 80
- char buf[MAX_SIZE];
- strncat(buf, s, MAX_SIZE-strlen(buf)-1); // GOOD
- strncat(buf, s, MAX_SIZE-strlen(buf)); // BAD
- strncat(buf, "fix", MAX_SIZE-strlen(buf)); // BAD [NOT DETECTED]
-}
-void workFunction_2_0(char *s) {
- char * buf;
- int len=80;
- buf = (char *) malloc(len);
- strncat(buf, s, len-strlen(buf)-1); // GOOD
- strncat(buf, s, len-strlen(buf)); // BAD
- strncat(buf, "fix", len-strlen(buf)); // BAD [NOT DETECTED]
-}
-void workFunction_2_1(char *s) {
- char * buf;
- int len=80;
- buf = (char *) malloc(len+1);
- strncat(buf, s, len-strlen(buf)-1); // GOOD
- strncat(buf, s, len-strlen(buf)); // GOOD
-}
+char * strncat(char*, const char*, unsigned);
+unsigned strlen(const char*);
+void* malloc(unsigned);
struct buffers
{
- unsigned char buff1[50];
- unsigned char *buff2;
+ unsigned char array[50];
+ unsigned char *pointer;
} globalBuff1,*globalBuff2,globalBuff1_c,*globalBuff2_c;
-
-void badFunc0(){
+void strlen_test1(){
unsigned char buff1[12];
struct buffers buffAll;
struct buffers * buffAll1;
buff1[strlen(buff1)]=0; // BAD
- buffAll.buff1[strlen(buffAll.buff1)]=0; // BAD
- buffAll.buff2[strlen(buffAll.buff2)]=0; // BAD
- buffAll1->buff1[strlen(buffAll1->buff1)]=0; // BAD
- buffAll1->buff2[strlen(buffAll1->buff2)]=0; // BAD
- globalBuff1.buff1[strlen(globalBuff1.buff1)]=0; // BAD
- globalBuff1.buff2[strlen(globalBuff1.buff2)]=0; // BAD
- globalBuff2->buff1[strlen(globalBuff2->buff1)]=0; // BAD
- globalBuff2->buff2[strlen(globalBuff2->buff2)]=0; // BAD
+ buffAll.array[strlen(buffAll.array)]=0; // BAD
+ buffAll.pointer[strlen(buffAll.pointer)]=0; // BAD
+ buffAll1->array[strlen(buffAll1->array)]=0; // BAD
+ buffAll1->pointer[strlen(buffAll1->pointer)]=0; // BAD
+ globalBuff1.array[strlen(globalBuff1.array)]=0; // BAD
+ globalBuff1.pointer[strlen(globalBuff1.pointer)]=0; // BAD
+ globalBuff2->array[strlen(globalBuff2->array)]=0; // BAD
+ globalBuff2->pointer[strlen(globalBuff2->pointer)]=0; // BAD
}
-void noBadFunc0(){
+
+void strlen_test2(){
unsigned char buff1[12],buff1_c[12];
struct buffers buffAll,buffAll_c;
struct buffers * buffAll1,*buffAll1_c;
buff1[strlen(buff1_c)]=0; // GOOD
- buffAll.buff1[strlen(buffAll_c.buff1)]=0; // GOOD
- buffAll.buff2[strlen(buffAll.buff1)]=0; // GOOD
- buffAll1->buff1[strlen(buffAll1_c->buff1)]=0; // GOOD
- buffAll1->buff2[strlen(buffAll1->buff1)]=0; // GOOD
- globalBuff1.buff1[strlen(globalBuff1_c.buff1)]=0; // GOOD
- globalBuff1.buff2[strlen(globalBuff1.buff1)]=0; // GOOD
- globalBuff2->buff1[strlen(globalBuff2_c->buff1)]=0; // GOOD
- globalBuff2->buff2[strlen(globalBuff2->buff1)]=0; // GOOD
+ buffAll.array[strlen(buffAll_c.array)]=0; // GOOD
+ buffAll.pointer[strlen(buffAll.array)]=0; // GOOD
+ buffAll1->array[strlen(buffAll1_c->array)]=0; // GOOD
+ buffAll1->pointer[strlen(buffAll1->array)]=0; // GOOD
+ globalBuff1.array[strlen(globalBuff1_c.array)]=0; // GOOD
+ globalBuff1.pointer[strlen(globalBuff1.array)]=0; // GOOD
+ globalBuff2->array[strlen(globalBuff2_c->array)]=0; // GOOD
+ globalBuff2->pointer[strlen(globalBuff2->array)]=0; // GOOD
}
-void goodFunc0(){
+
+void strlen_test3(){
unsigned char buffer[12];
int i;
for(i = 0; i < 6; i++)
diff --git a/cpp/ql/test/include/memory.h b/cpp/ql/test/include/memory.h
new file mode 100644
index 000000000000..fdd09a7d43b5
--- /dev/null
+++ b/cpp/ql/test/include/memory.h
@@ -0,0 +1,139 @@
+#if !defined(CODEQL_MEMORY_H)
+#define CODEQL_MEMORY_H
+
+namespace std {
+ namespace detail {
+ template
+ class compressed_pair_element {
+ T element;
+
+ public:
+ compressed_pair_element() = default;
+ compressed_pair_element(const T& t) : element(t) {}
+
+ T& get() { return element; }
+
+ const T& get() const { return element; }
+ };
+
+ template
+ struct compressed_pair : private compressed_pair_element, private compressed_pair_element {
+ compressed_pair() = default;
+ compressed_pair(T& t) : compressed_pair_element(t), compressed_pair_element() {}
+ compressed_pair(const compressed_pair&) = delete;
+ compressed_pair(compressed_pair&&) noexcept = default;
+
+ T& first() { return static_cast&>(*this).get(); }
+ U& second() { return static_cast&>(*this).get(); }
+
+ const T& first() const { return static_cast&>(*this).get(); }
+ const U& second() const { return static_cast&>(*this).get(); }
+ };
+ }
+
+ template
+ struct default_delete {
+ void operator()(T* ptr) const { delete ptr; }
+ };
+
+ template
+ struct default_delete {
+ template
+ void operator()(U* ptr) const { delete[] ptr; }
+ };
+
+ template >
+ class unique_ptr {
+ private:
+ detail::compressed_pair data;
+ public:
+ constexpr unique_ptr() noexcept {}
+ explicit unique_ptr(T* ptr) noexcept : data(ptr) {}
+ unique_ptr(const unique_ptr& ptr) = delete;
+ unique_ptr(unique_ptr&& ptr) noexcept = default;
+
+ unique_ptr& operator=(unique_ptr&& ptr) noexcept = default;
+
+ T& operator*() const { return *get(); }
+ T* operator->() const noexcept { return get(); }
+
+ T* get() const noexcept { return data.first(); }
+ T* release() noexcept {
+ Deleter& d = data.second();
+ d(data.first());
+ data.first() = nullptr;
+ }
+
+ ~unique_ptr() {
+ Deleter& d = data.second();
+ d(data.first());
+ }
+ };
+
+ template unique_ptr make_unique(Args&&... args) {
+ return unique_ptr(new T(args...)); // std::forward calls elided for simplicity.
+ }
+
+ class ctrl_block {
+ unsigned uses;
+
+ public:
+ ctrl_block() : uses(1) {}
+
+ void inc() { ++uses; }
+ bool dec() { return --uses == 0; }
+
+ virtual void destroy() = 0;
+ virtual ~ctrl_block() {}
+ };
+
+ template >
+ struct ctrl_block_impl: public ctrl_block {
+ T* ptr;
+ Deleter d;
+
+ ctrl_block_impl(T* ptr, Deleter d) : ptr(ptr), d(d) {}
+ virtual void destroy() override { d(ptr); }
+ };
+
+ template
+ class shared_ptr {
+ private:
+ ctrl_block* ctrl;
+ T* ptr;
+
+ void dec() {
+ if(ctrl->dec()) {
+ ctrl->destroy();
+ delete ctrl;
+ }
+ }
+
+ void inc() {
+ ctrl->inc();
+ }
+
+ public:
+ constexpr shared_ptr() noexcept = default;
+ shared_ptr(T* ptr) : ctrl(new ctrl_block_impl(ptr, default_delete())) {}
+ shared_ptr(const shared_ptr& s) noexcept : ptr(s.ptr), ctrl(s.ctrl) {
+ inc();
+ }
+ shared_ptr(shared_ptr&& s) noexcept = default;
+ shared_ptr(unique_ptr&& s) : shared_ptr(s.release()) {
+ }
+ T* operator->() const { return ptr; }
+
+ T& operator*() const { return *ptr; }
+
+ T* get() const noexcept { return ptr; }
+
+ ~shared_ptr() { dec(); }
+ };
+
+ template shared_ptr make_shared(Args&&... args) {
+ return shared_ptr(new T(args...)); // std::forward calls elided for simplicity.
+ }
+}
+
+#endif
\ No newline at end of file
diff --git a/cpp/ql/test/include/type_traits.h b/cpp/ql/test/include/type_traits.h
new file mode 100644
index 000000000000..19bdd46906be
--- /dev/null
+++ b/cpp/ql/test/include/type_traits.h
@@ -0,0 +1,21 @@
+#if !defined(CODEQL_TYPE_TRAITS_H)
+#define CODEQL_TYPE_TRAITS_H
+
+namespace std {
+ template
+ struct remove_reference {
+ typedef T type;
+ };
+
+ template
+ struct remove_reference {
+ typedef T type;
+ };
+
+ template
+ struct remove_reference {
+ typedef T type;
+ };
+}
+
+#endif
diff --git a/cpp/ql/test/include/utility.h b/cpp/ql/test/include/utility.h
new file mode 100644
index 000000000000..24c8572886db
--- /dev/null
+++ b/cpp/ql/test/include/utility.h
@@ -0,0 +1,13 @@
+#if !defined(CODEQL_UTILITY_H)
+#define CODEQL_UTILITY_H
+
+#include "type_traits.h"
+
+namespace std {
+ template
+ typename remove_reference::type&& move(T&& src) {
+ return static_cast::type&&>(src);
+ }
+}
+
+#endif
diff --git a/cpp/ql/test/library-tests/clang_ms/element.expected b/cpp/ql/test/library-tests/clang_ms/element.expected
index 11e28a50670d..8ca381ef4063 100644
--- a/cpp/ql/test/library-tests/clang_ms/element.expected
+++ b/cpp/ql/test/library-tests/clang_ms/element.expected
@@ -26,6 +26,8 @@
| clang_ms.cpp:17:1:17:32 | #pragma |
| clang_ms.cpp:18:1:18:31 | #pragma |
| file://:0:0:0:0 | |
+| file://:0:0:0:0 | & |
+| file://:0:0:0:0 | && |
| file://:0:0:0:0 | (global namespace) |
| file://:0:0:0:0 | (unnamed parameter 0) |
| file://:0:0:0:0 | (unnamed parameter 0) |
diff --git a/cpp/ql/test/library-tests/conditions/elements.expected b/cpp/ql/test/library-tests/conditions/elements.expected
index 483f5ad22ed9..cdab5557305a 100644
--- a/cpp/ql/test/library-tests/conditions/elements.expected
+++ b/cpp/ql/test/library-tests/conditions/elements.expected
@@ -1,4 +1,6 @@
| file://:0:0:0:0 | |
+| file://:0:0:0:0 | & |
+| file://:0:0:0:0 | && |
| file://:0:0:0:0 | (global namespace) |
| file://:0:0:0:0 | (unnamed parameter 0) |
| file://:0:0:0:0 | (unnamed parameter 0) |
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp
index d64f1966b49b..843587b576a0 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp
@@ -187,10 +187,10 @@ void test_pointers1()
ptr4 = &ptr3;
sink(buffer); // $ ast,ir
- sink(ptr1); // $ ast,ir
+ sink(ptr1); // $ ast MISSING: ir
sink(ptr2); // $ SPURIOUS: ast
sink(*ptr2); // $ ast MISSING: ir
- sink(ptr3); // $ ast,ir
+ sink(ptr3); // $ ast MISSING: ir
sink(ptr4); // $ SPURIOUS: ast
sink(*ptr4); // $ ast MISSING: ir
}
diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected
index e6234ca17f77..2736cdcd9e10 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected
+++ b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected
@@ -58,29 +58,29 @@ edges
| aliasing.cpp:98:3:98:21 | Chi [m1] | aliasing.cpp:100:14:100:14 | Store [m1] |
| aliasing.cpp:98:10:98:19 | call to user_input | aliasing.cpp:98:3:98:21 | Chi [m1] |
| aliasing.cpp:100:14:100:14 | Store [m1] | aliasing.cpp:102:8:102:10 | * ... |
-| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:121:15:121:16 | taint_a_ptr output argument [array content] |
-| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:131:15:131:16 | taint_a_ptr output argument [array content] |
-| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:136:15:136:17 | taint_a_ptr output argument [array content] |
-| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:175:15:175:22 | taint_a_ptr output argument [array content] |
-| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:187:15:187:22 | taint_a_ptr output argument [array content] |
-| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:200:15:200:24 | taint_a_ptr output argument [array content] |
-| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:106:3:106:20 | Chi [array content] |
-| aliasing.cpp:121:15:121:16 | Chi [array content] | aliasing.cpp:122:8:122:12 | access to array |
-| aliasing.cpp:121:15:121:16 | taint_a_ptr output argument [array content] | aliasing.cpp:121:15:121:16 | Chi [array content] |
-| aliasing.cpp:131:15:131:16 | Chi [array content] | aliasing.cpp:132:8:132:14 | * ... |
-| aliasing.cpp:131:15:131:16 | taint_a_ptr output argument [array content] | aliasing.cpp:131:15:131:16 | Chi [array content] |
-| aliasing.cpp:136:15:136:17 | Chi [array content] | aliasing.cpp:137:8:137:11 | * ... |
-| aliasing.cpp:136:15:136:17 | taint_a_ptr output argument [array content] | aliasing.cpp:136:15:136:17 | Chi [array content] |
+| aliasing.cpp:106:3:106:20 | Chi [[]] | aliasing.cpp:121:15:121:16 | taint_a_ptr output argument [[]] |
+| aliasing.cpp:106:3:106:20 | Chi [[]] | aliasing.cpp:131:15:131:16 | taint_a_ptr output argument [[]] |
+| aliasing.cpp:106:3:106:20 | Chi [[]] | aliasing.cpp:136:15:136:17 | taint_a_ptr output argument [[]] |
+| aliasing.cpp:106:3:106:20 | Chi [[]] | aliasing.cpp:175:15:175:22 | taint_a_ptr output argument [[]] |
+| aliasing.cpp:106:3:106:20 | Chi [[]] | aliasing.cpp:187:15:187:22 | taint_a_ptr output argument [[]] |
+| aliasing.cpp:106:3:106:20 | Chi [[]] | aliasing.cpp:200:15:200:24 | taint_a_ptr output argument [[]] |
+| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:106:3:106:20 | Chi [[]] |
+| aliasing.cpp:121:15:121:16 | Chi [[]] | aliasing.cpp:122:8:122:12 | access to array |
+| aliasing.cpp:121:15:121:16 | taint_a_ptr output argument [[]] | aliasing.cpp:121:15:121:16 | Chi [[]] |
+| aliasing.cpp:131:15:131:16 | Chi [[]] | aliasing.cpp:132:8:132:14 | * ... |
+| aliasing.cpp:131:15:131:16 | taint_a_ptr output argument [[]] | aliasing.cpp:131:15:131:16 | Chi [[]] |
+| aliasing.cpp:136:15:136:17 | Chi [[]] | aliasing.cpp:137:8:137:11 | * ... |
+| aliasing.cpp:136:15:136:17 | taint_a_ptr output argument [[]] | aliasing.cpp:136:15:136:17 | Chi [[]] |
| aliasing.cpp:175:15:175:22 | Chi | aliasing.cpp:175:15:175:22 | Chi [m1] |
| aliasing.cpp:175:15:175:22 | Chi [m1] | aliasing.cpp:176:13:176:14 | m1 |
-| aliasing.cpp:175:15:175:22 | taint_a_ptr output argument [array content] | aliasing.cpp:175:15:175:22 | Chi |
+| aliasing.cpp:175:15:175:22 | taint_a_ptr output argument [[]] | aliasing.cpp:175:15:175:22 | Chi |
| aliasing.cpp:187:15:187:22 | Chi | aliasing.cpp:187:15:187:22 | Chi [m1] |
| aliasing.cpp:187:15:187:22 | Chi [m1] | aliasing.cpp:188:13:188:14 | Store [m1] |
-| aliasing.cpp:187:15:187:22 | taint_a_ptr output argument [array content] | aliasing.cpp:187:15:187:22 | Chi |
+| aliasing.cpp:187:15:187:22 | taint_a_ptr output argument [[]] | aliasing.cpp:187:15:187:22 | Chi |
| aliasing.cpp:188:13:188:14 | Store [m1] | aliasing.cpp:189:15:189:16 | m1 |
| aliasing.cpp:200:15:200:24 | Chi | aliasing.cpp:200:15:200:24 | Chi [m1] |
| aliasing.cpp:200:15:200:24 | Chi [m1] | aliasing.cpp:201:15:201:16 | m1 |
-| aliasing.cpp:200:15:200:24 | taint_a_ptr output argument [array content] | aliasing.cpp:200:15:200:24 | Chi |
+| aliasing.cpp:200:15:200:24 | taint_a_ptr output argument [[]] | aliasing.cpp:200:15:200:24 | Chi |
| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:7:8:7:13 | access to array |
| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:9:8:9:11 | * ... |
| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:10:8:10:15 | * ... |
@@ -108,67 +108,51 @@ edges
| 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:13:88:22 | call to user_input | by_reference.cpp:88:3:88:24 | Chi [a] |
-| by_reference.cpp:92:3:92:20 | Chi [array content] | by_reference.cpp:104:15:104:22 | taint_a_ptr output argument [array content] |
-| by_reference.cpp:92:3:92:20 | Chi [array content] | by_reference.cpp:108:15:108:24 | taint_a_ptr output argument [array content] |
-| by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:92:3:92:20 | Chi [array content] |
-| by_reference.cpp:96:3:96:19 | Chi [array content] | by_reference.cpp:124:15:124:21 | taint_a_ref output argument [array content] |
-| by_reference.cpp:96:3:96:19 | Chi [array content] | by_reference.cpp:128:15:128:23 | taint_a_ref output argument [array content] |
-| by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:96:3:96:19 | Chi [array content] |
+| by_reference.cpp:92:3:92:20 | Chi [[]] | by_reference.cpp:104:15:104:22 | taint_a_ptr output argument [[]] |
+| by_reference.cpp:92:3:92:20 | Chi [[]] | by_reference.cpp:108:15:108:24 | taint_a_ptr output argument [[]] |
+| by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:92:3:92:20 | Chi [[]] |
+| by_reference.cpp:96:3:96:19 | Chi [[]] | by_reference.cpp:124:15:124:21 | taint_a_ref output argument [[]] |
+| by_reference.cpp:96:3:96:19 | Chi [[]] | by_reference.cpp:128:15:128:23 | taint_a_ref output argument [[]] |
+| by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:96:3:96:19 | Chi [[]] |
| 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:104:15:104:22 | Chi | by_reference.cpp:104:15:104:22 | Chi [a] |
| by_reference.cpp:104:15:104:22 | Chi [a] | by_reference.cpp:112:14:112:14 | a |
-| by_reference.cpp:104:15:104:22 | taint_a_ptr output argument [array content] | by_reference.cpp:104:15:104:22 | Chi |
+| by_reference.cpp:104:15:104:22 | taint_a_ptr output argument [[]] | by_reference.cpp:104:15:104:22 | Chi |
| 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:108:15:108:24 | Chi | by_reference.cpp:108:15:108:24 | Chi [a] |
| by_reference.cpp:108:15:108:24 | Chi [a] | by_reference.cpp:116:16:116:16 | a |
-| by_reference.cpp:108:15:108:24 | taint_a_ptr output argument [array content] | by_reference.cpp:108:15:108:24 | Chi |
+| by_reference.cpp:108:15:108:24 | taint_a_ptr output argument [[]] | by_reference.cpp:108:15:108:24 | Chi |
| 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:124:15:124:21 | Chi | by_reference.cpp:124:15:124:21 | Chi [a] |
| by_reference.cpp:124:15:124:21 | Chi [a] | by_reference.cpp:132:14:132:14 | a |
-| by_reference.cpp:124:15:124:21 | taint_a_ref output argument [array content] | by_reference.cpp:124:15:124:21 | Chi |
+| by_reference.cpp:124:15:124:21 | taint_a_ref output argument [[]] | by_reference.cpp:124:15:124:21 | Chi |
| 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] |
| by_reference.cpp:128:15:128:23 | Chi | by_reference.cpp:128:15:128:23 | Chi [a] |
| by_reference.cpp:128:15:128:23 | Chi [a] | by_reference.cpp:136:16:136:16 | a |
-| by_reference.cpp:128:15:128:23 | taint_a_ref output argument [array content] | by_reference.cpp:128:15:128:23 | Chi |
+| by_reference.cpp:128:15:128:23 | taint_a_ref output argument [[]] | by_reference.cpp:128:15:128:23 | Chi |
| complex.cpp:40:17:40:17 | *b [a_] | complex.cpp:42:16:42:16 | f indirection [a_] |
-| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:42:16:42:16 | Chi [b_] |
| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:42:16:42:16 | f indirection [b_] |
| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:43:16:43:16 | f indirection [b_] |
-| complex.cpp:42:16:42:16 | Chi [b_] | complex.cpp:43:16:43:16 | f indirection [b_] |
-| complex.cpp:42:16:42:16 | a output argument [b_] | complex.cpp:42:16:42:16 | Chi [b_] |
| complex.cpp:42:16:42:16 | a output argument [b_] | complex.cpp:43:16:43:16 | f indirection [b_] |
| complex.cpp:42:16:42:16 | f indirection [a_] | complex.cpp:42:18:42:18 | call to a |
| complex.cpp:42:16:42:16 | f indirection [b_] | complex.cpp:42:16:42:16 | a output argument [b_] |
| complex.cpp:43:16:43:16 | f indirection [b_] | complex.cpp:43:18:43:18 | call to b |
-| complex.cpp:53:12:53:12 | Chi [a_] | complex.cpp:59:7:59:8 | b1 indirection [a_] |
-| complex.cpp:53:12:53:12 | setA output argument [a_] | complex.cpp:53:12:53:12 | Chi [a_] |
| complex.cpp:53:12:53:12 | setA output argument [a_] | complex.cpp:59:7:59:8 | b1 indirection [a_] |
| complex.cpp:53:14:53:17 | call to user_input | complex.cpp:53:12:53:12 | setA output argument [a_] |
| complex.cpp:53:19:53:28 | call to user_input | complex.cpp:53:14:53:17 | call to user_input |
-| complex.cpp:54:12:54:12 | Chi [b_] | complex.cpp:62:7:62:8 | b2 indirection [b_] |
-| complex.cpp:54:12:54:12 | setB output argument [b_] | complex.cpp:54:12:54:12 | Chi [b_] |
| complex.cpp:54:12:54:12 | setB output argument [b_] | complex.cpp:62:7:62:8 | b2 indirection [b_] |
| complex.cpp:54:14:54:17 | call to user_input | complex.cpp:54:12:54:12 | setB output argument [b_] |
| complex.cpp:54:19:54:28 | call to user_input | complex.cpp:54:14:54:17 | call to user_input |
-| complex.cpp:55:12:55:12 | Chi [a_] | complex.cpp:56:12:56:12 | Chi [a_] |
-| complex.cpp:55:12:55:12 | Chi [a_] | complex.cpp:56:12:56:12 | f indirection [a_] |
-| complex.cpp:55:12:55:12 | Chi [a_] | complex.cpp:65:7:65:8 | b3 indirection [a_] |
-| complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:55:12:55:12 | Chi [a_] |
-| complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:56:12:56:12 | Chi [a_] |
| complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:56:12:56:12 | f indirection [a_] |
| complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:65:7:65:8 | b3 indirection [a_] |
| complex.cpp:55:14:55:17 | call to user_input | complex.cpp:55:12:55:12 | setA output argument [a_] |
| complex.cpp:55:19:55:28 | call to user_input | complex.cpp:55:14:55:17 | call to user_input |
-| complex.cpp:56:12:56:12 | Chi [a_] | complex.cpp:65:7:65:8 | b3 indirection [a_] |
-| complex.cpp:56:12:56:12 | Chi [b_] | complex.cpp:65:7:65:8 | b3 indirection [b_] |
| complex.cpp:56:12:56:12 | f indirection [a_] | complex.cpp:56:12:56:12 | setB output argument [a_] |
-| complex.cpp:56:12:56:12 | setB output argument [a_] | complex.cpp:56:12:56:12 | Chi [a_] |
| complex.cpp:56:12:56:12 | setB output argument [a_] | complex.cpp:65:7:65:8 | b3 indirection [a_] |
-| complex.cpp:56:12:56:12 | setB output argument [b_] | complex.cpp:56:12:56:12 | Chi [b_] |
| complex.cpp:56:12:56:12 | setB output argument [b_] | complex.cpp:65:7:65:8 | b3 indirection [b_] |
| complex.cpp:56:14:56:17 | call to user_input | complex.cpp:56:12:56:12 | setB output argument [b_] |
| complex.cpp:56:19:56:28 | call to user_input | complex.cpp:56:14:56:17 | call to user_input |
@@ -320,29 +304,29 @@ nodes
| aliasing.cpp:98:10:98:19 | call to user_input | semmle.label | call to user_input |
| aliasing.cpp:100:14:100:14 | Store [m1] | semmle.label | Store [m1] |
| aliasing.cpp:102:8:102:10 | * ... | semmle.label | * ... |
-| aliasing.cpp:106:3:106:20 | Chi [array content] | semmle.label | Chi [array content] |
+| aliasing.cpp:106:3:106:20 | Chi [[]] | semmle.label | Chi [[]] |
| aliasing.cpp:106:9:106:18 | call to user_input | semmle.label | call to user_input |
-| aliasing.cpp:121:15:121:16 | Chi [array content] | semmle.label | Chi [array content] |
-| aliasing.cpp:121:15:121:16 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
+| aliasing.cpp:121:15:121:16 | Chi [[]] | semmle.label | Chi [[]] |
+| aliasing.cpp:121:15:121:16 | taint_a_ptr output argument [[]] | semmle.label | taint_a_ptr output argument [[]] |
| aliasing.cpp:122:8:122:12 | access to array | semmle.label | access to array |
-| aliasing.cpp:131:15:131:16 | Chi [array content] | semmle.label | Chi [array content] |
-| aliasing.cpp:131:15:131:16 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
+| aliasing.cpp:131:15:131:16 | Chi [[]] | semmle.label | Chi [[]] |
+| aliasing.cpp:131:15:131:16 | taint_a_ptr output argument [[]] | semmle.label | taint_a_ptr output argument [[]] |
| aliasing.cpp:132:8:132:14 | * ... | semmle.label | * ... |
-| aliasing.cpp:136:15:136:17 | Chi [array content] | semmle.label | Chi [array content] |
-| aliasing.cpp:136:15:136:17 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
+| aliasing.cpp:136:15:136:17 | Chi [[]] | semmle.label | Chi [[]] |
+| aliasing.cpp:136:15:136:17 | taint_a_ptr output argument [[]] | semmle.label | taint_a_ptr output argument [[]] |
| aliasing.cpp:137:8:137:11 | * ... | semmle.label | * ... |
| aliasing.cpp:175:15:175:22 | Chi | semmle.label | Chi |
| aliasing.cpp:175:15:175:22 | Chi [m1] | semmle.label | Chi [m1] |
-| aliasing.cpp:175:15:175:22 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
+| aliasing.cpp:175:15:175:22 | taint_a_ptr output argument [[]] | semmle.label | taint_a_ptr output argument [[]] |
| aliasing.cpp:176:13:176:14 | m1 | semmle.label | m1 |
| aliasing.cpp:187:15:187:22 | Chi | semmle.label | Chi |
| aliasing.cpp:187:15:187:22 | Chi [m1] | semmle.label | Chi [m1] |
-| aliasing.cpp:187:15:187:22 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
+| aliasing.cpp:187:15:187:22 | taint_a_ptr output argument [[]] | semmle.label | taint_a_ptr output argument [[]] |
| aliasing.cpp:188:13:188:14 | Store [m1] | semmle.label | Store [m1] |
| aliasing.cpp:189:15:189:16 | m1 | semmle.label | m1 |
| aliasing.cpp:200:15:200:24 | Chi | semmle.label | Chi |
| aliasing.cpp:200:15:200:24 | Chi [m1] | semmle.label | Chi [m1] |
-| aliasing.cpp:200:15:200:24 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
+| aliasing.cpp:200:15:200:24 | taint_a_ptr output argument [[]] | semmle.label | taint_a_ptr output argument [[]] |
| aliasing.cpp:201:15:201:16 | m1 | semmle.label | m1 |
| arrays.cpp:6:12:6:21 | call to user_input | semmle.label | call to user_input |
| arrays.cpp:7:8:7:13 | access to array | semmle.label | access to array |
@@ -376,20 +360,20 @@ nodes
| 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:13:88:22 | call to user_input | semmle.label | call to user_input |
-| by_reference.cpp:92:3:92:20 | Chi [array content] | semmle.label | Chi [array content] |
+| by_reference.cpp:92:3:92:20 | Chi [[]] | semmle.label | Chi [[]] |
| by_reference.cpp:92:9:92:18 | call to user_input | semmle.label | call to user_input |
-| by_reference.cpp:96:3:96:19 | Chi [array content] | semmle.label | Chi [array content] |
+| by_reference.cpp:96:3:96:19 | Chi [[]] | semmle.label | Chi [[]] |
| by_reference.cpp:96:8:96:17 | 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 | taint_inner_a_ptr output argument [a] | semmle.label | taint_inner_a_ptr output argument [a] |
| by_reference.cpp:104:15:104:22 | Chi | semmle.label | Chi |
| by_reference.cpp:104:15:104:22 | Chi [a] | semmle.label | Chi [a] |
-| by_reference.cpp:104:15:104:22 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
+| by_reference.cpp:104:15:104:22 | taint_a_ptr output argument [[]] | semmle.label | taint_a_ptr output argument [[]] |
| by_reference.cpp:106:21:106:41 | Chi [a] | semmle.label | Chi [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:108:15:108:24 | Chi | semmle.label | Chi |
| by_reference.cpp:108:15:108:24 | Chi [a] | semmle.label | Chi [a] |
-| by_reference.cpp:108:15:108:24 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
+| by_reference.cpp:108:15:108:24 | taint_a_ptr output argument [[]] | semmle.label | taint_a_ptr output argument [[]] |
| by_reference.cpp:110:27:110:27 | a | semmle.label | a |
| by_reference.cpp:112:14:112:14 | a | semmle.label | a |
| by_reference.cpp:114:29:114:29 | a | semmle.label | a |
@@ -398,39 +382,33 @@ nodes
| 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:124:15:124:21 | Chi | semmle.label | Chi |
| by_reference.cpp:124:15:124:21 | Chi [a] | semmle.label | Chi [a] |
-| by_reference.cpp:124:15:124:21 | taint_a_ref output argument [array content] | semmle.label | taint_a_ref output argument [array content] |
+| by_reference.cpp:124:15:124:21 | taint_a_ref output argument [[]] | semmle.label | taint_a_ref output argument [[]] |
| by_reference.cpp:126:21:126:40 | Chi [a] | semmle.label | Chi [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:128:15:128:23 | Chi | semmle.label | Chi |
| by_reference.cpp:128:15:128:23 | Chi [a] | semmle.label | Chi [a] |
-| by_reference.cpp:128:15:128:23 | taint_a_ref output argument [array content] | semmle.label | taint_a_ref output argument [array content] |
+| by_reference.cpp:128:15:128:23 | taint_a_ref output argument [[]] | semmle.label | taint_a_ref output argument [[]] |
| by_reference.cpp:130:27:130:27 | a | semmle.label | a |
| by_reference.cpp:132:14:132:14 | a | semmle.label | a |
| by_reference.cpp:134:29:134:29 | a | semmle.label | a |
| by_reference.cpp:136:16:136:16 | a | semmle.label | 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:42:16:42:16 | Chi [b_] | semmle.label | Chi [b_] |
| complex.cpp:42:16:42:16 | a output argument [b_] | semmle.label | a output argument [b_] |
| complex.cpp:42:16:42:16 | f indirection [a_] | semmle.label | f indirection [a_] |
| complex.cpp:42:16:42:16 | f indirection [b_] | semmle.label | f indirection [b_] |
| complex.cpp:42:18:42:18 | call to a | semmle.label | call to a |
| complex.cpp:43:16:43:16 | f indirection [b_] | semmle.label | f indirection [b_] |
| complex.cpp:43:18:43:18 | call to b | semmle.label | call to b |
-| complex.cpp:53:12:53:12 | Chi [a_] | semmle.label | Chi [a_] |
| complex.cpp:53:12:53:12 | setA output argument [a_] | semmle.label | setA output argument [a_] |
| complex.cpp:53:14:53:17 | call to user_input | semmle.label | call to user_input |
| complex.cpp:53:19:53:28 | call to user_input | semmle.label | call to user_input |
-| complex.cpp:54:12:54:12 | Chi [b_] | semmle.label | Chi [b_] |
| complex.cpp:54:12:54:12 | setB output argument [b_] | semmle.label | setB output argument [b_] |
| complex.cpp:54:14:54:17 | call to user_input | semmle.label | call to user_input |
| complex.cpp:54:19:54:28 | call to user_input | semmle.label | call to user_input |
-| complex.cpp:55:12:55:12 | Chi [a_] | semmle.label | Chi [a_] |
| complex.cpp:55:12:55:12 | setA output argument [a_] | semmle.label | setA output argument [a_] |
| complex.cpp:55:14:55:17 | call to user_input | semmle.label | call to user_input |
| complex.cpp:55:19:55:28 | call to user_input | semmle.label | call to user_input |
-| complex.cpp:56:12:56:12 | Chi [a_] | semmle.label | Chi [a_] |
-| complex.cpp:56:12:56:12 | Chi [b_] | semmle.label | Chi [b_] |
| complex.cpp:56:12:56:12 | f indirection [a_] | semmle.label | f indirection [a_] |
| complex.cpp:56:12:56:12 | setB output argument [a_] | semmle.label | setB output argument [a_] |
| complex.cpp:56:12:56:12 | setB output argument [b_] | semmle.label | setB output argument [b_] |
diff --git a/cpp/ql/test/library-tests/dataflow/smart-pointers-taint/test.cpp b/cpp/ql/test/library-tests/dataflow/smart-pointers-taint/test.cpp
index 0dbbd7a23843..bd24f0e72f24 100644
--- a/cpp/ql/test/library-tests/dataflow/smart-pointers-taint/test.cpp
+++ b/cpp/ql/test/library-tests/dataflow/smart-pointers-taint/test.cpp
@@ -7,7 +7,7 @@ void test_unique_ptr_int() {
std::unique_ptr p1(new int(source()));
std::unique_ptr p2 = std::make_unique(source());
- sink(*p1); // $ MISSING: ast,ir
+ sink(*p1); // $ ir MISSING: ast
sink(*p2); // $ ast ir=8:50
}
@@ -21,7 +21,7 @@ void test_unique_ptr_struct() {
std::unique_ptr p1(new A{source(), 0});
std::unique_ptr p2 = std::make_unique(source(), 0);
- sink(p1->x); // $ MISSING: ast,ir
+ sink(p1->x); // $ ir MISSING: ast
sink(p1->y);
sink(p2->x); // $ MISSING: ast,ir
sink(p2->y);
@@ -31,7 +31,7 @@ void test_shared_ptr_int() {
std::shared_ptr p1(new int(source()));
std::shared_ptr p2 = std::make_shared(source());
- sink(*p1); // $ ast
+ sink(*p1); // $ ast ir
sink(*p2); // $ ast ir=32:50
}
@@ -39,7 +39,7 @@ void test_shared_ptr_struct() {
std::shared_ptr p1(new A{source(), 0});
std::shared_ptr p2 = std::make_shared(source(), 0);
- sink(p1->x); // $ MISSING: ast,ir
+ sink(p1->x); // $ ir MISSING: ast
sink(p1->y);
sink(p2->x); // $ MISSING: ast,ir
sink(p2->y);
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
index 59ba0948d858..ce939661c92d 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
@@ -3228,6 +3228,7 @@
| smart_pointer.cpp:17:32:17:54 | call to make_shared | smart_pointer.cpp:18:11:18:11 | p | |
| smart_pointer.cpp:17:32:17:54 | call to make_shared | smart_pointer.cpp:19:10:19:10 | p | |
| smart_pointer.cpp:18:10:18:10 | ref arg call to operator* | smart_pointer.cpp:18:11:18:11 | p [inner post update] | |
+| smart_pointer.cpp:18:10:18:10 | ref arg call to operator* | smart_pointer.cpp:18:11:18:11 | ref arg p | TAINT |
| smart_pointer.cpp:18:10:18:10 | ref arg call to operator* | smart_pointer.cpp:19:10:19:10 | p | |
| smart_pointer.cpp:18:11:18:11 | p | smart_pointer.cpp:18:10:18:10 | call to operator* | TAINT |
| smart_pointer.cpp:18:11:18:11 | ref arg p | smart_pointer.cpp:18:11:18:11 | p [inner post update] | |
@@ -3240,6 +3241,7 @@
| smart_pointer.cpp:29:32:29:54 | call to make_unique | smart_pointer.cpp:30:11:30:11 | p | |
| smart_pointer.cpp:29:32:29:54 | call to make_unique | smart_pointer.cpp:31:10:31:10 | p | |
| smart_pointer.cpp:30:10:30:10 | ref arg call to operator* | smart_pointer.cpp:30:11:30:11 | p [inner post update] | |
+| smart_pointer.cpp:30:10:30:10 | ref arg call to operator* | smart_pointer.cpp:30:11:30:11 | ref arg p | TAINT |
| smart_pointer.cpp:30:10:30:10 | ref arg call to operator* | smart_pointer.cpp:31:10:31:10 | p | |
| smart_pointer.cpp:30:11:30:11 | p | smart_pointer.cpp:30:10:30:10 | call to operator* | TAINT |
| smart_pointer.cpp:30:11:30:11 | ref arg p | smart_pointer.cpp:30:11:30:11 | p [inner post update] | |
@@ -3248,6 +3250,7 @@
| smart_pointer.cpp:35:30:35:50 | call to make_shared | smart_pointer.cpp:38:10:38:10 | p | |
| smart_pointer.cpp:35:30:35:50 | call to make_shared | smart_pointer.cpp:39:11:39:11 | p | |
| smart_pointer.cpp:37:5:37:5 | call to operator* [post update] | smart_pointer.cpp:37:6:37:6 | p [inner post update] | |
+| smart_pointer.cpp:37:5:37:5 | call to operator* [post update] | smart_pointer.cpp:37:6:37:6 | ref arg p | TAINT |
| smart_pointer.cpp:37:5:37:5 | call to operator* [post update] | smart_pointer.cpp:38:10:38:10 | p | |
| smart_pointer.cpp:37:5:37:5 | call to operator* [post update] | smart_pointer.cpp:39:11:39:11 | p | |
| smart_pointer.cpp:37:5:37:17 | ... = ... | smart_pointer.cpp:37:5:37:5 | call to operator* [post update] | |
@@ -3262,6 +3265,7 @@
| smart_pointer.cpp:43:29:43:51 | call to unique_ptr | smart_pointer.cpp:46:10:46:10 | p | |
| smart_pointer.cpp:43:29:43:51 | call to unique_ptr | smart_pointer.cpp:47:11:47:11 | p | |
| smart_pointer.cpp:45:5:45:5 | call to operator* [post update] | smart_pointer.cpp:45:6:45:6 | p [inner post update] | |
+| smart_pointer.cpp:45:5:45:5 | call to operator* [post update] | smart_pointer.cpp:45:6:45:6 | ref arg p | TAINT |
| smart_pointer.cpp:45:5:45:5 | call to operator* [post update] | smart_pointer.cpp:46:10:46:10 | p | |
| smart_pointer.cpp:45:5:45:5 | call to operator* [post update] | smart_pointer.cpp:47:11:47:11 | p | |
| smart_pointer.cpp:45:5:45:17 | ... = ... | smart_pointer.cpp:45:5:45:5 | call to operator* [post update] | |
@@ -3275,11 +3279,11 @@
| smart_pointer.cpp:51:30:51:50 | call to make_shared | smart_pointer.cpp:52:10:52:10 | p | |
| smart_pointer.cpp:51:52:51:57 | call to source | smart_pointer.cpp:51:30:51:50 | call to make_shared | TAINT |
| smart_pointer.cpp:52:10:52:10 | p | smart_pointer.cpp:52:12:52:14 | call to get | |
-| smart_pointer.cpp:52:12:52:14 | ref arg call to get | smart_pointer.cpp:52:10:52:10 | ref arg p | |
+| smart_pointer.cpp:52:12:52:14 | ref arg call to get | smart_pointer.cpp:52:10:52:10 | ref arg p | TAINT |
| smart_pointer.cpp:56:30:56:50 | call to make_unique | smart_pointer.cpp:57:10:57:10 | p | |
| smart_pointer.cpp:56:52:56:57 | call to source | smart_pointer.cpp:56:30:56:50 | call to make_unique | TAINT |
| smart_pointer.cpp:57:10:57:10 | p | smart_pointer.cpp:57:12:57:14 | call to get | |
-| smart_pointer.cpp:57:12:57:14 | ref arg call to get | smart_pointer.cpp:57:10:57:10 | ref arg p | |
+| smart_pointer.cpp:57:12:57:14 | ref arg call to get | smart_pointer.cpp:57:10:57:10 | ref arg p | TAINT |
| smart_pointer.cpp:65:28:65:46 | call to make_unique | smart_pointer.cpp:66:10:66:10 | p | |
| smart_pointer.cpp:65:28:65:46 | call to make_unique | smart_pointer.cpp:67:10:67:10 | p | |
| smart_pointer.cpp:65:48:65:53 | call to source | smart_pointer.cpp:65:28:65:46 | call to make_unique | TAINT |
@@ -3291,6 +3295,7 @@
| smart_pointer.cpp:70:37:70:39 | ptr | smart_pointer.cpp:71:4:71:6 | ptr | |
| smart_pointer.cpp:71:3:71:3 | call to operator* [post update] | smart_pointer.cpp:70:37:70:39 | ptr | |
| smart_pointer.cpp:71:3:71:3 | call to operator* [post update] | smart_pointer.cpp:71:4:71:6 | ptr [inner post update] | |
+| smart_pointer.cpp:71:3:71:3 | call to operator* [post update] | smart_pointer.cpp:71:4:71:6 | ref arg ptr | TAINT |
| smart_pointer.cpp:71:3:71:17 | ... = ... | smart_pointer.cpp:71:3:71:3 | call to operator* [post update] | |
| smart_pointer.cpp:71:4:71:6 | ptr | smart_pointer.cpp:71:3:71:3 | call to operator* | |
| smart_pointer.cpp:71:4:71:6 | ref arg ptr | smart_pointer.cpp:70:37:70:39 | ptr | |
@@ -3319,7 +3324,7 @@
| smart_pointer.cpp:87:3:87:3 | ref arg p | smart_pointer.cpp:89:8:89:8 | p | |
| smart_pointer.cpp:87:3:87:17 | ... = ... | smart_pointer.cpp:87:6:87:6 | x [post update] | |
| smart_pointer.cpp:87:3:87:17 | ... = ... | smart_pointer.cpp:88:11:88:11 | x | |
-| smart_pointer.cpp:87:4:87:4 | call to operator-> [post update] | smart_pointer.cpp:87:3:87:3 | ref arg p | |
+| smart_pointer.cpp:87:4:87:4 | call to operator-> [post update] | smart_pointer.cpp:87:3:87:3 | ref arg p | TAINT |
| smart_pointer.cpp:87:10:87:15 | call to source | smart_pointer.cpp:87:3:87:17 | ... = ... | |
| smart_pointer.cpp:88:8:88:8 | p | smart_pointer.cpp:88:9:88:9 | call to operator-> | |
| smart_pointer.cpp:88:8:88:8 | ref arg p | smart_pointer.cpp:86:45:86:45 | p | |
@@ -3333,7 +3338,7 @@
| smart_pointer.cpp:91:3:91:3 | ref arg q | smart_pointer.cpp:94:8:94:8 | q | |
| smart_pointer.cpp:91:3:91:20 | ... = ... | smart_pointer.cpp:91:9:91:9 | x [post update] | |
| smart_pointer.cpp:91:3:91:20 | ... = ... | smart_pointer.cpp:92:14:92:14 | x | |
-| smart_pointer.cpp:91:4:91:4 | call to operator-> [post update] | smart_pointer.cpp:91:3:91:3 | ref arg q | |
+| smart_pointer.cpp:91:4:91:4 | call to operator-> [post update] | smart_pointer.cpp:91:3:91:3 | ref arg q | TAINT |
| smart_pointer.cpp:91:13:91:18 | call to source | smart_pointer.cpp:91:3:91:20 | ... = ... | |
| smart_pointer.cpp:92:8:92:8 | q | smart_pointer.cpp:92:9:92:9 | call to operator-> | |
| smart_pointer.cpp:92:8:92:8 | ref arg q | smart_pointer.cpp:86:67:86:67 | q | |
@@ -3351,26 +3356,27 @@
| smart_pointer.cpp:102:25:102:50 | call to unique_ptr | smart_pointer.cpp:104:8:104:8 | p | |
| smart_pointer.cpp:103:11:103:11 | p | smart_pointer.cpp:103:13:103:15 | call to get | |
| smart_pointer.cpp:103:11:103:11 | ref arg p | smart_pointer.cpp:104:8:104:8 | p | |
-| smart_pointer.cpp:103:13:103:15 | ref arg call to get | smart_pointer.cpp:103:11:103:11 | ref arg p | |
+| smart_pointer.cpp:103:13:103:15 | ref arg call to get | smart_pointer.cpp:103:11:103:11 | ref arg p | TAINT |
| smart_pointer.cpp:104:8:104:8 | p | smart_pointer.cpp:104:9:104:9 | call to operator-> | |
| smart_pointer.cpp:112:40:112:42 | ptr | smart_pointer.cpp:112:40:112:42 | ptr | |
| smart_pointer.cpp:112:40:112:42 | ptr | smart_pointer.cpp:113:2:113:4 | ptr | |
| smart_pointer.cpp:113:2:113:4 | ptr | smart_pointer.cpp:113:5:113:5 | call to operator-> | |
| smart_pointer.cpp:113:2:113:4 | ref arg ptr | smart_pointer.cpp:112:40:112:42 | ptr | |
| smart_pointer.cpp:113:2:113:18 | ... = ... | smart_pointer.cpp:113:7:113:7 | x [post update] | |
-| smart_pointer.cpp:113:5:113:5 | call to operator-> [post update] | smart_pointer.cpp:113:2:113:4 | ref arg ptr | |
+| smart_pointer.cpp:113:5:113:5 | call to operator-> [post update] | smart_pointer.cpp:113:2:113:4 | ref arg ptr | TAINT |
| smart_pointer.cpp:113:11:113:16 | call to source | smart_pointer.cpp:113:2:113:18 | ... = ... | |
| smart_pointer.cpp:116:52:116:54 | ptr | smart_pointer.cpp:116:52:116:54 | ptr | |
| smart_pointer.cpp:116:52:116:54 | ptr | smart_pointer.cpp:117:2:117:4 | ptr | |
| smart_pointer.cpp:117:2:117:4 | ptr | smart_pointer.cpp:117:5:117:5 | call to operator-> | |
| smart_pointer.cpp:117:2:117:4 | ref arg ptr | smart_pointer.cpp:116:52:116:54 | ptr | |
| smart_pointer.cpp:117:2:117:18 | ... = ... | smart_pointer.cpp:117:7:117:7 | x [post update] | |
-| smart_pointer.cpp:117:5:117:5 | call to operator-> [post update] | smart_pointer.cpp:117:2:117:4 | ref arg ptr | |
+| smart_pointer.cpp:117:5:117:5 | call to operator-> [post update] | smart_pointer.cpp:117:2:117:4 | ref arg ptr | TAINT |
| smart_pointer.cpp:117:11:117:16 | call to source | smart_pointer.cpp:117:2:117:18 | ... = ... | |
| smart_pointer.cpp:120:48:120:50 | ptr | smart_pointer.cpp:120:48:120:50 | ptr | |
| smart_pointer.cpp:120:48:120:50 | ptr | smart_pointer.cpp:121:4:121:6 | ptr | |
| smart_pointer.cpp:121:3:121:3 | call to operator* [post update] | smart_pointer.cpp:120:48:120:50 | ptr | |
| smart_pointer.cpp:121:3:121:3 | call to operator* [post update] | smart_pointer.cpp:121:4:121:6 | ptr [inner post update] | |
+| smart_pointer.cpp:121:3:121:3 | call to operator* [post update] | smart_pointer.cpp:121:4:121:6 | ref arg ptr | TAINT |
| smart_pointer.cpp:121:3:121:17 | ... = ... | smart_pointer.cpp:121:3:121:3 | call to operator* [post update] | |
| smart_pointer.cpp:121:4:121:6 | ptr | smart_pointer.cpp:121:3:121:3 | call to operator* | |
| smart_pointer.cpp:121:4:121:6 | ref arg ptr | smart_pointer.cpp:120:48:120:50 | ptr | |
@@ -3386,17 +3392,19 @@
| smart_pointer.cpp:125:18:125:19 | ref arg p1 | smart_pointer.cpp:124:48:124:49 | p1 | |
| smart_pointer.cpp:125:18:125:19 | ref arg p1 | smart_pointer.cpp:126:8:126:9 | p1 | |
| smart_pointer.cpp:125:18:125:22 | ref arg call to shared_ptr | smart_pointer.cpp:125:22:125:22 | q [inner post update] | |
-| smart_pointer.cpp:125:20:125:20 | call to operator-> [post update] | smart_pointer.cpp:125:18:125:19 | ref arg p1 | |
+| smart_pointer.cpp:125:20:125:20 | call to operator-> [post update] | smart_pointer.cpp:125:18:125:19 | ref arg p1 | TAINT |
| smart_pointer.cpp:125:22:125:22 | q | smart_pointer.cpp:125:18:125:22 | call to shared_ptr | |
| smart_pointer.cpp:125:22:125:22 | ref arg q | smart_pointer.cpp:125:22:125:22 | q [inner post update] | |
| smart_pointer.cpp:126:8:126:9 | p1 | smart_pointer.cpp:126:10:126:10 | call to operator-> | |
| smart_pointer.cpp:126:8:126:9 | ref arg p1 | smart_pointer.cpp:124:48:124:49 | p1 | |
-| smart_pointer.cpp:126:10:126:10 | call to operator-> [post update] | smart_pointer.cpp:126:8:126:9 | ref arg p1 | |
+| smart_pointer.cpp:126:10:126:10 | call to operator-> [post update] | smart_pointer.cpp:126:8:126:9 | ref arg p1 | TAINT |
| smart_pointer.cpp:126:12:126:12 | q | smart_pointer.cpp:126:13:126:13 | call to operator-> | |
| smart_pointer.cpp:128:13:128:13 | call to operator* | smart_pointer.cpp:128:13:128:15 | call to shared_ptr | TAINT |
+| smart_pointer.cpp:128:13:128:13 | call to operator* [inner post update] | smart_pointer.cpp:128:14:128:15 | ref arg p2 | TAINT |
| smart_pointer.cpp:128:13:128:13 | ref arg call to operator* | smart_pointer.cpp:124:90:124:91 | p2 | |
| smart_pointer.cpp:128:13:128:13 | ref arg call to operator* | smart_pointer.cpp:128:13:128:13 | call to operator* [inner post update] | |
| smart_pointer.cpp:128:13:128:13 | ref arg call to operator* | smart_pointer.cpp:128:14:128:15 | p2 [inner post update] | |
+| smart_pointer.cpp:128:13:128:13 | ref arg call to operator* | smart_pointer.cpp:128:14:128:15 | ref arg p2 | TAINT |
| smart_pointer.cpp:128:13:128:13 | ref arg call to operator* | smart_pointer.cpp:129:10:129:11 | p2 | |
| smart_pointer.cpp:128:13:128:15 | ref arg call to shared_ptr | smart_pointer.cpp:124:90:124:91 | p2 | |
| smart_pointer.cpp:128:13:128:15 | ref arg call to shared_ptr | smart_pointer.cpp:128:13:128:13 | call to operator* [inner post update] | |
@@ -3409,6 +3417,7 @@
| smart_pointer.cpp:129:9:129:9 | call to operator* | smart_pointer.cpp:129:8:129:8 | call to operator* | TAINT |
| smart_pointer.cpp:129:9:129:9 | ref arg call to operator* | smart_pointer.cpp:124:90:124:91 | p2 | |
| smart_pointer.cpp:129:9:129:9 | ref arg call to operator* | smart_pointer.cpp:129:10:129:11 | p2 [inner post update] | |
+| smart_pointer.cpp:129:9:129:9 | ref arg call to operator* | smart_pointer.cpp:129:10:129:11 | ref arg p2 | TAINT |
| smart_pointer.cpp:129:10:129:11 | p2 | smart_pointer.cpp:129:8:129:8 | call to operator* | |
| smart_pointer.cpp:129:10:129:11 | p2 | smart_pointer.cpp:129:9:129:9 | call to operator* | TAINT |
| smart_pointer.cpp:129:10:129:11 | ref arg p2 | smart_pointer.cpp:124:90:124:91 | p2 | |
@@ -3422,13 +3431,14 @@
| smart_pointer.cpp:133:23:133:24 | p1 | smart_pointer.cpp:133:25:133:25 | call to operator-> | |
| smart_pointer.cpp:133:23:133:24 | ref arg p1 | smart_pointer.cpp:132:53:132:54 | p1 | |
| smart_pointer.cpp:133:23:133:24 | ref arg p1 | smart_pointer.cpp:134:8:134:9 | p1 | |
-| smart_pointer.cpp:133:25:133:25 | call to operator-> [post update] | smart_pointer.cpp:133:23:133:24 | ref arg p1 | |
+| smart_pointer.cpp:133:25:133:25 | call to operator-> [post update] | smart_pointer.cpp:133:23:133:24 | ref arg p1 | TAINT |
| smart_pointer.cpp:134:8:134:9 | p1 | smart_pointer.cpp:134:10:134:10 | call to operator-> | |
| smart_pointer.cpp:134:8:134:9 | ref arg p1 | smart_pointer.cpp:132:53:132:54 | p1 | |
-| smart_pointer.cpp:134:10:134:10 | call to operator-> [post update] | smart_pointer.cpp:134:8:134:9 | ref arg p1 | |
+| smart_pointer.cpp:134:10:134:10 | call to operator-> [post update] | smart_pointer.cpp:134:8:134:9 | ref arg p1 | TAINT |
| smart_pointer.cpp:134:12:134:12 | q | smart_pointer.cpp:134:13:134:13 | call to operator-> | |
| smart_pointer.cpp:136:17:136:17 | ref arg call to operator* | smart_pointer.cpp:132:95:132:96 | p2 | |
| smart_pointer.cpp:136:17:136:17 | ref arg call to operator* | smart_pointer.cpp:136:18:136:19 | p2 [inner post update] | |
+| smart_pointer.cpp:136:17:136:17 | ref arg call to operator* | smart_pointer.cpp:136:18:136:19 | ref arg p2 | TAINT |
| smart_pointer.cpp:136:17:136:17 | ref arg call to operator* | smart_pointer.cpp:137:10:137:11 | p2 | |
| smart_pointer.cpp:136:18:136:19 | p2 | smart_pointer.cpp:136:17:136:17 | call to operator* | TAINT |
| smart_pointer.cpp:136:18:136:19 | ref arg p2 | smart_pointer.cpp:132:95:132:96 | p2 | |
@@ -3437,6 +3447,7 @@
| smart_pointer.cpp:137:9:137:9 | call to operator* | smart_pointer.cpp:137:8:137:8 | call to operator* | TAINT |
| smart_pointer.cpp:137:9:137:9 | ref arg call to operator* | smart_pointer.cpp:132:95:132:96 | p2 | |
| smart_pointer.cpp:137:9:137:9 | ref arg call to operator* | smart_pointer.cpp:137:10:137:11 | p2 [inner post update] | |
+| smart_pointer.cpp:137:9:137:9 | ref arg call to operator* | smart_pointer.cpp:137:10:137:11 | ref arg p2 | TAINT |
| smart_pointer.cpp:137:10:137:11 | p2 | smart_pointer.cpp:137:8:137:8 | call to operator* | |
| smart_pointer.cpp:137:10:137:11 | p2 | smart_pointer.cpp:137:9:137:9 | call to operator* | TAINT |
| smart_pointer.cpp:137:10:137:11 | ref arg p2 | smart_pointer.cpp:132:95:132:96 | p2 | |
@@ -3532,13 +3543,13 @@
| standalone_iterators.cpp:116:10:116:14 | call to begin | standalone_iterators.cpp:120:2:120:3 | it | |
| standalone_iterators.cpp:116:10:116:14 | call to begin | standalone_iterators.cpp:121:7:121:8 | it | |
| standalone_iterators.cpp:117:7:117:8 | it [post update] | standalone_iterators.cpp:122:7:122:8 | c1 | |
-| standalone_iterators.cpp:118:2:118:3 | it | standalone_iterators.cpp:118:5:118:5 | call to operator+= | |
+| standalone_iterators.cpp:118:2:118:3 | it | standalone_iterators.cpp:118:5:118:5 | call to operator+= | TAINT |
| standalone_iterators.cpp:118:2:118:3 | ref arg it | standalone_iterators.cpp:119:7:119:8 | it | |
| standalone_iterators.cpp:118:2:118:3 | ref arg it | standalone_iterators.cpp:120:2:120:3 | it | |
| standalone_iterators.cpp:118:2:118:3 | ref arg it | standalone_iterators.cpp:121:7:121:8 | it | |
| standalone_iterators.cpp:118:2:118:3 | ref arg it | standalone_iterators.cpp:122:7:122:8 | c1 | |
| standalone_iterators.cpp:118:8:118:8 | 1 | standalone_iterators.cpp:118:2:118:3 | ref arg it | TAINT |
-| standalone_iterators.cpp:120:2:120:3 | it | standalone_iterators.cpp:120:5:120:5 | call to operator+= | |
+| standalone_iterators.cpp:120:2:120:3 | it | standalone_iterators.cpp:120:5:120:5 | call to operator+= | TAINT |
| standalone_iterators.cpp:120:2:120:3 | ref arg it | standalone_iterators.cpp:121:7:121:8 | it | |
| standalone_iterators.cpp:120:8:120:13 | call to source | standalone_iterators.cpp:120:2:120:3 | ref arg it | TAINT |
| stl.h:75:8:75:8 | Unknown literal | stl.h:75:8:75:8 | constructor init of field container | TAINT |
@@ -3655,12 +3666,12 @@
| stl.h:402:95:402:95 | y | stl.h:403:79:403:79 | y | |
| stl.h:402:95:402:95 | y | stl.h:403:79:403:79 | y | |
| stl.h:402:95:402:95 | y | stl.h:403:79:403:79 | y | |
-| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | |
-| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | |
-| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | |
-| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | |
-| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | |
-| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | |
+| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT |
+| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT |
+| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT |
+| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT |
+| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT |
+| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT |
| stl.h:403:62:403:77 | call to forward | stl.h:403:3:403:82 | call to pair | TAINT |
| stl.h:403:62:403:77 | call to forward | stl.h:403:3:403:82 | call to pair | TAINT |
| stl.h:403:62:403:77 | call to forward | stl.h:403:3:403:82 | call to pair | TAINT |
@@ -3673,12 +3684,12 @@
| stl.h:403:79:403:79 | y | stl.h:403:3:403:82 | call to pair | TAINT |
| stl.h:403:79:403:79 | y | stl.h:403:3:403:82 | call to pair | TAINT |
| stl.h:403:79:403:79 | y | stl.h:403:3:403:82 | call to pair | TAINT |
-| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | |
-| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | |
-| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | |
-| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | |
-| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | |
-| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | |
+| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT |
+| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT |
+| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT |
+| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT |
+| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT |
+| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT |
| string.cpp:25:12:25:17 | call to source | string.cpp:29:7:29:7 | a | |
| string.cpp:26:16:26:20 | 123 | string.cpp:26:16:26:21 | call to basic_string | TAINT |
| string.cpp:26:16:26:21 | call to basic_string | string.cpp:30:7:30:7 | b | |
@@ -4231,13 +4242,13 @@
| string.cpp:407:9:407:10 | i6 | string.cpp:407:8:407:8 | call to operator* | TAINT |
| string.cpp:408:8:408:9 | i2 | string.cpp:408:3:408:9 | ... = ... | |
| string.cpp:408:8:408:9 | i2 | string.cpp:409:10:409:11 | i7 | |
-| string.cpp:409:10:409:11 | i7 | string.cpp:409:12:409:12 | call to operator+= | |
+| string.cpp:409:10:409:11 | i7 | string.cpp:409:12:409:12 | call to operator+= | TAINT |
| string.cpp:409:12:409:12 | call to operator+= | string.cpp:409:8:409:8 | call to operator* | TAINT |
| string.cpp:409:12:409:12 | ref arg call to operator+= | string.cpp:409:10:409:11 | ref arg i7 | TAINT |
| string.cpp:409:14:409:14 | 1 | string.cpp:409:10:409:11 | ref arg i7 | TAINT |
| string.cpp:410:8:410:9 | i2 | string.cpp:410:3:410:9 | ... = ... | |
| string.cpp:410:8:410:9 | i2 | string.cpp:411:10:411:11 | i8 | |
-| string.cpp:411:10:411:11 | i8 | string.cpp:411:12:411:12 | call to operator-= | |
+| string.cpp:411:10:411:11 | i8 | string.cpp:411:12:411:12 | call to operator-= | TAINT |
| string.cpp:411:12:411:12 | call to operator-= | string.cpp:411:8:411:8 | call to operator* | TAINT |
| string.cpp:411:12:411:12 | ref arg call to operator-= | string.cpp:411:10:411:11 | ref arg i8 | TAINT |
| string.cpp:411:14:411:14 | 1 | string.cpp:411:10:411:11 | ref arg i8 | TAINT |
@@ -4652,7 +4663,7 @@
| stringstream.cpp:33:7:33:9 | ref arg ss3 | stringstream.cpp:39:7:39:9 | ss3 | |
| stringstream.cpp:33:7:33:9 | ref arg ss3 | stringstream.cpp:44:7:44:9 | ss3 | |
| stringstream.cpp:33:7:33:9 | ss3 | stringstream.cpp:33:11:33:11 | call to operator<< | |
-| stringstream.cpp:33:11:33:11 | call to operator<< | stringstream.cpp:33:20:33:20 | call to operator<< | |
+| stringstream.cpp:33:11:33:11 | call to operator<< | stringstream.cpp:33:20:33:20 | call to operator<< | TAINT |
| stringstream.cpp:33:11:33:11 | ref arg call to operator<< | stringstream.cpp:33:7:33:9 | ref arg ss3 | TAINT |
| stringstream.cpp:33:14:33:18 | 123 | stringstream.cpp:33:7:33:9 | ref arg ss3 | TAINT |
| stringstream.cpp:33:14:33:18 | 123 | stringstream.cpp:33:11:33:11 | call to operator<< | TAINT |
@@ -4661,7 +4672,7 @@
| stringstream.cpp:34:7:34:9 | ref arg ss4 | stringstream.cpp:40:7:40:9 | ss4 | |
| stringstream.cpp:34:7:34:9 | ref arg ss4 | stringstream.cpp:45:7:45:9 | ss4 | |
| stringstream.cpp:34:7:34:9 | ss4 | stringstream.cpp:34:11:34:11 | call to operator<< | |
-| stringstream.cpp:34:11:34:11 | call to operator<< | stringstream.cpp:34:23:34:23 | call to operator<< | |
+| stringstream.cpp:34:11:34:11 | call to operator<< | stringstream.cpp:34:23:34:23 | call to operator<< | TAINT |
| stringstream.cpp:34:11:34:11 | ref arg call to operator<< | stringstream.cpp:34:7:34:9 | ref arg ss4 | TAINT |
| stringstream.cpp:34:14:34:19 | call to source | stringstream.cpp:34:7:34:9 | ref arg ss4 | TAINT |
| stringstream.cpp:34:14:34:19 | call to source | stringstream.cpp:34:11:34:11 | call to operator<< | TAINT |
@@ -4942,7 +4953,7 @@
| stringstream.cpp:147:7:147:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | |
| stringstream.cpp:147:7:147:9 | ss2 | stringstream.cpp:147:11:147:11 | call to operator>> | |
| stringstream.cpp:147:7:147:9 | ss2 | stringstream.cpp:147:14:147:15 | ref arg s3 | TAINT |
-| stringstream.cpp:147:11:147:11 | call to operator>> | stringstream.cpp:147:17:147:17 | call to operator>> | |
+| stringstream.cpp:147:11:147:11 | call to operator>> | stringstream.cpp:147:17:147:17 | call to operator>> | TAINT |
| stringstream.cpp:147:11:147:11 | call to operator>> | stringstream.cpp:147:20:147:21 | ref arg s4 | TAINT |
| stringstream.cpp:147:11:147:11 | ref arg call to operator>> | stringstream.cpp:147:7:147:9 | ref arg ss2 | TAINT |
| stringstream.cpp:147:14:147:15 | ref arg s3 | stringstream.cpp:150:7:150:8 | s3 | |
@@ -4974,7 +4985,7 @@
| stringstream.cpp:155:7:155:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | |
| stringstream.cpp:155:7:155:9 | ss2 | stringstream.cpp:155:11:155:11 | call to operator>> | |
| stringstream.cpp:155:7:155:9 | ss2 | stringstream.cpp:155:14:155:15 | ref arg b3 | TAINT |
-| stringstream.cpp:155:11:155:11 | call to operator>> | stringstream.cpp:155:17:155:17 | call to operator>> | |
+| stringstream.cpp:155:11:155:11 | call to operator>> | stringstream.cpp:155:17:155:17 | call to operator>> | TAINT |
| stringstream.cpp:155:11:155:11 | call to operator>> | stringstream.cpp:155:20:155:21 | ref arg b4 | TAINT |
| stringstream.cpp:155:11:155:11 | ref arg call to operator>> | stringstream.cpp:155:7:155:9 | ref arg ss2 | TAINT |
| stringstream.cpp:155:14:155:15 | ref arg b3 | stringstream.cpp:158:7:158:8 | b3 | |
@@ -5296,7 +5307,7 @@
| stringstream.cpp:245:15:245:17 | ss1 | stringstream.cpp:245:7:245:13 | call to getline | |
| stringstream.cpp:245:15:245:17 | ss1 | stringstream.cpp:245:20:245:21 | ref arg s6 | TAINT |
| stringstream.cpp:245:20:245:21 | ref arg s6 | stringstream.cpp:248:7:248:8 | s6 | |
-| stringstream.cpp:250:15:250:21 | call to getline | stringstream.cpp:250:7:250:13 | call to getline | |
+| stringstream.cpp:250:15:250:21 | call to getline | stringstream.cpp:250:7:250:13 | call to getline | TAINT |
| stringstream.cpp:250:15:250:21 | call to getline | stringstream.cpp:250:33:250:34 | ref arg s8 | TAINT |
| stringstream.cpp:250:15:250:21 | ref arg call to getline | stringstream.cpp:250:23:250:25 | ref arg ss2 | TAINT |
| stringstream.cpp:250:23:250:25 | ss2 | stringstream.cpp:250:15:250:21 | call to getline | |
@@ -5500,7 +5511,7 @@
| swap1.cpp:100:9:100:17 | ref arg call to move | swap1.cpp:103:10:103:10 | x | |
| swap1.cpp:100:19:100:19 | x | swap1.cpp:100:5:100:5 | ref arg y | TAINT |
| swap1.cpp:100:19:100:19 | x | swap1.cpp:100:7:100:7 | call to operator= | TAINT |
-| swap1.cpp:100:19:100:19 | x | swap1.cpp:100:9:100:17 | call to move | |
+| swap1.cpp:100:19:100:19 | x | swap1.cpp:100:9:100:17 | call to move | TAINT |
| swap1.cpp:108:23:108:31 | move_from | swap1.cpp:109:5:109:13 | move_from | |
| swap1.cpp:108:23:108:31 | move_from | swap1.cpp:111:10:111:18 | move_from | |
| swap1.cpp:108:23:108:31 | move_from | swap1.cpp:113:41:113:49 | move_from | |
@@ -5513,7 +5524,7 @@
| swap1.cpp:113:31:113:39 | call to move | swap1.cpp:113:31:113:51 | call to Class | TAINT |
| swap1.cpp:113:31:113:39 | ref arg call to move | swap1.cpp:113:41:113:49 | move_from [inner post update] | |
| swap1.cpp:113:31:113:51 | call to Class | swap1.cpp:115:10:115:16 | move_to | |
-| swap1.cpp:113:41:113:49 | move_from | swap1.cpp:113:31:113:39 | call to move | |
+| swap1.cpp:113:41:113:49 | move_from | swap1.cpp:113:31:113:39 | call to move | TAINT |
| swap1.cpp:113:41:113:49 | move_from | swap1.cpp:113:31:113:51 | call to Class | |
| swap1.cpp:120:23:120:23 | x | swap1.cpp:122:5:122:5 | x | |
| swap1.cpp:120:23:120:23 | x | swap1.cpp:124:10:124:10 | x | |
@@ -5547,7 +5558,7 @@
| swap1.cpp:142:5:142:5 | ref arg y | swap1.cpp:144:10:144:10 | y | |
| swap1.cpp:142:19:142:27 | ref arg call to move | swap1.cpp:142:29:142:29 | x [inner post update] | |
| swap1.cpp:142:19:142:27 | ref arg call to move | swap1.cpp:145:10:145:10 | x | |
-| swap1.cpp:142:29:142:29 | x | swap1.cpp:142:19:142:27 | call to move | |
+| swap1.cpp:142:29:142:29 | x | swap1.cpp:142:19:142:27 | call to move | TAINT |
| 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 | |
@@ -5679,7 +5690,7 @@
| swap2.cpp:100:9:100:17 | ref arg call to move | swap2.cpp:103:10:103:10 | x | |
| swap2.cpp:100:19:100:19 | x | swap2.cpp:100:5:100:5 | ref arg y | TAINT |
| swap2.cpp:100:19:100:19 | x | swap2.cpp:100:7:100:7 | call to operator= | TAINT |
-| swap2.cpp:100:19:100:19 | x | swap2.cpp:100:9:100:17 | call to move | |
+| swap2.cpp:100:19:100:19 | x | swap2.cpp:100:9:100:17 | call to move | TAINT |
| swap2.cpp:108:23:108:31 | move_from | swap2.cpp:109:5:109:13 | move_from | |
| swap2.cpp:108:23:108:31 | move_from | swap2.cpp:111:10:111:18 | move_from | |
| swap2.cpp:108:23:108:31 | move_from | swap2.cpp:113:41:113:49 | move_from | |
@@ -5692,7 +5703,7 @@
| swap2.cpp:113:31:113:39 | call to move | swap2.cpp:113:31:113:51 | call to Class | TAINT |
| swap2.cpp:113:31:113:39 | ref arg call to move | swap2.cpp:113:41:113:49 | move_from [inner post update] | |
| swap2.cpp:113:31:113:51 | call to Class | swap2.cpp:115:10:115:16 | move_to | |
-| swap2.cpp:113:41:113:49 | move_from | swap2.cpp:113:31:113:39 | call to move | |
+| swap2.cpp:113:41:113:49 | move_from | swap2.cpp:113:31:113:39 | call to move | TAINT |
| swap2.cpp:113:41:113:49 | move_from | swap2.cpp:113:31:113:51 | call to Class | |
| swap2.cpp:120:23:120:23 | x | swap2.cpp:122:5:122:5 | x | |
| swap2.cpp:120:23:120:23 | x | swap2.cpp:124:10:124:10 | x | |
@@ -5726,7 +5737,7 @@
| swap2.cpp:142:5:142:5 | ref arg y | swap2.cpp:144:10:144:10 | y | |
| swap2.cpp:142:19:142:27 | ref arg call to move | swap2.cpp:142:29:142:29 | x [inner post update] | |
| swap2.cpp:142:19:142:27 | ref arg call to move | swap2.cpp:145:10:145:10 | x | |
-| swap2.cpp:142:29:142:29 | x | swap2.cpp:142:19:142:27 | call to move | |
+| swap2.cpp:142:29:142:29 | x | swap2.cpp:142:19:142:27 | call to move | TAINT |
| 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 | |
@@ -7950,7 +7961,7 @@
| vector.cpp:527:9:527:10 | ref arg it | vector.cpp:529:9:529:10 | it | |
| vector.cpp:527:9:527:10 | ref arg it | vector.cpp:530:3:530:4 | it | |
| vector.cpp:527:9:527:10 | ref arg it | vector.cpp:531:9:531:10 | it | |
-| vector.cpp:528:3:528:4 | it | vector.cpp:528:6:528:6 | call to operator+= | |
+| vector.cpp:528:3:528:4 | it | vector.cpp:528:6:528:6 | call to operator+= | TAINT |
| vector.cpp:528:3:528:4 | ref arg it | vector.cpp:529:9:529:10 | it | |
| vector.cpp:528:3:528:4 | ref arg it | vector.cpp:530:3:530:4 | it | |
| vector.cpp:528:3:528:4 | ref arg it | vector.cpp:531:9:531:10 | it | |
@@ -7958,7 +7969,7 @@
| vector.cpp:529:9:529:10 | it | vector.cpp:529:8:529:8 | call to operator* | TAINT |
| vector.cpp:529:9:529:10 | ref arg it | vector.cpp:530:3:530:4 | it | |
| vector.cpp:529:9:529:10 | ref arg it | vector.cpp:531:9:531:10 | it | |
-| vector.cpp:530:3:530:4 | it | vector.cpp:530:6:530:6 | call to operator+= | |
+| vector.cpp:530:3:530:4 | it | vector.cpp:530:6:530:6 | call to operator+= | TAINT |
| vector.cpp:530:3:530:4 | ref arg it | vector.cpp:531:9:531:10 | it | |
| vector.cpp:530:9:530:14 | call to source | vector.cpp:530:3:530:4 | ref arg it | TAINT |
| vector.cpp:531:9:531:10 | it | vector.cpp:531:8:531:8 | call to operator* | TAINT |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp
index 39cff39e3edc..28e44779009b 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp
@@ -9,7 +9,7 @@ template void sink(std::unique_ptr&);
void test_make_shared() {
std::shared_ptr p = std::make_shared(source());
- sink(*p); // $ ast MISSING: ir
+ sink(*p); // $ ast,ir
sink(p); // $ ast,ir
}
@@ -21,7 +21,7 @@ void test_make_shared_array() {
void test_make_unique() {
std::unique_ptr p = std::make_unique(source());
- sink(*p); // $ ast MISSING: ir
+ sink(*p); // $ ast,ir
sink(p); // $ ast,ir
}
@@ -101,7 +101,7 @@ void taint_x(A* pa) {
void reverse_taint_smart_pointer() {
std::unique_ptr p = std::unique_ptr(new A);
taint_x(p.get());
- sink(p->x); // $ ast MISSING: ir
+ sink(p->x); // $ ast,ir
}
struct C {
diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected
index 44d379e46fef..040dbe0bfb54 100644
--- a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected
+++ b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected
@@ -5925,7 +5925,7 @@ ir.cpp:
# 705| getStmt(0): [ReturnStmt] return ...
# 705| getExpr(): [ConditionalExpr] ... ? ... : ...
# 705| Type = [UnknownType] unknown
-# 705| ValueCategory = prvalue
+# 705| ValueCategory = prvalue(load)
# 705| getCondition(): [LTExpr] ... < ...
# 705| Type = [UnknownType] unknown
# 705| ValueCategory = prvalue
@@ -11365,6 +11365,225 @@ perf-regression.cpp:
# 12| Type = [IntType] int
# 12| Value = [Literal] 0
# 12| ValueCategory = prvalue
+smart_ptr.cpp:
+# 8| [TopLevelFunction] void unique_ptr_arg(std::unique_ptr>)
+# 8| :
+# 8| getParameter(0): [Parameter] up
+# 8| Type = [ClassTemplateInstantiation] unique_ptr>
+# 10| [TopLevelFunction] void call_unique_ptr_arg(int*)
+# 10| :
+# 10| getParameter(0): [Parameter] p
+# 10| Type = [IntPointerType] int *
+# 10| getEntryPoint(): [BlockStmt] { ... }
+# 11| getStmt(0): [DeclStmt] declaration
+# 11| getDeclarationEntry(0): [VariableDeclarationEntry] definition of up
+# 11| Type = [ClassTemplateInstantiation] unique_ptr>
+# 11| getVariable().getInitializer(): [Initializer] initializer for up
+# 11| getExpr(): [ConstructorCall] call to unique_ptr
+# 11| Type = [VoidType] void
+# 11| ValueCategory = prvalue
+# 11| getArgument(0): [VariableAccess] p
+# 11| Type = [IntPointerType] int *
+# 11| ValueCategory = prvalue(load)
+# 12| getStmt(1): [ExprStmt] ExprStmt
+# 12| getExpr(): [FunctionCall] call to unique_ptr_arg
+# 12| Type = [VoidType] void
+# 12| ValueCategory = prvalue
+# 12| getArgument(0): [FunctionCall] call to move
+# 12| Type = [RValueReferenceType] type &&
+# 12| ValueCategory = prvalue
+# 12| getArgument(0): [VariableAccess] up
+# 12| Type = [ClassTemplateInstantiation] unique_ptr>
+# 12| ValueCategory = lvalue
+# 12| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to)
+# 12| Type = [LValueReferenceType] unique_ptr> &
+# 12| ValueCategory = prvalue
+# 12| getArgument(0).getFullyConverted(): [TemporaryObjectExpr] temporary object
+# 12| Type = [ClassTemplateInstantiation] unique_ptr>
+# 12| ValueCategory = lvalue
+# 12| getExpr(): [ReferenceDereferenceExpr] (reference dereference)
+# 12| Type = [CTypedefType,NestedTypedefType] type
+# 12| ValueCategory = prvalue(load)
+# 13| getStmt(2): [ReturnStmt] return ...
+# 15| [TopLevelFunction] void shared_ptr_arg(std::shared_ptr)
+# 15| :
+# 15| getParameter(0): [Parameter] sp
+# 15| Type = [ClassTemplateInstantiation] shared_ptr
+# 17| [TopLevelFunction] void call_shared_ptr_arg(float*)
+# 17| :
+# 17| getParameter(0): [Parameter] p
+# 17| Type = [PointerType] float *
+# 17| getEntryPoint(): [BlockStmt] { ... }
+# 18| getStmt(0): [DeclStmt] declaration
+# 18| getDeclarationEntry(0): [VariableDeclarationEntry] definition of sp
+# 18| Type = [ClassTemplateInstantiation] shared_ptr
+# 18| getVariable().getInitializer(): [Initializer] initializer for sp
+# 18| getExpr(): [ConstructorCall] call to shared_ptr
+# 18| Type = [VoidType] void
+# 18| ValueCategory = prvalue
+# 18| getArgument(0): [VariableAccess] p
+# 18| Type = [PointerType] float *
+# 18| ValueCategory = prvalue(load)
+# 19| getStmt(1): [ExprStmt] ExprStmt
+# 19| getExpr(): [FunctionCall] call to shared_ptr_arg
+# 19| Type = [VoidType] void
+# 19| ValueCategory = prvalue
+# 19| getArgument(0): [ConstructorCall] call to shared_ptr
+# 19| Type = [VoidType] void
+# 19| ValueCategory = prvalue
+# 19| getArgument(0): [VariableAccess] sp
+# 19| Type = [ClassTemplateInstantiation] shared_ptr
+# 19| ValueCategory = lvalue
+# 19| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to)
+# 19| Type = [LValueReferenceType] const shared_ptr &
+# 19| ValueCategory = prvalue
+# 19| getExpr(): [CStyleCast] (const shared_ptr)...
+# 19| Conversion = [GlvalueConversion] glvalue conversion
+# 19| Type = [SpecifiedType] const shared_ptr
+# 19| ValueCategory = lvalue
+# 19| getArgument(0).getFullyConverted(): [TemporaryObjectExpr] temporary object
+# 19| Type = [ClassTemplateInstantiation] shared_ptr
+# 19| ValueCategory = lvalue
+# 20| getStmt(2): [ReturnStmt] return ...
+# 22| [TopLevelFunction] void shared_ptr_const_int(std::shared_ptr)
+# 22| :
+# 22| getParameter(0): [Parameter] (unnamed parameter 0)
+# 22| Type = [ClassTemplateInstantiation] shared_ptr
+# 23| [TopLevelFunction] void shared_ptr_const_int_ptr(std::shared_ptr)
+# 23| :
+# 23| getParameter(0): [Parameter] (unnamed parameter 0)
+# 23| Type = [ClassTemplateInstantiation] shared_ptr
+# 24| [TopLevelFunction] void shared_ptr_shared_ptr_const_int(std::shared_ptr>)
+# 24| :
+# 24| getParameter(0): [Parameter] (unnamed parameter 0)
+# 24| Type = [ClassTemplateInstantiation] shared_ptr>
+# 25| [TopLevelFunction] void shared_ptr_const_shared_ptr_int(std::shared_ptr const>)
+# 25| :
+# 25| getParameter(0): [Parameter] (unnamed parameter 0)
+# 25| Type = [ClassTemplateInstantiation] shared_ptr>
+# 26| [TopLevelFunction] void shared_ptr_const_shared_ptr_const_int(std::shared_ptr const>)
+# 26| :
+# 26| getParameter(0): [Parameter] (unnamed parameter 0)
+# 26| Type = [ClassTemplateInstantiation] shared_ptr>
+# 28| [TopLevelFunction] void call_shared_ptr_consts()
+# 28| :
+# 28| getEntryPoint(): [BlockStmt] { ... }
+# 29| getStmt(0): [DeclStmt] declaration
+# 29| getDeclarationEntry(0): [VariableDeclarationEntry] definition of sp_const_int
+# 29| Type = [ClassTemplateInstantiation] shared_ptr
+# 31| getStmt(1): [ExprStmt] ExprStmt
+# 31| getExpr(): [FunctionCall] call to shared_ptr_const_int
+# 31| Type = [VoidType] void
+# 31| ValueCategory = prvalue
+# 31| getArgument(0): [ConstructorCall] call to shared_ptr
+# 31| Type = [VoidType] void
+# 31| ValueCategory = prvalue
+# 31| getArgument(0): [VariableAccess] sp_const_int
+# 31| Type = [ClassTemplateInstantiation] shared_ptr
+# 31| ValueCategory = lvalue
+# 31| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to)
+# 31| Type = [LValueReferenceType] const shared_ptr &
+# 31| ValueCategory = prvalue
+# 31| getExpr(): [CStyleCast] (const shared_ptr)...
+# 31| Conversion = [GlvalueConversion] glvalue conversion
+# 31| Type = [SpecifiedType] const shared_ptr
+# 31| ValueCategory = lvalue
+# 31| getArgument(0).getFullyConverted(): [TemporaryObjectExpr] temporary object
+# 31| Type = [ClassTemplateInstantiation] shared_ptr
+# 31| ValueCategory = lvalue
+# 33| getStmt(2): [DeclStmt] declaration
+# 33| getDeclarationEntry(0): [VariableDeclarationEntry] definition of sp_const_int_pointer
+# 33| Type = [ClassTemplateInstantiation] shared_ptr
+# 35| getStmt(3): [ExprStmt] ExprStmt
+# 35| getExpr(): [FunctionCall] call to shared_ptr_const_int_ptr
+# 35| Type = [VoidType] void
+# 35| ValueCategory = prvalue
+# 35| getArgument(0): [ConstructorCall] call to shared_ptr
+# 35| Type = [VoidType] void
+# 35| ValueCategory = prvalue
+# 35| getArgument(0): [VariableAccess] sp_const_int_pointer
+# 35| Type = [ClassTemplateInstantiation] shared_ptr
+# 35| ValueCategory = lvalue
+# 35| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to)
+# 35| Type = [LValueReferenceType] const shared_ptr &
+# 35| ValueCategory = prvalue
+# 35| getExpr(): [CStyleCast] (const shared_ptr)...
+# 35| Conversion = [GlvalueConversion] glvalue conversion
+# 35| Type = [SpecifiedType] const shared_ptr
+# 35| ValueCategory = lvalue
+# 35| getArgument(0).getFullyConverted(): [TemporaryObjectExpr] temporary object
+# 35| Type = [ClassTemplateInstantiation] shared_ptr
+# 35| ValueCategory = lvalue
+# 37| getStmt(4): [DeclStmt] declaration
+# 37| getDeclarationEntry(0): [VariableDeclarationEntry] definition of sp_sp_const_int
+# 37| Type = [ClassTemplateInstantiation] shared_ptr>
+# 39| getStmt(5): [ExprStmt] ExprStmt
+# 39| getExpr(): [FunctionCall] call to shared_ptr_shared_ptr_const_int
+# 39| Type = [VoidType] void
+# 39| ValueCategory = prvalue
+# 39| getArgument(0): [ConstructorCall] call to shared_ptr
+# 39| Type = [VoidType] void
+# 39| ValueCategory = prvalue
+# 39| getArgument(0): [VariableAccess] sp_sp_const_int
+# 39| Type = [ClassTemplateInstantiation] shared_ptr>
+# 39| ValueCategory = lvalue
+# 39| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to)
+# 39| Type = [LValueReferenceType] const shared_ptr> &
+# 39| ValueCategory = prvalue
+# 39| getExpr(): [CStyleCast] (const shared_ptr>)...
+# 39| Conversion = [GlvalueConversion] glvalue conversion
+# 39| Type = [SpecifiedType] const shared_ptr>
+# 39| ValueCategory = lvalue
+# 39| getArgument(0).getFullyConverted(): [TemporaryObjectExpr] temporary object
+# 39| Type = [ClassTemplateInstantiation] shared_ptr>
+# 39| ValueCategory = lvalue
+# 41| getStmt(6): [DeclStmt] declaration
+# 41| getDeclarationEntry(0): [VariableDeclarationEntry] definition of sp_const_sp_int
+# 41| Type = [ClassTemplateInstantiation] shared_ptr>
+# 43| getStmt(7): [ExprStmt] ExprStmt
+# 43| getExpr(): [FunctionCall] call to shared_ptr_const_shared_ptr_int
+# 43| Type = [VoidType] void
+# 43| ValueCategory = prvalue
+# 43| getArgument(0): [ConstructorCall] call to shared_ptr
+# 43| Type = [VoidType] void
+# 43| ValueCategory = prvalue
+# 43| getArgument(0): [VariableAccess] sp_const_sp_int
+# 43| Type = [ClassTemplateInstantiation] shared_ptr>
+# 43| ValueCategory = lvalue
+# 43| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to)
+# 43| Type = [LValueReferenceType] const shared_ptr> &
+# 43| ValueCategory = prvalue
+# 43| getExpr(): [CStyleCast] (const shared_ptr>)...
+# 43| Conversion = [GlvalueConversion] glvalue conversion
+# 43| Type = [SpecifiedType] const shared_ptr>
+# 43| ValueCategory = lvalue
+# 43| getArgument(0).getFullyConverted(): [TemporaryObjectExpr] temporary object
+# 43| Type = [ClassTemplateInstantiation] shared_ptr>
+# 43| ValueCategory = lvalue
+# 45| getStmt(8): [DeclStmt] declaration
+# 45| getDeclarationEntry(0): [VariableDeclarationEntry] definition of sp_const_sp_const_int
+# 45| Type = [ClassTemplateInstantiation] shared_ptr>
+# 47| getStmt(9): [ExprStmt] ExprStmt
+# 47| getExpr(): [FunctionCall] call to shared_ptr_const_shared_ptr_const_int
+# 47| Type = [VoidType] void
+# 47| ValueCategory = prvalue
+# 47| getArgument(0): [ConstructorCall] call to shared_ptr
+# 47| Type = [VoidType] void
+# 47| ValueCategory = prvalue
+# 47| getArgument(0): [VariableAccess] sp_const_sp_const_int
+# 47| Type = [ClassTemplateInstantiation] shared_ptr>
+# 47| ValueCategory = lvalue
+# 47| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to)
+# 47| Type = [LValueReferenceType] const shared_ptr> &
+# 47| ValueCategory = prvalue
+# 47| getExpr(): [CStyleCast] (const shared_ptr>)...
+# 47| Conversion = [GlvalueConversion] glvalue conversion
+# 47| Type = [SpecifiedType] const shared_ptr>
+# 47| ValueCategory = lvalue
+# 47| getArgument(0).getFullyConverted(): [TemporaryObjectExpr] temporary object
+# 47| Type = [ClassTemplateInstantiation] shared_ptr>
+# 47| ValueCategory = lvalue
+# 48| getStmt(10): [ReturnStmt] return ...
struct_init.cpp:
# 1| [TopLevelFunction] int handler1(void*)
# 1| :
diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.ql b/cpp/ql/test/library-tests/ir/ir/PrintAST.ql
new file mode 100644
index 000000000000..e107c80de02a
--- /dev/null
+++ b/cpp/ql/test/library-tests/ir/ir/PrintAST.ql
@@ -0,0 +1,11 @@
+/**
+ * @kind graph
+ */
+
+private import cpp
+private import semmle.code.cpp.PrintAST
+private import PrintConfig
+
+private class PrintConfig extends PrintASTConfiguration {
+ override predicate shouldPrintFunction(Function func) { shouldDumpFunction(func) }
+}
diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.qlref b/cpp/ql/test/library-tests/ir/ir/PrintAST.qlref
deleted file mode 100644
index 6fcb30ac7a6e..000000000000
--- a/cpp/ql/test/library-tests/ir/ir/PrintAST.qlref
+++ /dev/null
@@ -1 +0,0 @@
-semmle/code/cpp/PrintAST.ql
\ No newline at end of file
diff --git a/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll b/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll
new file mode 100644
index 000000000000..3253a1196b60
--- /dev/null
+++ b/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll
@@ -0,0 +1,10 @@
+private import cpp
+
+/**
+ * Holds if the AST or IR for the specified function should be printed in the test output.
+ *
+ * This predicate excludes functions defined in standard headers.
+ */
+predicate shouldDumpFunction(Function func) {
+ not func.getLocation().getFile().getAbsolutePath().regexpMatch(".*/include/[^/]+")
+}
diff --git a/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected b/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected
index 31e5b01229ce..57f16b48a1ab 100644
--- a/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected
+++ b/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected
@@ -6,6 +6,7 @@ missingOperandType
duplicateChiOperand
sideEffectWithoutPrimary
instructionWithoutSuccessor
+| ../../../include/memory.h:68:25:68:33 | CopyValue: (reference to) | Instruction 'CopyValue: (reference to)' has no successors in function '$@'. | ../../../include/memory.h:67:5:67:5 | void std::unique_ptr>::~unique_ptr() | void std::unique_ptr>::~unique_ptr() |
ambiguousSuccessors
unexplainedLoop
unnecessaryPhiInstruction
diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected
index e008e2de6596..3746b0e2c6ca 100644
--- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected
+++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected
@@ -7903,6 +7903,180 @@ perf-regression.cpp:
# 9| v9_6(void) = AliasedUse : ~m?
# 9| v9_7(void) = ExitFunction :
+smart_ptr.cpp:
+# 10| void call_unique_ptr_arg(int*)
+# 10| Block 0
+# 10| v10_1(void) = EnterFunction :
+# 10| mu10_2(unknown) = AliasedDefinition :
+# 10| mu10_3(unknown) = InitializeNonLocal :
+# 10| r10_4(glval) = VariableAddress[p] :
+# 10| mu10_5(int *) = InitializeParameter[p] : &:r10_4
+# 10| r10_6(int *) = Load[p] : &:r10_4, ~m?
+# 10| mu10_7(unknown) = InitializeIndirection[p] : &:r10_6
+# 11| r11_1(glval>>) = VariableAddress[up] :
+# 11| mu11_2(unique_ptr>) = Uninitialized[up] : &:r11_1
+# 11| r11_3(glval) = FunctionAddress[unique_ptr] :
+# 11| r11_4(glval) = VariableAddress[p] :
+# 11| r11_5(int *) = Load[p] : &:r11_4, ~m?
+# 11| v11_6(void) = Call[unique_ptr] : func:r11_3, this:r11_1, 0:r11_5
+# 11| mu11_7(unknown) = ^CallSideEffect : ~m?
+# 11| mu11_8(unique_ptr>) = ^IndirectMustWriteSideEffect[-1] : &:r11_1
+# 12| r12_1(glval) = FunctionAddress[unique_ptr_arg] :
+# 12| r12_2(glval>>) = VariableAddress[#temp12:20] :
+# 12| r12_3(glval) = FunctionAddress[move] :
+# 12| r12_4(glval>>) = VariableAddress[up] :
+# 12| r12_5(unique_ptr> &) = CopyValue : r12_4
+# 12| r12_6(unique_ptr> &&) = Call[move] : func:r12_3, 0:r12_5
+# 12| r12_7(unique_ptr>) = Load[?] : &:r12_6, ~m?
+# 12| mu12_8(unique_ptr>) = Store[#temp12:20] : &:r12_2, r12_7
+# 12| r12_9(unique_ptr>) = Load[#temp12:20] : &:r12_2, ~m?
+# 12| v12_10(void) = Call[unique_ptr_arg] : func:r12_1, 0:r12_9
+# 12| mu12_11(unknown) = ^CallSideEffect : ~m?
+# 12| v12_12(void) = ^BufferReadSideEffect[0] : &:r12_9, ~m?
+# 12| mu12_13(unknown) = ^BufferMayWriteSideEffect[0] : &:r12_9
+# 13| v13_1(void) = NoOp :
+# 10| v10_8(void) = ReturnIndirection[p] : &:r10_6, ~m?
+# 10| v10_9(void) = ReturnVoid :
+# 10| v10_10(void) = AliasedUse : ~m?
+# 10| v10_11(void) = ExitFunction :
+
+# 17| void call_shared_ptr_arg(float*)
+# 17| Block 0
+# 17| v17_1(void) = EnterFunction :
+# 17| mu17_2(unknown) = AliasedDefinition :
+# 17| mu17_3(unknown) = InitializeNonLocal :
+# 17| r17_4(glval) = VariableAddress[p] :
+# 17| mu17_5(float *) = InitializeParameter[p] : &:r17_4
+# 17| r17_6(float *) = Load[p] : &:r17_4, ~m?
+# 17| mu17_7(unknown) = InitializeIndirection[p] : &:r17_6
+# 18| r18_1(glval>) = VariableAddress[sp] :
+# 18| mu18_2(shared_ptr) = Uninitialized[sp] : &:r18_1
+# 18| r18_3(glval) = FunctionAddress[shared_ptr] :
+# 18| r18_4(glval) = VariableAddress[p] :
+# 18| r18_5(float *) = Load[p] : &:r18_4, ~m?
+# 18| v18_6(void) = Call[shared_ptr] : func:r18_3, this:r18_1, 0:r18_5
+# 18| mu18_7(unknown) = ^CallSideEffect : ~m?
+# 18| mu18_8(shared_ptr) = ^IndirectMustWriteSideEffect[-1] : &:r18_1
+# 19| r19_1(glval) = FunctionAddress[shared_ptr_arg] :
+# 19| r19_2(glval>) = VariableAddress[#temp19:20] :
+# 19| mu19_3(shared_ptr) = Uninitialized[#temp19:20] : &:r19_2
+# 19| r19_4(glval) = FunctionAddress[shared_ptr] :
+# 19| r19_5(glval>) = VariableAddress[sp] :
+# 19| r19_6(glval>) = Convert : r19_5
+# 19| r19_7(shared_ptr &) = CopyValue : r19_6
+# 19| v19_8(void) = Call[shared_ptr] : func:r19_4, this:r19_2, 0:r19_7
+# 19| mu19_9(unknown) = ^CallSideEffect : ~m?
+# 19| mu19_10(shared_ptr) = ^IndirectMustWriteSideEffect[-1] : &:r19_2
+# 19| v19_11(void) = ^IndirectReadSideEffect[0] : &:r19_7, ~m?
+# 19| r19_12(shared_ptr) = Load[#temp19:20] : &:r19_2, ~m?
+# 19| v19_13(void) = Call[shared_ptr_arg] : func:r19_1, 0:r19_12
+# 19| mu19_14(unknown) = ^CallSideEffect : ~m?
+# 19| v19_15(void) = ^BufferReadSideEffect[0] : &:r19_12, ~m?
+# 19| mu19_16(unknown) = ^BufferMayWriteSideEffect[0] : &:r19_12
+# 20| v20_1(void) = NoOp :
+# 17| v17_8(void) = ReturnIndirection[p] : &:r17_6, ~m?
+# 17| v17_9(void) = ReturnVoid :
+# 17| v17_10(void) = AliasedUse : ~m?
+# 17| v17_11(void) = ExitFunction :
+
+# 28| void call_shared_ptr_consts()
+# 28| Block 0
+# 28| v28_1(void) = EnterFunction :
+# 28| mu28_2(unknown) = AliasedDefinition :
+# 28| mu28_3(unknown) = InitializeNonLocal :
+# 29| r29_1(glval>) = VariableAddress[sp_const_int] :
+# 29| mu29_2(shared_ptr) = Uninitialized[sp_const_int] : &:r29_1
+# 31| r31_1(glval) = FunctionAddress[shared_ptr_const_int] :
+# 31| r31_2(glval>) = VariableAddress[#temp31:26] :
+# 31| mu31_3(shared_ptr) = Uninitialized[#temp31:26] : &:r31_2
+# 31| r31_4(glval) = FunctionAddress[shared_ptr] :
+# 31| r31_5(glval>) = VariableAddress[sp_const_int] :
+# 31| r31_6(glval>) = Convert : r31_5
+# 31| r31_7(shared_ptr &) = CopyValue : r31_6
+# 31| v31_8(void) = Call[shared_ptr] : func:r31_4, this:r31_2, 0:r31_7
+# 31| mu31_9(unknown) = ^CallSideEffect : ~m?
+# 31| mu31_10(shared_ptr) = ^IndirectMustWriteSideEffect[-1] : &:r31_2
+# 31| v31_11(void) = ^IndirectReadSideEffect[0] : &:r31_7, ~m?
+# 31| r31_12(shared_ptr) = Load[#temp31:26] : &:r31_2, ~m?
+# 31| v31_13(void) = Call[shared_ptr_const_int] : func:r31_1, 0:r31_12
+# 31| mu31_14(unknown) = ^CallSideEffect : ~m?
+# 31| v31_15(void) = ^BufferReadSideEffect[0] : &:r31_12, ~m?
+# 33| r33_1(glval>) = VariableAddress[sp_const_int_pointer] :
+# 33| mu33_2(shared_ptr) = Uninitialized[sp_const_int_pointer] : &:r33_1
+# 35| r35_1(glval) = FunctionAddress[shared_ptr_const_int_ptr] :
+# 35| r35_2(glval>) = VariableAddress[#temp35:30] :
+# 35| mu35_3(shared_ptr) = Uninitialized[#temp35:30] : &:r35_2
+# 35| r35_4(glval) = FunctionAddress[shared_ptr] :
+# 35| r35_5(glval>) = VariableAddress[sp_const_int_pointer] :
+# 35| r35_6(glval>) = Convert : r35_5
+# 35| r35_7(shared_ptr &) = CopyValue : r35_6
+# 35| v35_8(void) = Call[shared_ptr] : func:r35_4, this:r35_2, 0:r35_7
+# 35| mu35_9(unknown) = ^CallSideEffect : ~m?
+# 35| mu35_10(shared_ptr) = ^IndirectMustWriteSideEffect[-1] : &:r35_2
+# 35| v35_11(void) = ^IndirectReadSideEffect[0] : &:r35_7, ~m?
+# 35| r35_12(shared_ptr) = Load[#temp35:30] : &:r35_2, ~m?
+# 35| v35_13(void) = Call[shared_ptr_const_int_ptr] : func:r35_1, 0:r35_12
+# 35| mu35_14(unknown) = ^CallSideEffect : ~m?
+# 35| v35_15(void) = ^BufferReadSideEffect[0] : &:r35_12, ~m?
+# 35| mu35_16(unknown) = ^BufferMayWriteSideEffect[0] : &:r35_12
+# 37| r37_1(glval>>) = VariableAddress[sp_sp_const_int] :
+# 37| mu37_2(shared_ptr>) = Uninitialized[sp_sp_const_int] : &:r37_1
+# 39| r39_1(glval) = FunctionAddress[shared_ptr_shared_ptr_const_int] :
+# 39| r39_2(glval>>) = VariableAddress[#temp39:37] :
+# 39| mu39_3(shared_ptr>) = Uninitialized[#temp39:37] : &:r39_2
+# 39| r39_4(glval) = FunctionAddress[shared_ptr] :
+# 39| r39_5(glval>>) = VariableAddress[sp_sp_const_int] :
+# 39| r39_6(glval>>) = Convert : r39_5
+# 39| r39_7(shared_ptr> &) = CopyValue : r39_6
+# 39| v39_8(void) = Call[shared_ptr] : func:r39_4, this:r39_2, 0:r39_7
+# 39| mu39_9(unknown) = ^CallSideEffect : ~m?
+# 39| mu39_10(shared_ptr>) = ^IndirectMustWriteSideEffect[-1] : &:r39_2
+# 39| v39_11(void) = ^IndirectReadSideEffect[0] : &:r39_7, ~m?
+# 39| r39_12(shared_ptr>) = Load[#temp39:37] : &:r39_2, ~m?
+# 39| v39_13(void) = Call[shared_ptr_shared_ptr_const_int] : func:r39_1, 0:r39_12
+# 39| mu39_14(unknown) = ^CallSideEffect : ~m?
+# 39| v39_15(void) = ^BufferReadSideEffect[0] : &:r39_12, ~m?
+# 39| mu39_16(unknown) = ^BufferMayWriteSideEffect[0] : &:r39_12
+# 41| r41_1(glval>>) = VariableAddress[sp_const_sp_int] :
+# 41| mu41_2(shared_ptr>) = Uninitialized[sp_const_sp_int] : &:r41_1
+# 43| r43_1(glval) = FunctionAddress[shared_ptr_const_shared_ptr_int] :
+# 43| r43_2(glval>>) = VariableAddress[#temp43:37] :
+# 43| mu43_3(shared_ptr>) = Uninitialized[#temp43:37] : &:r43_2
+# 43| r43_4(glval) = FunctionAddress[shared_ptr] :
+# 43| r43_5(glval>>) = VariableAddress[sp_const_sp_int] :
+# 43| r43_6(glval>>) = Convert : r43_5
+# 43| r43_7(shared_ptr> &) = CopyValue : r43_6
+# 43| v43_8(void) = Call[shared_ptr] : func:r43_4, this:r43_2, 0:r43_7
+# 43| mu43_9(unknown) = ^CallSideEffect : ~m?
+# 43| mu43_10(shared_ptr>) = ^IndirectMustWriteSideEffect[-1] : &:r43_2
+# 43| v43_11(void) = ^IndirectReadSideEffect[0] : &:r43_7, ~m?
+# 43| r43_12(shared_ptr>) = Load[#temp43:37] : &:r43_2, ~m?
+# 43| v43_13(void) = Call[shared_ptr_const_shared_ptr_int] : func:r43_1, 0:r43_12
+# 43| mu43_14(unknown) = ^CallSideEffect : ~m?
+# 43| v43_15(void) = ^BufferReadSideEffect[0] : &:r43_12, ~m?
+# 43| mu43_16(unknown) = ^BufferMayWriteSideEffect[0] : &:r43_12
+# 45| r45_1(glval>>) = VariableAddress[sp_const_sp_const_int] :
+# 45| mu45_2(shared_ptr>) = Uninitialized[sp_const_sp_const_int] : &:r45_1
+# 47| r47_1(glval) = FunctionAddress[shared_ptr_const_shared_ptr_const_int] :
+# 47| r47_2(glval>>) = VariableAddress[#temp47:43] :
+# 47| mu47_3(shared_ptr>) = Uninitialized[#temp47:43] : &:r47_2
+# 47| r47_4(glval) = FunctionAddress[shared_ptr] :
+# 47| r47_5(glval>>) = VariableAddress[sp_const_sp_const_int] :
+# 47| r47_6(glval>>) = Convert : r47_5
+# 47| r47_7(shared_ptr> &) = CopyValue : r47_6
+# 47| v47_8(void) = Call[shared_ptr] : func:r47_4, this:r47_2, 0:r47_7
+# 47| mu47_9(unknown) = ^CallSideEffect : ~m?
+# 47| mu47_10(shared_ptr>) = ^IndirectMustWriteSideEffect[-1] : &:r47_2
+# 47| v47_11(void) = ^IndirectReadSideEffect[0] : &:r47_7, ~m?
+# 47| r47_12(shared_ptr>) = Load[#temp47:43] : &:r47_2, ~m?
+# 47| v47_13(void) = Call[shared_ptr_const_shared_ptr_const_int] : func:r47_1, 0:r47_12
+# 47| mu47_14(unknown) = ^CallSideEffect : ~m?
+# 47| v47_15(void) = ^BufferReadSideEffect[0] : &:r47_12, ~m?
+# 48| v48_1(void) = NoOp :
+# 28| v28_4(void) = ReturnVoid :
+# 28| v28_5(void) = AliasedUse : ~m?
+# 28| v28_6(void) = ExitFunction :
+
struct_init.cpp:
# 16| void let_info_escape(Info*)
# 16| Block 0
diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.ql b/cpp/ql/test/library-tests/ir/ir/raw_ir.ql
new file mode 100644
index 000000000000..a0ebe4d2bddb
--- /dev/null
+++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.ql
@@ -0,0 +1,11 @@
+/**
+ * @kind graph
+ */
+
+private import cpp
+private import semmle.code.cpp.ir.implementation.raw.PrintIR
+private import PrintConfig
+
+private class PrintConfig extends PrintIRConfiguration {
+ override predicate shouldPrintFunction(Function func) { shouldDumpFunction(func) }
+}
diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.qlref b/cpp/ql/test/library-tests/ir/ir/raw_ir.qlref
deleted file mode 100644
index fa4c4bda9032..000000000000
--- a/cpp/ql/test/library-tests/ir/ir/raw_ir.qlref
+++ /dev/null
@@ -1 +0,0 @@
-semmle/code/cpp/ir/implementation/raw/PrintIR.ql
\ No newline at end of file
diff --git a/cpp/ql/test/library-tests/ir/ir/smart_ptr.cpp b/cpp/ql/test/library-tests/ir/ir/smart_ptr.cpp
new file mode 100644
index 000000000000..f40c54bde446
--- /dev/null
+++ b/cpp/ql/test/library-tests/ir/ir/smart_ptr.cpp
@@ -0,0 +1,48 @@
+#include "../../../include/memory.h"
+#include "../../../include/utility.h"
+
+using std::move;
+using std::shared_ptr;
+using std::unique_ptr;
+
+void unique_ptr_arg(unique_ptr up);
+
+void call_unique_ptr_arg(int* p) {
+ unique_ptr up(p);
+ unique_ptr_arg(move(up));
+}
+
+void shared_ptr_arg(shared_ptr sp);
+
+void call_shared_ptr_arg(float* p) {
+ shared_ptr sp(p);
+ shared_ptr_arg(sp);
+}
+
+void shared_ptr_const_int(shared_ptr);
+void shared_ptr_const_int_ptr(shared_ptr);
+void shared_ptr_shared_ptr_const_int(shared_ptr>);
+void shared_ptr_const_shared_ptr_int(shared_ptr>);
+void shared_ptr_const_shared_ptr_const_int(shared_ptr>);
+
+void call_shared_ptr_consts() {
+ shared_ptr sp_const_int;
+ // cannot modify *sp_const_int
+ shared_ptr_const_int(sp_const_int);
+
+ shared_ptr sp_const_int_pointer;
+ // can modify **sp_const_int_pointer
+ shared_ptr_const_int_ptr(sp_const_int_pointer);
+
+ shared_ptr> sp_sp_const_int;
+ // can modify *sp_const_int_pointer
+ shared_ptr_shared_ptr_const_int(sp_sp_const_int);
+
+ shared_ptr> sp_const_sp_int;
+ // can modify **sp_const_int_pointer
+ shared_ptr_const_shared_ptr_int(sp_const_sp_int);
+
+ shared_ptr> sp_const_sp_const_int;
+ // cannot modify *sp_const_int_pointer or **sp_const_int_pointer
+ shared_ptr_const_shared_ptr_const_int(sp_const_sp_const_int);
+}
\ No newline at end of file
diff --git a/cpp/ql/test/library-tests/ir/points_to/points_to.cpp b/cpp/ql/test/library-tests/ir/points_to/points_to.cpp
index 024a3190706d..fa4d062c5f87 100644
--- a/cpp/ql/test/library-tests/ir/points_to/points_to.cpp
+++ b/cpp/ql/test/library-tests/ir/points_to/points_to.cpp
@@ -37,51 +37,51 @@ void Locals() {
}
void PointsTo(
- int a, //$raw,ussa=a
- Point& b, //$raw,ussa=b ussa=*b
- Point* c, //$raw,ussa=c ussa=*c
- int* d, //$raw,ussa=d ussa=*d
- DerivedSI* e, //$raw,ussa=e ussa=*e
- DerivedMI* f, //$raw,ussa=f ussa=*f
- DerivedVI* g //$raw,ussa=g ussa=*g
+ int a, //$raw=a
+ Point& b, //$raw=b ussa=*b
+ Point* c, //$raw=c ussa=*c
+ int* d, //$raw=d ussa=*d
+ DerivedSI* e, //$raw=e ussa=*e
+ DerivedMI* f, //$raw=f ussa=*f
+ DerivedVI* g //$raw=g ussa=*g
) {
- int i = a; //$raw,ussa=a
- i = *&a; //$raw,ussa=a
- i = *(&a + 0); //$raw,ussa=a
- i = b.x; //$raw,ussa=b ussa=*b[0..4)
- i = b.y; //$raw,ussa=b ussa=*b[4..8)
- i = c->x; //$raw,ussa=c ussa=*c[0..4)
- i = c->y; //$raw,ussa=c ussa=*c[4..8)
- i = *d; //$raw,ussa=d ussa=*d[0..4)
- i = *(d + 0); //$raw,ussa=d ussa=*d[0..4)
- i = d[5]; //$raw,ussa=d ussa=*d[20..24)
- i = 5[d]; //$raw,ussa=d ussa=*d[20..24)
- i = d[a]; //$raw,ussa=d raw,ussa=a ussa=*d[?..?)
- i = a[d]; //$raw,ussa=d raw,ussa=a ussa=*d[?..?)
+ int i = a; //$raw=a
+ i = *&a; //$raw=a
+ i = *(&a + 0); //$raw=a
+ i = b.x; //$raw=b ussa=*b[0..4)
+ i = b.y; //$raw=b ussa=*b[4..8)
+ i = c->x; //$raw=c ussa=*c[0..4)
+ i = c->y; //$raw=c ussa=*c[4..8)
+ i = *d; //$raw=d ussa=*d[0..4)
+ i = *(d + 0); //$raw=d ussa=*d[0..4)
+ i = d[5]; //$raw=d ussa=*d[20..24)
+ i = 5[d]; //$raw=d ussa=*d[20..24)
+ i = d[a]; //$raw=d raw=a ussa=*d[?..?)
+ i = a[d]; //$raw=d raw=a ussa=*d[?..?)
- int* p = &b.x; //$raw,ussa=b
+ int* p = &b.x; //$raw=b
i = *p; //$ussa=*b[0..4)
- p = &b.y; //$raw,ussa=b
+ p = &b.y; //$raw=b
i = *p; //$ussa=*b[4..8)
- p = &c->x; //$raw,ussa=c
+ p = &c->x; //$raw=c
i = *p; //$ussa=*c[0..4)
- p = &c->y; //$raw,ussa=c
+ p = &c->y; //$raw=c
i = *p; //$ussa=*c[4..8)
- p = &d[5]; //$raw,ussa=d
+ p = &d[5]; //$raw=d
i = *p; //$ussa=*d[20..24)
- p = &d[a]; //$raw,ussa=d raw,ussa=a
+ p = &d[a]; //$raw=d raw=a
i = *p; //$ussa=*d[?..?)
- Point* q = &c[a]; //$raw,ussa=c raw,ussa=a
+ Point* q = &c[a]; //$raw=c raw=a
i = q->x; //$ussa=*c[?..?)