diff --git a/.github/workflows/check-change-note.yml b/.github/workflows/check-change-note.yml
index e4e385a5319..0b90ffbc668 100644
--- a/.github/workflows/check-change-note.yml
+++ b/.github/workflows/check-change-note.yml
@@ -1,3 +1,5 @@
+name: Check change note
+
on:
pull_request_target:
types: [labeled, unlabeled, opened, synchronize, reopened, ready_for_review]
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 8ddae33fd7b..b86009ef6da 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -5,8 +5,6 @@ on:
branches:
- main
- 'rc/*'
- paths:
- - 'csharp/**'
pull_request:
branches:
- main
diff --git a/.github/workflows/docs-review.yml b/.github/workflows/docs-review.yml
new file mode 100644
index 00000000000..bf813f46ead
--- /dev/null
+++ b/.github/workflows/docs-review.yml
@@ -0,0 +1,29 @@
+# When a PR is labelled with 'ready-for-docs-review',
+# this workflow comments on the PR to notify the GitHub CodeQL docs team.
+name: Request docs review
+on:
+ # Runs in the context of the base repo.
+ # This gives the workflow write access to comment on PRs.
+ # The workflow should not check out or build the given ref,
+ # or use untrusted data from the event payload in a command line.
+ pull_request_target:
+ types: [labeled]
+
+jobs:
+ request-docs-review:
+ name: Request docs review
+ # Run only on labelled PRs to the main repository.
+ # Do not run on PRs to forks.
+ if:
+ github.event.label.name == 'ready-for-docs-review'
+ && github.event.pull_request.draft == false
+ && github.event.pull_request.base.repo.full_name == 'github/codeql'
+ runs-on: ubuntu-latest
+ steps:
+ - name: Comment to request docs review
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ PR_NUMBER: ${{ github.event.pull_request.number }}
+ run: |
+ gh pr comment "$PR_NUMBER" --repo "github/codeql" \
+ --body "Hello @github/docs-content-codeql - this PR is ready for docs review."
diff --git a/.github/workflows/generate-query-help-docs.yml b/.github/workflows/generate-query-help-docs.yml
deleted file mode 100644
index e3af19c6dea..00000000000
--- a/.github/workflows/generate-query-help-docs.yml
+++ /dev/null
@@ -1,60 +0,0 @@
-name: Generate CodeQL query help documentation using Sphinx
-
-on:
- workflow_dispatch:
- inputs:
- description:
- description: A description of the purpose of this job. For human consumption.
- required: false
- push:
- branches:
- - 'lgtm.com'
- pull_request:
- paths:
- - '.github/workflows/generate-query-help-docs.yml'
- - 'docs/codeql/query-help/**'
-
-jobs:
- build:
- runs-on: ubuntu-latest
- steps:
- - name: Clone github/codeql
- uses: actions/checkout@v2
- with:
- path: codeql
- - name: Clone github/codeql-go
- uses: actions/checkout@v2
- with:
- repository: 'github/codeql-go'
- path: codeql-go
- - name: Set up Python 3.8
- uses: actions/setup-python@v2
- with:
- python-version: 3.8
- - name: Download CodeQL CLI
- uses: dsaltares/fetch-gh-release-asset@aa37ae5c44d3c9820bc12fe675e8670ecd93bd1c
- with:
- repo: "github/codeql-cli-binaries"
- version: "latest"
- file: "codeql-linux64.zip"
- token: ${{ secrets.GITHUB_TOKEN }}
- - name: Unzip CodeQL CLI
- run: unzip -d codeql-cli codeql-linux64.zip
- - name: Set up query help docs folder
- run: |
- cp -r codeql/docs/codeql/** .
- - name: Query help to markdown
- run: |
- PATH="$PATH:codeql-cli/codeql" python codeql/docs/codeql/query-help-markdown.py
- - name: Run Sphinx for query help
- uses: ammaraskar/sphinx-action@8b4f60114d7fd1faeba1a712269168508d4750d2 # v0.4
- with:
- docs-folder: "query-help/"
- pre-build-command: "python -m pip install --upgrade recommonmark && python -m pip install --upgrade sphinx-markdown-tables"
- build-command: "sphinx-build -b dirhtml . _build"
- - name: Upload HTML artifacts
- uses: actions/upload-artifact@v2
- with:
- name: query-help-html
- path: query-help/_build
-
diff --git a/.vscode/settings.json b/.vscode/settings.json
index b467b469f22..8b22c91bb77 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,3 +1,3 @@
{
"omnisharp.autoStart": false
-}
\ No newline at end of file
+}
diff --git a/CODEOWNERS b/CODEOWNERS
index 95f707722ae..da670301f30 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -3,3 +3,17 @@
/java/ @github/codeql-java
/javascript/ @github/codeql-javascript
/python/ @github/codeql-python
+
+# Make @xcorail (GitHub Security Lab) a code owner for experimental queries so he gets pinged when we promote a query out of experimental
+/cpp/**/experimental/**/* @github/codeql-c-analysis @xcorail
+/csharp/**/experimental/**/* @github/codeql-csharp @xcorail
+/java/**/experimental/**/* @github/codeql-java @xcorail
+/javascript/**/experimental/**/* @github/codeql-javascript @xcorail
+/python/**/experimental/**/* @github/codeql-python @xcorail
+
+# Notify members of codeql-go about PRs to the shared data-flow library files
+/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @github/codeql-java @github/codeql-go
+/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @github/codeql-java @github/codeql-go
+/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @github/codeql-java @github/codeql-go
+/java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go
+/java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f9f6fe310a4..8b885094185 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -38,7 +38,7 @@ If you have an idea for a query that you would like to share with other CodeQL u
- The queries and libraries must be autoformatted, for example using the "Format Document" command in [CodeQL for Visual Studio Code](https://help.semmle.com/codeql/codeql-for-vscode/procedures/about-codeql-for-vscode.html).
- If you prefer, you can use this [pre-commit hook](misc/scripts/pre-commit) that automatically checks whether your files are correctly formatted. See the [pre-commit hook installation guide](docs/install-pre-commit-hook.md) for instructions on how to install the hook.
+ If you prefer, you can use this [pre-commit hook](misc/scripts/pre-commit) that automatically checks whether your files are correctly formatted. See the [pre-commit hook installation guide](docs/pre-commit-hook-setup.md) for instructions on how to install the hook.
4. **Compilation**
diff --git a/config/identical-files.json b/config/identical-files.json
index d68dabba861..b63690c5f1e 100644
--- a/config/identical-files.json
+++ b/config/identical-files.json
@@ -374,50 +374,50 @@
"javascript/ql/src/semmle/javascript/XML.qll",
"python/ql/src/semmle/python/xml/XML.qll"
],
- "DuplicationProblems.qhelp": [
- "cpp/ql/src/Metrics/Files/DuplicationProblems.qhelp",
- "csharp/ql/src/Metrics/Files/DuplicationProblems.qhelp",
- "javascript/ql/src/Metrics/DuplicationProblems.qhelp",
- "python/ql/src/Metrics/DuplicationProblems.qhelp"
+ "DuplicationProblems.inc.qhelp": [
+ "cpp/ql/src/Metrics/Files/DuplicationProblems.inc.qhelp",
+ "csharp/ql/src/Metrics/Files/DuplicationProblems.inc.qhelp",
+ "javascript/ql/src/Metrics/DuplicationProblems.inc.qhelp",
+ "python/ql/src/Metrics/DuplicationProblems.inc.qhelp"
],
- "CommentedOutCodeQuery.qhelp": [
- "cpp/ql/src/Documentation/CommentedOutCodeQuery.qhelp",
- "python/ql/src/Lexical/CommentedOutCodeQuery.qhelp",
- "csharp/ql/src/Bad Practices/Comments/CommentedOutCodeQuery.qhelp",
- "java/ql/src/Violations of Best Practice/Comments/CommentedOutCodeQuery.qhelp",
- "javascript/ql/src/Comments/CommentedOutCodeQuery.qhelp"
+ "CommentedOutCodeQuery.inc.qhelp": [
+ "cpp/ql/src/Documentation/CommentedOutCodeQuery.inc.qhelp",
+ "python/ql/src/Lexical/CommentedOutCodeQuery.inc.qhelp",
+ "csharp/ql/src/Bad Practices/Comments/CommentedOutCodeQuery.inc.qhelp",
+ "java/ql/src/Violations of Best Practice/Comments/CommentedOutCodeQuery.inc.qhelp",
+ "javascript/ql/src/Comments/CommentedOutCodeQuery.inc.qhelp"
],
- "FLinesOfCodeReferences.qhelp": [
- "java/ql/src/Metrics/Files/FLinesOfCodeReferences.qhelp",
- "javascript/ql/src/Metrics/FLinesOfCodeReferences.qhelp"
+ "FLinesOfCodeReferences.inc.qhelp": [
+ "java/ql/src/Metrics/Files/FLinesOfCodeReferences.inc.qhelp",
+ "javascript/ql/src/Metrics/FLinesOfCodeReferences.inc.qhelp"
],
- "FCommentRatioCommon.qhelp": [
- "java/ql/src/Metrics/Files/FCommentRatioCommon.qhelp",
- "javascript/ql/src/Metrics/FCommentRatioCommon.qhelp"
+ "FCommentRatioCommon.inc.qhelp": [
+ "java/ql/src/Metrics/Files/FCommentRatioCommon.inc.qhelp",
+ "javascript/ql/src/Metrics/FCommentRatioCommon.inc.qhelp"
],
- "FLinesOfCodeOverview.qhelp": [
- "java/ql/src/Metrics/Files/FLinesOfCodeOverview.qhelp",
- "javascript/ql/src/Metrics/FLinesOfCodeOverview.qhelp"
+ "FLinesOfCodeOverview.inc.qhelp": [
+ "java/ql/src/Metrics/Files/FLinesOfCodeOverview.inc.qhelp",
+ "javascript/ql/src/Metrics/FLinesOfCodeOverview.inc.qhelp"
],
- "CommentedOutCodeMetricOverview.qhelp": [
- "cpp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp",
- "csharp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp",
- "java/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp",
- "javascript/ql/src/Comments/CommentedOutCodeMetricOverview.qhelp",
- "python/ql/src/Lexical/CommentedOutCodeMetricOverview.qhelp"
+ "CommentedOutCodeMetricOverview.inc.qhelp": [
+ "cpp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.inc.qhelp",
+ "csharp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.inc.qhelp",
+ "java/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.inc.qhelp",
+ "javascript/ql/src/Comments/CommentedOutCodeMetricOverview.inc.qhelp",
+ "python/ql/src/Lexical/CommentedOutCodeMetricOverview.inc.qhelp"
],
- "FLinesOfDuplicatedCodeCommon.qhelp": [
- "cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.qhelp",
- "java/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.qhelp",
- "javascript/ql/src/Metrics/FLinesOfDuplicatedCodeCommon.qhelp",
- "python/ql/src/Metrics/FLinesOfDuplicatedCodeCommon.qhelp"
+ "FLinesOfDuplicatedCodeCommon.inc.qhelp": [
+ "cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.inc.qhelp",
+ "java/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.inc.qhelp",
+ "javascript/ql/src/Metrics/FLinesOfDuplicatedCodeCommon.inc.qhelp",
+ "python/ql/src/Metrics/FLinesOfDuplicatedCodeCommon.inc.qhelp"
],
- "CommentedOutCodeReferences.qhelp": [
- "cpp/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp",
- "csharp/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp",
- "java/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp",
- "javascript/ql/src/Comments/CommentedOutCodeReferences.qhelp",
- "python/ql/src/Lexical/CommentedOutCodeReferences.qhelp"
+ "CommentedOutCodeReferences.inc.qhelp": [
+ "cpp/ql/src/Metrics/Files/CommentedOutCodeReferences.inc.qhelp",
+ "csharp/ql/src/Metrics/Files/CommentedOutCodeReferences.inc.qhelp",
+ "java/ql/src/Metrics/Files/CommentedOutCodeReferences.inc.qhelp",
+ "javascript/ql/src/Comments/CommentedOutCodeReferences.inc.qhelp",
+ "python/ql/src/Lexical/CommentedOutCodeReferences.inc.qhelp"
],
"IDE Contextual Queries": [
"cpp/ql/src/IDEContextual.qll",
@@ -425,5 +425,14 @@
"java/ql/src/IDEContextual.qll",
"javascript/ql/src/IDEContextual.qll",
"python/ql/src/analysis/IDEContextual.qll"
+ ],
+ "SSA C#": [
+ "csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll",
+ "csharp/ql/src/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll",
+ "csharp/ql/src/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll"
+ ],
+ "CryptoAlgorithms Python/JS": [
+ "javascript/ql/src/semmle/javascript/security/CryptoAlgorithms.qll",
+ "python/ql/src/semmle/crypto/Crypto.qll"
]
}
diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/Semmle.Autobuild.Cpp.Tests.csproj b/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/Semmle.Autobuild.Cpp.Tests.csproj
index 7de677b5610..9bf293c7e8d 100644
--- a/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/Semmle.Autobuild.Cpp.Tests.csproj
+++ b/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/Semmle.Autobuild.Cpp.Tests.csproj
@@ -2,7 +2,7 @@
socket where there is no corresponding FILE*, but may return an
This can occur when an operation performed on the open descriptor fails, and the function returns with an error before closing the open resource. An improperly handled error may cause the function to leak file descriptors.
-fopen with no corresponding fclose
Leaving files open will cause a resource leak that will persist even after the program terminates.
-
+
diff --git a/cpp/ql/src/Critical/GlobalUseBeforeInit.qhelp b/cpp/ql/src/Critical/GlobalUseBeforeInit.qhelp
index 9566a31f870..89608d89cc2 100644
--- a/cpp/ql/src/Critical/GlobalUseBeforeInit.qhelp
+++ b/cpp/ql/src/Critical/GlobalUseBeforeInit.qhelp
@@ -10,7 +10,7 @@ Not all compilers generate code that zero-out memory, especially when optimizati
is not compliant with the latest language standards. Accessing uninitialized memory will lead to undefined results.
-
+
diff --git a/cpp/ql/src/Critical/InconsistentNullnessTesting.qhelp b/cpp/ql/src/Critical/InconsistentNullnessTesting.qhelp
index bd530b74829..a68e1fe4662 100644
--- a/cpp/ql/src/Critical/InconsistentNullnessTesting.qhelp
+++ b/cpp/ql/src/Critical/InconsistentNullnessTesting.qhelp
@@ -12,7 +12,7 @@ Dereferencing a null pointer and attempting to modify its contents can lead to a
important system data (including the interrupt table in some architectures).
-
+
diff --git a/cpp/ql/src/Critical/InitialisationNotRun.qhelp b/cpp/ql/src/Critical/InitialisationNotRun.qhelp
index 5152620c502..83b06f371e9 100644
--- a/cpp/ql/src/Critical/InitialisationNotRun.qhelp
+++ b/cpp/ql/src/Critical/InitialisationNotRun.qhelp
@@ -11,7 +11,7 @@ Uninitialized variables may contain any value, as not all compilers generate cod
optimizations are enabled or the compiler is not compliant with the latest language standards.
-
+
diff --git a/cpp/ql/src/Critical/LateNegativeTest.qhelp b/cpp/ql/src/Critical/LateNegativeTest.qhelp
index fd4603c5f72..859ec6588c6 100644
--- a/cpp/ql/src/Critical/LateNegativeTest.qhelp
+++ b/cpp/ql/src/Critical/LateNegativeTest.qhelp
@@ -13,7 +13,7 @@ after. Otherwise, if the value is negative then the program will have failed
before performing the test.
-
+
diff --git a/cpp/ql/src/Critical/MemoryMayNotBeFreed.qhelp b/cpp/ql/src/Critical/MemoryMayNotBeFreed.qhelp
index cb065be0ad0..5734b1b04b2 100644
--- a/cpp/ql/src/Critical/MemoryMayNotBeFreed.qhelp
+++ b/cpp/ql/src/Critical/MemoryMayNotBeFreed.qhelp
@@ -9,7 +9,7 @@
This rule looks for functions that allocate memory, but may return without freeing it. This can occur when an operation performed on the memory block fails, and the function returns with an error before freeing the allocated block. This causes the function to leak memory and may eventually lead to software failure.
-
+
diff --git a/cpp/ql/src/Critical/MemoryNeverFreed.qhelp b/cpp/ql/src/Critical/MemoryNeverFreed.qhelp
index c582034b909..8cb8ef81a64 100644
--- a/cpp/ql/src/Critical/MemoryNeverFreed.qhelp
+++ b/cpp/ql/src/Critical/MemoryNeverFreed.qhelp
@@ -10,7 +10,7 @@ This rule finds calls to the alloc family of functions without a co
This leads to memory leaks.
-
+
diff --git a/cpp/ql/src/Critical/MissingNegativityTest.qhelp b/cpp/ql/src/Critical/MissingNegativityTest.qhelp
index c8e83acfd14..8cfac261544 100644
--- a/cpp/ql/src/Critical/MissingNegativityTest.qhelp
+++ b/cpp/ql/src/Critical/MissingNegativityTest.qhelp
@@ -16,7 +16,7 @@ buffer overruns.
The query looks only at the return values of functions that may return a negative value (not all functions).
-
+
diff --git a/cpp/ql/src/Critical/NewArrayDeleteMismatch.qhelp b/cpp/ql/src/Critical/NewArrayDeleteMismatch.qhelp
index 5b7992ce828..2ecb73d4479 100644
--- a/cpp/ql/src/Critical/NewArrayDeleteMismatch.qhelp
+++ b/cpp/ql/src/Critical/NewArrayDeleteMismatch.qhelp
@@ -63,7 +63,7 @@ destructors likely not be called (as previously noted), but the pointer will als
potentially less of a serious issue than that posed by the first approach, but it should still be avoided.
-
+
diff --git a/cpp/ql/src/Critical/NewDeleteArrayMismatch.qhelp b/cpp/ql/src/Critical/NewDeleteArrayMismatch.qhelp
index ca039fecc04..46f34dbfaf6 100644
--- a/cpp/ql/src/Critical/NewDeleteArrayMismatch.qhelp
+++ b/cpp/ql/src/Critical/NewDeleteArrayMismatch.qhelp
@@ -18,7 +18,7 @@ an array (which could have header data specifying the length of the array) and w
element of the 'array', which would likely lead to a segfault due to the invalid header data.
-
+
diff --git a/cpp/ql/src/Critical/OverflowCalculated.qhelp b/cpp/ql/src/Critical/OverflowCalculated.qhelp
index 50ad526503b..35cf8149a6d 100644
--- a/cpp/ql/src/Critical/OverflowCalculated.qhelp
+++ b/cpp/ql/src/Critical/OverflowCalculated.qhelp
@@ -19,7 +19,7 @@ the data being copied. Buffer overflows can result to anything from a segmentati
if the array is on stack-allocated memory).
-
+
diff --git a/cpp/ql/src/Critical/OverflowDestination.qhelp b/cpp/ql/src/Critical/OverflowDestination.qhelp
index c110bc4f6a6..828cb5333e6 100644
--- a/cpp/ql/src/Critical/OverflowDestination.qhelp
+++ b/cpp/ql/src/Critical/OverflowDestination.qhelp
@@ -14,7 +14,7 @@ Buffer overflows can lead to anything from a segmentation fault to a security vu
Ensure that the size parameter is derived from the size of the destination buffer, and
not the source buffer.
-
+
diff --git a/cpp/ql/src/Critical/ReturnStackAllocatedObject.qhelp b/cpp/ql/src/Critical/ReturnStackAllocatedObject.qhelp
index 0cb1371f313..3edf794edf9 100644
--- a/cpp/ql/src/Critical/ReturnStackAllocatedObject.qhelp
+++ b/cpp/ql/src/Critical/ReturnStackAllocatedObject.qhelp
@@ -12,7 +12,7 @@ the contents of that memory become undefined after that. Clearly, using a pointe
memory after the function has already returned will have undefined results.
-
+
diff --git a/cpp/ql/src/Critical/UseAfterFree.qhelp b/cpp/ql/src/Critical/UseAfterFree.qhelp
index 9be2dec1a0c..6a532ed4d35 100644
--- a/cpp/ql/src/Critical/UseAfterFree.qhelp
+++ b/cpp/ql/src/Critical/UseAfterFree.qhelp
@@ -12,7 +12,7 @@ from a segfault to memory corruption that would cause subsequent calls to the dy
erratically, to a possible security vulnerability.
-
+
diff --git a/cpp/ql/src/Critical/aliasAnalysisWarning.qhelp b/cpp/ql/src/Critical/aliasAnalysisWarning.inc.qhelp
similarity index 100%
rename from cpp/ql/src/Critical/aliasAnalysisWarning.qhelp
rename to cpp/ql/src/Critical/aliasAnalysisWarning.inc.qhelp
diff --git a/cpp/ql/src/Critical/callGraphWarning.qhelp b/cpp/ql/src/Critical/callGraphWarning.inc.qhelp
similarity index 100%
rename from cpp/ql/src/Critical/callGraphWarning.qhelp
rename to cpp/ql/src/Critical/callGraphWarning.inc.qhelp
diff --git a/cpp/ql/src/Critical/dataFlowWarning.qhelp b/cpp/ql/src/Critical/dataFlowWarning.inc.qhelp
similarity index 100%
rename from cpp/ql/src/Critical/dataFlowWarning.qhelp
rename to cpp/ql/src/Critical/dataFlowWarning.inc.qhelp
diff --git a/cpp/ql/src/Critical/pointsToWarning.qhelp b/cpp/ql/src/Critical/pointsToWarning.inc.qhelp
similarity index 100%
rename from cpp/ql/src/Critical/pointsToWarning.qhelp
rename to cpp/ql/src/Critical/pointsToWarning.inc.qhelp
diff --git a/cpp/ql/src/Diagnostics/ExtractionErrors.ql b/cpp/ql/src/Diagnostics/ExtractionErrors.ql
new file mode 100644
index 00000000000..a445c3bc0c4
--- /dev/null
+++ b/cpp/ql/src/Diagnostics/ExtractionErrors.ql
@@ -0,0 +1,16 @@
+/**
+ * @name Extraction errors
+ * @description List all extraction errors for files in the source code directory.
+ * @kind diagnostic
+ * @id cpp/diagnostics/extraction-errors
+ */
+
+import cpp
+import ExtractionErrors
+
+from ExtractionError error
+where
+ error instanceof ExtractionUnknownError or
+ exists(error.getFile().getRelativePath())
+select error, "Extraction failed in " + error.getFile() + " with error " + error.getErrorMessage(),
+ error.getSeverity()
diff --git a/cpp/ql/src/Diagnostics/ExtractionErrors.qll b/cpp/ql/src/Diagnostics/ExtractionErrors.qll
new file mode 100644
index 00000000000..4cf6f8145f8
--- /dev/null
+++ b/cpp/ql/src/Diagnostics/ExtractionErrors.qll
@@ -0,0 +1,137 @@
+/**
+ * Provides a common hierarchy of all types of errors that can occur during extraction.
+ */
+
+import cpp
+
+/*
+ * A note about how the C/C++ extractor emits diagnostics:
+ * When the extractor frontend encounters an error, it emits a diagnostic message,
+ * that includes a message, location and severity.
+ * However, that process is best-effort and may fail (e.g. due to lack of memory).
+ * Thus, if the extractor emitted at least one diagnostic of severity discretionary
+ * error (or higher), it *also* emits a simple "There was an error during this compilation"
+ * error diagnostic, without location information.
+ * In the common case, this means that a compilation during which one or more errors happened also gets
+ * the catch-all diagnostic.
+ * This diagnostic has the empty string as file path.
+ * We filter out these useless diagnostics if there is at least one error-level diagnostic
+ * for the affected compilation in the database.
+ * Otherwise, we show it to indicate that something went wrong and that we
+ * don't know what exactly happened.
+ */
+
+/**
+ * An error that, if present, leads to a file being marked as non-successfully extracted.
+ */
+class ReportableError extends Diagnostic {
+ ReportableError() {
+ (
+ this instanceof CompilerDiscretionaryError or
+ this instanceof CompilerError or
+ this instanceof CompilerCatastrophe
+ ) and
+ // Filter for the catch-all diagnostic, see note above.
+ not this.getFile().getAbsolutePath() = ""
+ }
+}
+
+private newtype TExtractionError =
+ TReportableError(ReportableError err) or
+ TCompilationFailed(Compilation c, File f) {
+ f = c.getAFileCompiled() and not c.normalTermination()
+ } or
+ // Show the catch-all diagnostic (see note above) only if we haven't seen any other error-level diagnostic
+ // for that compilation
+ TUnknownError(CompilerError err) {
+ not exists(ReportableError e | e.getCompilation() = err.getCompilation())
+ }
+
+/**
+ * Superclass for the extraction error hierarchy.
+ */
+class ExtractionError extends TExtractionError {
+ /** Gets the string representation of the error. */
+ string toString() { none() }
+
+ /** Gets the error message for this error. */
+ string getErrorMessage() { none() }
+
+ /** Gets the file this error occured in. */
+ File getFile() { none() }
+
+ /** Gets the location this error occured in. */
+ Location getLocation() { none() }
+
+ /** Gets the SARIF severity of this error. */
+ int getSeverity() {
+ // Unfortunately, we can't distinguish between errors and fatal errors in SARIF,
+ // so all errors have severity 2.
+ result = 2
+ }
+}
+
+/**
+ * An unrecoverable extraction error, where extraction was unable to finish.
+ * This can be caused by a multitude of reasons, for example:
+ * - hitting a frontend assertion
+ * - crashing due to dereferencing an invalid pointer
+ * - stack overflow
+ * - out of memory
+ */
+class ExtractionUnrecoverableError extends ExtractionError, TCompilationFailed {
+ Compilation c;
+ File f;
+
+ ExtractionUnrecoverableError() { this = TCompilationFailed(c, f) }
+
+ override string toString() {
+ result = "Unrecoverable extraction error while compiling " + f.toString()
+ }
+
+ override string getErrorMessage() { result = "unrecoverable compilation failure." }
+
+ override File getFile() { result = f }
+
+ override Location getLocation() { result = f.getLocation() }
+}
+
+/**
+ * A recoverable extraction error.
+ * These are compiler errors from the frontend.
+ * Upon encountering one of these, we still continue extraction, but the
+ * database will be incomplete for that file.
+ */
+class ExtractionRecoverableError extends ExtractionError, TReportableError {
+ ReportableError err;
+
+ ExtractionRecoverableError() { this = TReportableError(err) }
+
+ override string toString() { result = "Recoverable extraction error: " + err }
+
+ override string getErrorMessage() { result = err.getFullMessage() }
+
+ override File getFile() { result = err.getFile() }
+
+ override Location getLocation() { result = err.getLocation() }
+}
+
+/**
+ * An unknown error happened during extraction.
+ * These are only displayed if we know that we encountered an error during extraction,
+ * but, for some reason, failed to emit a proper diagnostic with location information
+ * and error message.
+ */
+class ExtractionUnknownError extends ExtractionError, TUnknownError {
+ CompilerError err;
+
+ ExtractionUnknownError() { this = TUnknownError(err) }
+
+ override string toString() { result = "Unknown extraction error: " + err }
+
+ override string getErrorMessage() { result = err.getFullMessage() }
+
+ override File getFile() { result = err.getFile() }
+
+ override Location getLocation() { result = err.getLocation() }
+}
diff --git a/cpp/ql/src/Diagnostics/FailedExtractorInvocations.ql b/cpp/ql/src/Diagnostics/FailedExtractorInvocations.ql
new file mode 100644
index 00000000000..ec1a243f245
--- /dev/null
+++ b/cpp/ql/src/Diagnostics/FailedExtractorInvocations.ql
@@ -0,0 +1,22 @@
+/**
+ * @name Failed extractor invocations
+ * @description Gives the command line of compilations for which extraction did not run to completion.
+ * @kind diagnostic
+ * @id cpp/diagnostics/failed-extractor-invocations
+ */
+
+import cpp
+
+class AnonymousCompilation extends Compilation {
+ override string toString() { result = "" }
+}
+
+string describe(Compilation c) {
+ if c.getArgument(1) = "--mimic"
+ then result = "compiler invocation " + concat(int i | i > 1 | c.getArgument(i), " " order by i)
+ else result = "extractor invocation " + concat(int i | | c.getArgument(i), " " order by i)
+}
+
+from Compilation c
+where not c.normalTermination()
+select c, "Extraction aborted for " + describe(c), 2
diff --git a/cpp/ql/src/Diagnostics/SuccessfullyExtractedFiles.ql b/cpp/ql/src/Diagnostics/SuccessfullyExtractedFiles.ql
new file mode 100644
index 00000000000..a920af8e767
--- /dev/null
+++ b/cpp/ql/src/Diagnostics/SuccessfullyExtractedFiles.ql
@@ -0,0 +1,15 @@
+/**
+ * @name Successfully extracted files
+ * @description Lists all files in the source code directory that were extracted without encountering an error in the file.
+ * @kind diagnostic
+ * @id cpp/diagnostics/successfully-extracted-files
+ */
+
+import cpp
+import ExtractionErrors
+
+from File f
+where
+ not exists(ExtractionError e | e.getFile() = f) and
+ exists(f.getRelativePath())
+select f, ""
diff --git a/cpp/ql/src/Documentation/CommentedOutCode.qhelp b/cpp/ql/src/Documentation/CommentedOutCode.qhelp
index ba056ab73f7..5750b5be871 100644
--- a/cpp/ql/src/Documentation/CommentedOutCode.qhelp
+++ b/cpp/ql/src/Documentation/CommentedOutCode.qhelp
@@ -2,6 +2,6 @@
"-//Semmle//qhelp//EN"
"qhelp.dtd">
-
-
+
+
diff --git a/cpp/ql/src/Documentation/CommentedOutCodeQuery.qhelp b/cpp/ql/src/Documentation/CommentedOutCodeQuery.inc.qhelp
similarity index 100%
rename from cpp/ql/src/Documentation/CommentedOutCodeQuery.qhelp
rename to cpp/ql/src/Documentation/CommentedOutCodeQuery.inc.qhelp
diff --git a/cpp/ql/src/Likely Bugs/Leap Year/Adding365DaysPerYear.qhelp b/cpp/ql/src/Likely Bugs/Leap Year/Adding365DaysPerYear.qhelp
index 7d8522211a8..186ec807994 100644
--- a/cpp/ql/src/Likely Bugs/Leap Year/Adding365DaysPerYear.qhelp
+++ b/cpp/ql/src/Likely Bugs/Leap Year/Adding365DaysPerYear.qhelp
@@ -3,7 +3,7 @@
"qhelp.dtd">
-
+
When performing arithmetic operations on a variable that represents a date, leap years must be taken into account.
It is not safe to assume that a year is 365 days long.
diff --git a/cpp/ql/src/Likely Bugs/Leap Year/LeapYear.qhelp b/cpp/ql/src/Likely Bugs/Leap Year/LeapYear.inc.qhelp
similarity index 100%
rename from cpp/ql/src/Likely Bugs/Leap Year/LeapYear.qhelp
rename to cpp/ql/src/Likely Bugs/Leap Year/LeapYear.inc.qhelp
diff --git a/cpp/ql/src/Likely Bugs/Leap Year/UncheckedLeapYearAfterYearModification.qhelp b/cpp/ql/src/Likely Bugs/Leap Year/UncheckedLeapYearAfterYearModification.qhelp
index 931c200e0f4..8fbe7933201 100644
--- a/cpp/ql/src/Likely Bugs/Leap Year/UncheckedLeapYearAfterYearModification.qhelp
+++ b/cpp/ql/src/Likely Bugs/Leap Year/UncheckedLeapYearAfterYearModification.qhelp
@@ -3,7 +3,7 @@
"qhelp.dtd">
-
+
When performing arithmetic operations on a variable that represents a year, it is important to consider that the resulting value may not be a valid date.
The typical example is doing simple year arithmetic (i.e. date.year++) without considering if the resulting value will be a valid date or not.
diff --git a/cpp/ql/src/Likely Bugs/Leap Year/UncheckedReturnValueForTimeFunctions.qhelp b/cpp/ql/src/Likely Bugs/Leap Year/UncheckedReturnValueForTimeFunctions.qhelp
index 57afd1659dc..6be0e091caf 100644
--- a/cpp/ql/src/Likely Bugs/Leap Year/UncheckedReturnValueForTimeFunctions.qhelp
+++ b/cpp/ql/src/Likely Bugs/Leap Year/UncheckedReturnValueForTimeFunctions.qhelp
@@ -3,7 +3,7 @@
"qhelp.dtd">
-
+
When using a function that transforms a date structure, and the year on the input argument for the API has been manipulated, it is important to check for the return value of the function to make sure it succeeded.
Otherwise, the function may have failed, and the output parameter may contain invalid data that can cause any number of problems on the affected system.
diff --git a/cpp/ql/src/Likely Bugs/Leap Year/UnsafeArrayForDaysOfYear.qhelp b/cpp/ql/src/Likely Bugs/Leap Year/UnsafeArrayForDaysOfYear.qhelp
index 1db5fb52b6b..d2c0375f0af 100644
--- a/cpp/ql/src/Likely Bugs/Leap Year/UnsafeArrayForDaysOfYear.qhelp
+++ b/cpp/ql/src/Likely Bugs/Leap Year/UnsafeArrayForDaysOfYear.qhelp
@@ -3,7 +3,7 @@
"qhelp.dtd">
-
+
This query helps to detect when a developer allocates an array or other fixed-length data structure such as std::vector with 365 elements – one for each day of the year.
Since leap years have 366 days, there will be no allocated element on December 31st at the end of a leap year; which will lead to a buffer overflow on a leap year.
diff --git a/cpp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp b/cpp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.inc.qhelp
similarity index 100%
rename from cpp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp
rename to cpp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.inc.qhelp
diff --git a/cpp/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp b/cpp/ql/src/Metrics/Files/CommentedOutCodeReferences.inc.qhelp
similarity index 100%
rename from cpp/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp
rename to cpp/ql/src/Metrics/Files/CommentedOutCodeReferences.inc.qhelp
diff --git a/cpp/ql/src/Metrics/Files/DuplicationProblems.qhelp b/cpp/ql/src/Metrics/Files/DuplicationProblems.inc.qhelp
similarity index 100%
rename from cpp/ql/src/Metrics/Files/DuplicationProblems.qhelp
rename to cpp/ql/src/Metrics/Files/DuplicationProblems.inc.qhelp
diff --git a/cpp/ql/src/Metrics/Files/FLinesOfCommentedOutCode.qhelp b/cpp/ql/src/Metrics/Files/FLinesOfCommentedOutCode.qhelp
index d435289b652..ff045244d7b 100644
--- a/cpp/ql/src/Metrics/Files/FLinesOfCommentedOutCode.qhelp
+++ b/cpp/ql/src/Metrics/Files/FLinesOfCommentedOutCode.qhelp
@@ -2,6 +2,6 @@
"-//Semmle//qhelp//EN"
"qhelp.dtd">
-
-
+
+
diff --git a/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.qhelp b/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.qhelp
index 30a98df0cee..80dd9f98e63 100644
--- a/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.qhelp
+++ b/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.qhelp
@@ -2,5 +2,5 @@
"-//Semmle//qhelp//EN"
"qhelp.dtd">
-
+
\ No newline at end of file
diff --git a/java/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.qhelp b/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.inc.qhelp
similarity index 94%
rename from java/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.qhelp
rename to cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.inc.qhelp
index 17171fb7587..6fc36156c11 100644
--- a/java/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.qhelp
+++ b/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.inc.qhelp
@@ -14,7 +14,7 @@ for a number of reasons.
-
+
diff --git a/cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted-bad.c b/cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted-bad.c
new file mode 100644
index 00000000000..ac74314eefa
--- /dev/null
+++ b/cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted-bad.c
@@ -0,0 +1,3 @@
+char password[MAX_PASSWORD_LENGTH];
+// read and verify password
+memset(password, 0, MAX_PASSWORD_LENGTH);
diff --git a/cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted-good.c b/cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted-good.c
new file mode 100644
index 00000000000..2e72f553a4e
--- /dev/null
+++ b/cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted-good.c
@@ -0,0 +1,3 @@
+char password[MAX_PASSWORD_LENGTH];
+// read and verify password
+memset_s(password, MAX_PASSWORD_LENGTH, 0, MAX_PASSWORD_LENGTH);
diff --git a/cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted.qhelp b/cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted.qhelp
new file mode 100644
index 00000000000..2c087e37eaa
--- /dev/null
+++ b/cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted.qhelp
@@ -0,0 +1,45 @@
+
+
+
+Calling memset or bzero on a buffer to clear its contents may get optimized
+away by the compiler if the buffer is not subsequently used. This is not desirable behavior if the buffer
+contains sensitive data that could somehow be retrieved by an attacker.
+
+
+
+
+Use alternative platform-supplied functions that will not get optimized away. Examples of such
+functions include memset_s, SecureZeroMemory, and bzero_explicit.
+Alternatively, passing the -fno-builtin-memset option to the GCC/Clang compiler usually
+also prevents the optimization. Finally, you can use the public-domain secure_memzero function
+(see references below). This function, however, is not guaranteed to work on all platforms and compilers.
+
+
+
+The following program fragment uses memset to erase sensitive information after it is no
+longer needed:
+
+Because of dead store elimination, the call to memset may be removed by the compiler
+(since the buffer is not subsequently used), resulting in potentially sensitive data remaining in memory.
+
+
+The best solution to this problem is to use the memset_s function instead of
+memset:
+
+
+
+
+
+
+CERT C Coding Standard:
+MSC06-C. Beware of compiler optimizations.
+
+
+USENIX: The Advanced Computing Systems Association:
+Dead Store Elimination (Still) Considered Harmfuls
+
+
+
+
diff --git a/cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted.ql b/cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted.ql
new file mode 100644
index 00000000000..bbb77e807e7
--- /dev/null
+++ b/cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted.ql
@@ -0,0 +1,81 @@
+/**
+ * @name Call to `memset` may be deleted
+ * @description Using the `memset` function to clear private data in a variable that has no subsequent use
+ * can make information-leak vulnerabilities easier to exploit because the compiler can remove the call.
+ * @kind problem
+ * @id cpp/memset-may-be-deleted
+ * @problem.severity warning
+ * @precision high
+ * @tags security
+ * external/cwe/cwe-14
+ */
+
+import cpp
+import semmle.code.cpp.dataflow.EscapesTree
+import semmle.code.cpp.commons.Exclusions
+import semmle.code.cpp.models.interfaces.Alias
+
+class MemsetFunction extends Function {
+ MemsetFunction() {
+ this.hasGlobalOrStdOrBslName("memset")
+ or
+ this.hasGlobalOrStdName("wmemset")
+ or
+ this.hasGlobalName(["bzero", "__builtin_memset"])
+ }
+}
+
+predicate isNonEscapingArgument(Expr escaped) {
+ exists(Call call, AliasFunction aliasFunction, int i |
+ aliasFunction = call.getTarget() and
+ call.getArgument(i) = escaped.getUnconverted() and
+ (
+ aliasFunction.parameterNeverEscapes(i)
+ or
+ aliasFunction.parameterEscapesOnlyViaReturn(i) and
+ (call instanceof ExprInVoidContext or call.getConversion*() instanceof BoolConversion)
+ )
+ )
+}
+
+pragma[noinline]
+predicate callToMemsetWithRelevantVariable(
+ LocalVariable v, VariableAccess acc, FunctionCall call, MemsetFunction memset
+) {
+ not v.isStatic() and
+ // Reference-typed variables get special treatment in `variableAddressEscapesTree` so we leave them
+ // out of this query.
+ not v.getUnspecifiedType() instanceof ReferenceType and
+ call.getTarget() = memset and
+ acc = v.getAnAccess() and
+ // `v` escapes as the argument to `memset`
+ variableAddressEscapesTree(acc, call.getArgument(0).getFullyConverted())
+}
+
+pragma[noinline]
+predicate relevantVariable(LocalVariable v, FunctionCall call, MemsetFunction memset) {
+ exists(VariableAccess acc, VariableAccess anotherAcc |
+ callToMemsetWithRelevantVariable(v, acc, call, memset) and
+ // `v` is not only just used in the call to `memset`.
+ anotherAcc = v.getAnAccess() and
+ acc != anotherAcc and
+ not anotherAcc.isUnevaluated()
+ )
+}
+
+from FunctionCall call, LocalVariable v, MemsetFunction memset
+where
+ relevantVariable(v, call, memset) and
+ not isFromMacroDefinition(call) and
+ // `v` doesn't escape anywhere else.
+ forall(Expr escape | variableAddressEscapesTree(v.getAnAccess(), escape) |
+ isNonEscapingArgument(escape)
+ ) and
+ // There is no later use of `v`.
+ not v.getAnAccess() = call.getASuccessor*() and
+ // Not using the `-fno-builtin-memset` flag
+ exists(Compilation c |
+ c.getAFileCompiled() = call.getFile() and
+ not c.getAnArgument() = "-fno-builtin-memset"
+ )
+select call, "Call to " + memset.getName() + " may be deleted by the compiler."
diff --git a/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.qhelp b/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.qhelp
index 884b1dbdd4b..d715abca84c 100644
--- a/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.qhelp
+++ b/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.qhelp
@@ -2,4 +2,4 @@
"-//Semmle//qhelp//EN"
"qhelp.dtd">
-
+
diff --git a/cpp/ql/src/Security/CWE/CWE-311/CleartextFileWrite.qhelp b/cpp/ql/src/Security/CWE/CWE-311/CleartextFileWrite.qhelp
index 884b1dbdd4b..d715abca84c 100644
--- a/cpp/ql/src/Security/CWE/CWE-311/CleartextFileWrite.qhelp
+++ b/cpp/ql/src/Security/CWE/CWE-311/CleartextFileWrite.qhelp
@@ -2,4 +2,4 @@
"-//Semmle//qhelp//EN"
"qhelp.dtd">
-
+
diff --git a/cpp/ql/src/Security/CWE/CWE-311/CleartextStorage.qhelp b/cpp/ql/src/Security/CWE/CWE-311/CleartextStorage.inc.qhelp
similarity index 100%
rename from cpp/ql/src/Security/CWE/CWE-311/CleartextStorage.qhelp
rename to cpp/ql/src/Security/CWE/CWE-311/CleartextStorage.inc.qhelp
diff --git a/cpp/ql/src/Summary/LinesOfCode.ql b/cpp/ql/src/Summary/LinesOfCode.ql
new file mode 100644
index 00000000000..3b2aa2ac4c9
--- /dev/null
+++ b/cpp/ql/src/Summary/LinesOfCode.ql
@@ -0,0 +1,11 @@
+/**
+ * @id cpp/summary/lines-of-code
+ * @name Total lines of C/C++ code in the database
+ * @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
+ */
+
+import cpp
+
+select sum(File f | f.fromSource() | f.getMetrics().getNumberOfLinesOfCode())
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-020/LateCheckOfFunctionArgument.c b/cpp/ql/src/experimental/Security/CWE/CWE-020/LateCheckOfFunctionArgument.c
new file mode 100644
index 00000000000..f765413947b
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-020/LateCheckOfFunctionArgument.c
@@ -0,0 +1,7 @@
+if(len<0) return 1;
+memset(dest, source, len); // GOOD: variable `len` checked before call
+
+...
+
+memset(dest, source, len); // BAD: variable `len` checked after call
+if(len<0) return 1;
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-020/LateCheckOfFunctionArgument.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-020/LateCheckOfFunctionArgument.qhelp
new file mode 100644
index 00000000000..8ec02706a73
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-020/LateCheckOfFunctionArgument.qhelp
@@ -0,0 +1,28 @@
+
+
+
+Checking the function argument after calling the function itself. This situation looks suspicious and requires the attention of the developer. It may be necessary to add validation before calling the function
+
+
+
+
+
+We recommend checking before calling the function.
+
+
+
+The following example demonstrates an erroneous and fixed use of function argument validation.
+
+
+
+
+
+
+ CWE Common Weakness Enumeration:
+ CWE-20: Improper Input Validation.
+
+
+
+
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-020/LateCheckOfFunctionArgument.ql b/cpp/ql/src/experimental/Security/CWE/CWE-020/LateCheckOfFunctionArgument.ql
new file mode 100644
index 00000000000..3ef5bf3405e
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-020/LateCheckOfFunctionArgument.ql
@@ -0,0 +1,66 @@
+/**
+ * @name Late Check Of Function Argument
+ * @description --Checking the function argument after calling the function itself.
+ * --This situation looks suspicious and requires the attention of the developer.
+ * --It may be necessary to add validation before calling the function.
+ * @kind problem
+ * @id cpp/late-check-of-function-argument
+ * @problem.severity warning
+ * @precision medium
+ * @tags correctness
+ * security
+ * external/cwe/cwe-20
+ */
+
+import cpp
+import semmle.code.cpp.valuenumbering.GlobalValueNumbering
+
+/** Holds for a function `f` that has an argument at index `apos` used for positioning in a buffer. */
+predicate numberArgument(Function f, int apos) {
+ f.hasGlobalOrStdName("write") and apos = 2
+ or
+ f.hasGlobalOrStdName("read") and apos = 2
+ or
+ f.hasGlobalOrStdName("lseek") and apos = 1
+ or
+ f.hasGlobalOrStdName("memmove") and apos = 2
+ or
+ f.hasGlobalOrStdName("memset") and apos = 2
+ or
+ f.hasGlobalOrStdName("memcpy") and apos = 2
+ or
+ f.hasGlobalOrStdName("memcmp") and apos = 2
+ or
+ f.hasGlobalOrStdName("strncat") and apos = 2
+ or
+ f.hasGlobalOrStdName("strncpy") and apos = 2
+ or
+ f.hasGlobalOrStdName("strncmp") and apos = 2
+ or
+ f.hasGlobalOrStdName("snprintf") and apos = 1
+ or
+ f.hasGlobalOrStdName("strndup") and apos = 2
+}
+
+class IfCompareWithZero extends IfStmt {
+ IfCompareWithZero() { this.getCondition().(RelationalOperation).getAChild().getValue() = "0" }
+
+ Expr noZerroOperand() {
+ if this.getCondition().(RelationalOperation).getGreaterOperand().getValue() = "0"
+ then result = this.getCondition().(RelationalOperation).getLesserOperand()
+ else result = this.getCondition().(RelationalOperation).getGreaterOperand()
+ }
+}
+
+from FunctionCall fc, IfCompareWithZero ifc, int na
+where
+ numberArgument(fc.getTarget(), na) and
+ globalValueNumber(fc.getArgument(na)) = globalValueNumber(ifc.noZerroOperand()) and
+ dominates(fc, ifc) and
+ not exists(IfStmt ifc1 |
+ dominates(ifc1, fc) and
+ globalValueNumber(fc.getArgument(na)) = globalValueNumber(ifc1.getCondition().getAChild*())
+ )
+select fc,
+ "The value of argument '$@' appears to be checked after the call, rather than before it.",
+ fc.getArgument(na), fc.getArgument(na).toString()
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-14/CompilerRemovalOfCodeToClearBuffers.c b/cpp/ql/src/experimental/Security/CWE/CWE-14/CompilerRemovalOfCodeToClearBuffers.c
deleted file mode 100644
index 14dd07a573e..00000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-14/CompilerRemovalOfCodeToClearBuffers.c
+++ /dev/null
@@ -1,35 +0,0 @@
-// BAD: the memset call will probably be removed.
-void getPassword(void) {
- char pwd[64];
- if (GetPassword(pwd, sizeof(pwd))) {
- /* Checking of password, secure operations, etc. */
- }
- memset(pwd, 0, sizeof(pwd));
-}
-// GOOD: in this case the memset will not be removed.
-void getPassword(void) {
- char pwd[64];
-
- if (retrievePassword(pwd, sizeof(pwd))) {
- /* Checking of password, secure operations, etc. */
- }
- memset_s(pwd, 0, sizeof(pwd));
-}
-// GOOD: in this case the memset will not be removed.
-void getPassword(void) {
- char pwd[64];
- if (retrievePassword(pwd, sizeof(pwd))) {
- /* Checking of password, secure operations, etc. */
- }
- SecureZeroMemory(pwd, sizeof(pwd));
-}
-// GOOD: in this case the memset will not be removed.
-void getPassword(void) {
- char pwd[64];
- if (retrievePassword(pwd, sizeof(pwd))) {
- /* Checking of password, secure operations, etc. */
- }
-#pragma optimize("", off)
- memset(pwd, 0, sizeof(pwd));
-#pragma optimize("", on)
-}
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-14/CompilerRemovalOfCodeToClearBuffers.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-14/CompilerRemovalOfCodeToClearBuffers.qhelp
deleted file mode 100644
index df0ed151d8f..00000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-14/CompilerRemovalOfCodeToClearBuffers.qhelp
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-Compiler optimization will exclude the cleaning of private information.
-Using the memset function to clear private data in a variable that has no subsequent use is potentially dangerous, since the compiler can remove the call.
-For some compilers, optimization is also possible when using calls to free memory after the memset function.
-
-It is possible to miss detection of vulnerabilities if used to clear fields of structures or parts of a buffer.
-
-
-
-
-We recommend to use the RtlSecureZeroMemory or memset_s functions, or compilation flags that exclude optimization of memset calls (e.g. -fno-builtin-memset).
-
-
-
-The following example demonstrates an erroneous and corrected use of the memset function.
-
-
-
-
-
-
- CERT C Coding Standard:
- MSC06-C. Beware of compiler optimizations.
-
-
-
-
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-14/CompilerRemovalOfCodeToClearBuffers.ql b/cpp/ql/src/experimental/Security/CWE/CWE-14/CompilerRemovalOfCodeToClearBuffers.ql
deleted file mode 100644
index db09de9430d..00000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-14/CompilerRemovalOfCodeToClearBuffers.ql
+++ /dev/null
@@ -1,127 +0,0 @@
-/**
- * @name Compiler Removal Of Code To Clear Buffers
- * @description Using memset the function to clear private data in a variable that has no subsequent use
- * is potentially dangerous because the compiler can remove the call.
- * @kind problem
- * @id cpp/compiler-removal-of-code-to-clear-buffers
- * @problem.severity warning
- * @precision medium
- * @tags security
- * external/cwe/cwe-14
- */
-
-import cpp
-import semmle.code.cpp.dataflow.DataFlow
-import semmle.code.cpp.dataflow.StackAddress
-
-/**
- * A call to `memset` of the form `memset(ptr, value, num)`, for some local variable `ptr`.
- */
-class CompilerRemovaMemset extends FunctionCall {
- CompilerRemovaMemset() {
- this.getTarget().hasGlobalOrStdName("memset") and
- exists(DataFlow::Node source, DataFlow::Node sink, LocalVariable isv, Expr exp |
- DataFlow::localFlow(source, sink) and
- this.getArgument(0) = isv.getAnAccess() and
- (
- source.asExpr() = exp
- or
- // handle the case where exp is defined by an address being passed into some function.
- source.asDefiningArgument() = exp
- ) and
- exp.getLocation().getEndLine() < this.getArgument(0).getLocation().getStartLine() and
- sink.asExpr() = this.getArgument(0)
- )
- }
-
- predicate isExistsAllocForThisVariable() {
- exists(AllocationExpr alloc, Variable v |
- alloc = v.getAnAssignedValue() and
- this.getArgument(0) = v.getAnAccess() and
- alloc.getASuccessor+() = this
- )
- or
- not stackPointerFlowsToUse(this.getArgument(0), _, _, _)
- }
-
- predicate isExistsFreeForThisVariable() {
- exists(DeallocationExpr free, Variable v |
- this.getArgument(0) = v.getAnAccess() and
- free.getFreedExpr() = v.getAnAccess() and
- this.getASuccessor+() = free
- )
- }
-
- predicate isExistsCallWithThisVariableExcludingDeallocationCalls() {
- exists(FunctionCall fc, Variable v |
- not fc instanceof DeallocationExpr and
- this.getArgument(0) = v.getAnAccess() and
- fc.getAnArgument() = v.getAnAccess() and
- this.getASuccessor+() = fc
- )
- }
-
- predicate isVariableUseAfterMemsetExcludingCalls() {
- exists(DataFlow::Node source, DataFlow::Node sink, LocalVariable isv, Expr exp |
- DataFlow::localFlow(source, sink) and
- this.getArgument(0) = isv.getAnAccess() and
- source.asExpr() = isv.getAnAccess() and
- exp.getLocation().getStartLine() > this.getArgument(2).getLocation().getEndLine() and
- not exp.getParent() instanceof FunctionCall and
- sink.asExpr() = exp
- )
- }
-
- predicate isVariableUseBoundWithArgumentFunction() {
- exists(DataFlow::Node source, DataFlow::Node sink, LocalVariable isv, Parameter p, Expr exp |
- DataFlow::localFlow(source, sink) and
- this.getArgument(0) = isv.getAnAccess() and
- this.getEnclosingFunction().getAParameter() = p and
- exp.getAChild*() = p.getAnAccess() and
- source.asExpr() = exp and
- sink.asExpr() = isv.getAnAccess()
- )
- }
-
- predicate isVariableUseBoundWithGlobalVariable() {
- exists(
- DataFlow::Node source, DataFlow::Node sink, LocalVariable isv, GlobalVariable gv, Expr exp
- |
- DataFlow::localFlow(source, sink) and
- this.getArgument(0) = isv.getAnAccess() and
- exp.getAChild*() = gv.getAnAccess() and
- source.asExpr() = exp and
- sink.asExpr() = isv.getAnAccess()
- )
- }
-
- predicate isExistsCompilationFlagsBlockingRemoval() {
- exists(Compilation c |
- c.getAFileCompiled() = this.getFile() and
- c.getAnArgument() = "-fno-builtin-memset"
- )
- }
-
- predicate isUseVCCompilation() {
- exists(Compilation c |
- c.getAFileCompiled() = this.getFile() and
- (
- c.getArgument(2).matches("%gcc%") or
- c.getArgument(2).matches("%g++%") or
- c.getArgument(2).matches("%clang%") or
- c.getArgument(2) = "--force-recompute"
- )
- )
- }
-}
-
-from CompilerRemovaMemset fc
-where
- not (fc.isExistsAllocForThisVariable() and not fc.isExistsFreeForThisVariable()) and
- not (fc.isExistsFreeForThisVariable() and not fc.isUseVCCompilation()) and
- not fc.isVariableUseAfterMemsetExcludingCalls() and
- not fc.isExistsCallWithThisVariableExcludingDeallocationCalls() and
- not fc.isVariableUseBoundWithArgumentFunction() and
- not fc.isVariableUseBoundWithGlobalVariable() and
- not fc.isExistsCompilationFlagsBlockingRemoval()
-select fc.getArgument(0), "This variable will not be cleared."
diff --git a/cpp/ql/src/jsf/4.05 Libraries/AV Rule 24.qhelp b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 24.qhelp
index 70c8460c835..1a82992c1c2 100644
--- a/cpp/ql/src/jsf/4.05 Libraries/AV Rule 24.qhelp
+++ b/cpp/ql/src/jsf/4.05 Libraries/AV Rule 24.qhelp
@@ -7,7 +7,7 @@
-
+
This query highlights calls to the standard library functions abort, exit, getenv and system.
diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql
index 3cd944839f1..d582cfa4224 100644
--- a/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql
+++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql
@@ -13,6 +13,7 @@
import cpp
import Critical.NewDelete
+import semmle.code.cpp.valuenumbering.GlobalValueNumbering
/**
* An expression that acquires a resource, and the kind of resource that is acquired. The
@@ -98,7 +99,8 @@ private predicate exprReleases(Expr e, Expr released, string kind) {
e.(FunctionCall).getTarget().(MemberFunction).getAnOverridingFunction+() = f
) and
e.(FunctionCall).getArgument(arg) = released and
- exprReleases(_, exprOrDereference(f.getParameter(arg).getAnAccess()), kind)
+ exprReleases(_,
+ exprOrDereference(globalValueNumber(f.getParameter(arg).getAnAccess()).getAnExpr()), kind)
)
or
exists(Function f, ThisExpr innerThis |
@@ -110,7 +112,7 @@ private predicate exprReleases(Expr e, Expr released, string kind) {
) and
e.(FunctionCall).getQualifier() = exprOrDereference(released) and
innerThis.getEnclosingFunction() = f and
- exprReleases(_, innerThis, kind)
+ exprReleases(_, globalValueNumber(innerThis).getAnExpr(), kind)
)
}
diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.qhelp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.qhelp
index 69cc5322ad5..ef4110774e0 100644
--- a/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.qhelp
+++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.qhelp
@@ -7,7 +7,7 @@
-
+
This query ensures that all operators with opposites (e.g. == and !=) are both defined, and
diff --git a/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.qhelp b/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.qhelp
index 614fe4711fd..89b6a2f2a88 100644
--- a/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.qhelp
+++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.qhelp
@@ -7,7 +7,7 @@
-
+
This query highlights return statements that return pointers to an object allocated on the stack. The lifetime
@@ -18,7 +18,7 @@ memory after the function has already returned will have undefined results.
-
+
diff --git a/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.qhelp b/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.qhelp
index 853324158e6..84c430179dd 100644
--- a/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.qhelp
+++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.qhelp
@@ -12,7 +12,7 @@ calling convention for x86, it would be whatever value was in the AX/EAX registe
assuming the function had a non-float return type that can fit in a machine word.
-
+
diff --git a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.qhelp b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.qhelp
index d67d84c5e06..c581940f7aa 100644
--- a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.qhelp
+++ b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.qhelp
@@ -7,7 +7,7 @@
-
+
This query highlights identifiers in an inner scope that hide (have the same name as) an identifier in an outer scope.
diff --git a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.qhelp b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.qhelp
index 73e66f0b006..4bb93abe739 100644
--- a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.qhelp
+++ b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.qhelp
@@ -7,7 +7,7 @@
-
+
This query highlights variables with the register storage class specifier. Modern compilers are now capable of
diff --git a/cpp/ql/src/jsf/4.17 Types/AV Rule 147.qhelp b/cpp/ql/src/jsf/4.17 Types/AV Rule 147.qhelp
index 1633cb2a867..0d01757523c 100644
--- a/cpp/ql/src/jsf/4.17 Types/AV Rule 147.qhelp
+++ b/cpp/ql/src/jsf/4.17 Types/AV Rule 147.qhelp
@@ -7,7 +7,7 @@
-
+
This query highlights portions of code that can expose the floating point implementation of the underlying
diff --git a/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.qhelp b/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.qhelp
index b30e828428c..dcb9c262ca9 100644
--- a/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.qhelp
+++ b/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.qhelp
@@ -7,7 +7,7 @@
-
+
This query highlights string literals that are assigned to a non-const variable. String literals
diff --git a/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.qhelp b/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.qhelp
index 946e0873ab0..89f9c654f08 100644
--- a/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.qhelp
+++ b/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.qhelp
@@ -7,7 +7,7 @@
-
+
This query finds bit fields with members that are not explicitly declared to be unsigned.
diff --git a/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.qhelp b/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.qhelp
index 1beb8b94f35..10a184c8284 100644
--- a/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.qhelp
+++ b/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.qhelp
@@ -7,7 +7,7 @@
-
+
This query finds unsigned values that are being negated. Behavior is undefined in such cases.
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 b9ef8a97deb..d4edec95db9 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
@@ -6,7 +6,7 @@
-
+
Use of goto statements makes code more difficult to understand and maintain. Consequently, the use
of goto statements is deprecated except as a mechanism for breaking out of multiple nested loops.
diff --git a/cpp/ql/src/jsf/jsfNote.qhelp b/cpp/ql/src/jsf/jsfNote.inc.qhelp
similarity index 100%
rename from cpp/ql/src/jsf/jsfNote.qhelp
rename to cpp/ql/src/jsf/jsfNote.inc.qhelp
diff --git a/cpp/ql/src/semmle/code/cpp/Diagnostics.qll b/cpp/ql/src/semmle/code/cpp/Diagnostics.qll
index 79074fa8657..9ad38d4e4be 100644
--- a/cpp/ql/src/semmle/code/cpp/Diagnostics.qll
+++ b/cpp/ql/src/semmle/code/cpp/Diagnostics.qll
@@ -6,6 +6,9 @@ import semmle.code.cpp.Location
/** A compiler-generated error, warning or remark. */
class Diagnostic extends Locatable, @diagnostic {
+ /** Gets the compilation that generated this diagnostic. */
+ Compilation getCompilation() { diagnostic_for(underlyingElement(this), result, _, _) }
+
/**
* Gets the severity of the message, on a range from 1 to 5: 1=remark,
* 2=warning, 3=discretionary error, 4=error, 5=catastrophic error.
diff --git a/cpp/ql/src/semmle/code/cpp/Element.qll b/cpp/ql/src/semmle/code/cpp/Element.qll
index 66f23ea110f..daa15e0f625 100644
--- a/cpp/ql/src/semmle/code/cpp/Element.qll
+++ b/cpp/ql/src/semmle/code/cpp/Element.qll
@@ -80,11 +80,9 @@ class Element extends ElementBase {
File getFile() { result = this.getLocation().getFile() }
/**
- * Holds if this element may be from source.
- *
- * Note: this predicate is provided for consistency with the libraries
- * for other languages, such as Java and Python. In C++, all files are
- * classified as source files, so this predicate is always true.
+ * Holds if this element may be from source. This predicate holds for all
+ * elements, except for those in the dummy file, whose name is the empty string.
+ * The dummy file contains declarations that are built directly into the compiler.
*/
predicate fromSource() { this.getFile().fromSource() }
diff --git a/cpp/ql/src/semmle/code/cpp/File.qll b/cpp/ql/src/semmle/code/cpp/File.qll
index c7486f4d75d..c2257fe294e 100644
--- a/cpp/ql/src/semmle/code/cpp/File.qll
+++ b/cpp/ql/src/semmle/code/cpp/File.qll
@@ -276,7 +276,10 @@ class File extends Container, @file {
c.getAFileCompiled() = this and
(
c.getAnArgument() = "--microsoft" or
- c.getAnArgument().toLowerCase().replaceAll("\\", "/").matches("%/cl.exe")
+ c.getAnArgument()
+ .toLowerCase()
+ .replaceAll("\\", "/")
+ .matches(["%/cl.exe", "%/clang-cl.exe"])
)
)
or
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 59cc8d529a7..8b446d28b86 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
@@ -207,23 +207,14 @@ private predicate fullBarrier(Node node, Configuration config) {
)
}
-private class AdditionalFlowStepSource extends Node {
- AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) }
-}
-
-pragma[noinline]
-private predicate isAdditionalFlowStep(
- AdditionalFlowStepSource node1, Node node2, DataFlowCallable callable1, Configuration config
-) {
- config.isAdditionalFlowStep(node1, node2) and
- callable1 = node1.getEnclosingCallable()
-}
-
/**
* 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) and
+ (
+ simpleLocalFlowStep(node1, node2) or
+ reverseStepThroughInputOutputAlias(node1, node2)
+ ) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
@@ -234,7 +225,8 @@ private predicate localFlowStep(Node node1, Node node2, Configuration config) {
* Holds if the additional step from `node1` to `node2` does not jump between callables.
*/
private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
- isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and
+ 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
@@ -256,14 +248,12 @@ private predicate jumpStep(Node node1, Node node2, Configuration config) {
* Holds if the additional step from `node1` to `node2` jumps between callables.
*/
private predicate additionalJumpStep(Node node1, Node node2, Configuration config) {
- exists(DataFlowCallable callable1 |
- isAdditionalFlowStep(node1, node2, callable1, config) and
- node2.getEnclosingCallable() != callable1 and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, 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)
}
/**
@@ -454,8 +444,8 @@ private module Stage1 {
// read
exists(Node mid, Content c |
read(node, c, mid) and
- fwdFlowConsCand(c, unbind(config)) and
- revFlow(mid, toReturn, config)
+ fwdFlowConsCand(c, pragma[only_bind_into](config)) and
+ revFlow(mid, toReturn, pragma[only_bind_into](config))
)
or
// flow into a callable
@@ -481,18 +471,18 @@ private module Stage1 {
pragma[nomagic]
private predicate revFlowConsCand(Content c, Configuration config) {
exists(Node mid, Node node |
- fwdFlow(node, unbind(config)) and
+ fwdFlow(node, pragma[only_bind_into](config)) and
read(node, c, mid) and
- fwdFlowConsCand(c, unbind(config)) and
- revFlow(mid, _, config)
+ 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 |
- revFlow(mid, toReturn, config) and
- fwdFlowConsCand(c, unbind(config)) and
+ revFlow(mid, toReturn, pragma[only_bind_into](config)) and
+ fwdFlowConsCand(c, pragma[only_bind_into](config)) and
store(node, tc, mid, _) and
c = tc.getContent()
)
@@ -562,8 +552,8 @@ private module Stage1 {
Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
) {
exists(Content c |
- revFlowIsReadAndStored(c, config) and
- revFlow(node2, unbind(config)) and
+ revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
+ revFlow(node2, pragma[only_bind_into](config)) and
store(node1, tc, node2, contentType) and
c = tc.getContent() and
exists(ap1)
@@ -572,8 +562,8 @@ private module Stage1 {
pragma[nomagic]
predicate readStepCand(Node n1, Content c, Node n2, Configuration config) {
- revFlowIsReadAndStored(c, config) and
- revFlow(n2, unbind(config)) and
+ revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
+ revFlow(n2, pragma[only_bind_into](config)) and
read(n1, c, n2)
}
@@ -598,7 +588,7 @@ private module Stage1 {
) {
exists(ReturnNodeExt ret |
throughFlowNodeCand(ret, config) and
- callable = ret.getEnclosingCallable() and
+ callable = getNodeEnclosingCallable(ret) and
kind = ret.getKind()
)
}
@@ -611,7 +601,7 @@ private module Stage1 {
exists(ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
- p.getEnclosingCallable() = c and
+ getNodeEnclosingCallable(p) = c and
exists(ap) and
// we don't expect a parameter to return stored in itself
not exists(int pos |
@@ -636,9 +626,6 @@ private module Stage1 {
/* End: Stage 1 logic. */
}
-bindingset[result, b]
-private boolean unbindBool(boolean b) { result != b.booleanNot() }
-
pragma[noinline]
private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
Stage1::revFlow(node2, config) and
@@ -775,7 +762,7 @@ private module Stage2 {
bindingset[result, ap]
private ApApprox getApprox(Ap ap) { any() }
- private ApNil getApNil(Node node) { any() }
+ private ApNil getApNil(Node 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) }
@@ -874,16 +861,16 @@ private module Stage2 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -963,7 +950,7 @@ private module Stage2 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -982,7 +969,7 @@ private module Stage2 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1055,9 +1042,9 @@ private module Stage2 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -1069,9 +1056,9 @@ private module Stage2 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -1124,9 +1111,10 @@ private module Stage2 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -1192,9 +1180,10 @@ private module Stage2 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ unbindBool(ap2), _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -1213,13 +1202,13 @@ private module Stage2 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -1250,8 +1239,8 @@ private predicate flowOutOfCallNodeCand2(
DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
- Stage2::revFlow(node2, config) and
- Stage2::revFlow(node1, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node1, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -1260,8 +1249,8 @@ private predicate flowIntoCallNodeCand2(
Configuration config
) {
flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
- Stage2::revFlow(node2, config) and
- Stage2::revFlow(node1, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node1, pragma[only_bind_into](config))
}
private module LocalFlowBigStep {
@@ -1316,8 +1305,8 @@ private module LocalFlowBigStep {
pragma[noinline]
private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
- Stage2::revFlow(node1, _, _, false, config) and
- Stage2::revFlow(node2, _, _, false, unbind(config))
+ Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config))
}
/**
@@ -1334,7 +1323,7 @@ private module LocalFlowBigStep {
) {
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
(
- localFlowEntry(node1, config) and
+ localFlowEntry(node1, pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
@@ -1345,24 +1334,24 @@ private module LocalFlowBigStep {
t = getNodeType(node2)
) and
node1 != node2 and
- cc.relevantFor(node1.getEnclosingCallable()) and
+ cc.relevantFor(getNodeEnclosingCallable(node1)) and
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
- Stage2::revFlow(node2, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
or
exists(Node mid |
- localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
+ 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, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
)
or
exists(Node mid |
- localFlowStepPlus(node1, mid, _, _, config, cc) and
+ 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
- Stage2::revFlow(node2, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
)
)
}
@@ -1394,7 +1383,9 @@ private module Stage3 {
private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(Node node) { result = TFrontNil(getNodeType(node)) }
+ private ApNil getApNil(Node node) {
+ PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node))
+ }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
@@ -1467,6 +1458,13 @@ private module Stage3 {
PrevStage::revFlow(node, _, _, apa, config)
}
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ exists(ApApprox apa0 |
+ apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
+ )
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -1478,7 +1476,7 @@ private module Stage3 {
pragma[nomagic]
predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
- flowCand(node, unbindBool(getApprox(ap)), config) and
+ flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
@@ -1502,16 +1500,16 @@ private module Stage3 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -1556,7 +1554,7 @@ private module Stage3 {
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindBool(getApprox(ap1)), tc, node2, contentType, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1591,7 +1589,7 @@ private module Stage3 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1610,7 +1608,7 @@ private module Stage3 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1635,7 +1633,7 @@ private module Stage3 {
) {
exists(ParameterNode p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindBool(getApprox(ap)), config)
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
}
@@ -1683,9 +1681,9 @@ private module Stage3 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -1697,9 +1695,9 @@ private module Stage3 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -1752,9 +1750,10 @@ private module Stage3 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -1820,9 +1819,10 @@ private module Stage3 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ ap2, _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -1841,13 +1841,13 @@ private module Stage3 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -2087,7 +2087,9 @@ private module Stage4 {
private ApApprox getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(Node node) { result = TNil(getNodeType(node)) }
+ private ApNil getApNil(Node node) {
+ PrevStage::revFlow(node, _) and result = TNil(getNodeType(node))
+ }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
@@ -2131,8 +2133,11 @@ private module Stage4 {
bindingset[node, cc, config]
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
- localFlowEntry(node, config) and
- result = getLocalCallContext(cc, node.getEnclosingCallable())
+ exists(Cc cc0 |
+ cc = pragma[only_bind_into](cc0) and
+ localFlowEntry(node, config) and
+ result = getLocalCallContext(cc0, getNodeEnclosingCallable(node))
+ )
}
private predicate localStep(
@@ -2147,8 +2152,8 @@ private module Stage4 {
Configuration config
) {
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, _, _, _, config) and
- PrevStage::revFlow(node1, _, _, _, unbind(config))
+ PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -2157,8 +2162,8 @@ private module Stage4 {
Configuration config
) {
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, _, _, _, config) and
- PrevStage::revFlow(node1, _, _, _, unbind(config))
+ PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config))
}
bindingset[node, ap]
@@ -2173,6 +2178,13 @@ private module Stage4 {
PrevStage::revFlow(node, _, _, apa, config)
}
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ exists(ApApprox apa0 |
+ apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
+ )
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -2184,7 +2196,7 @@ private module Stage4 {
pragma[nomagic]
predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
- flowCand(node, getApprox(ap), config) and
+ flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
@@ -2208,16 +2220,16 @@ private module Stage4 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -2262,7 +2274,7 @@ private module Stage4 {
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, getApprox(ap1), tc, node2, contentType, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -2297,7 +2309,7 @@ private module Stage4 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -2316,7 +2328,7 @@ private module Stage4 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -2341,7 +2353,7 @@ private module Stage4 {
) {
exists(ParameterNode p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
}
@@ -2389,9 +2401,9 @@ private module Stage4 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -2403,9 +2415,9 @@ private module Stage4 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -2458,9 +2470,10 @@ private module Stage4 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -2526,9 +2539,10 @@ private module Stage4 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ ap2, _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -2547,13 +2561,13 @@ private module Stage4 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -2580,14 +2594,16 @@ private module Stage4 {
}
bindingset[conf, result]
-private Configuration unbind(Configuration conf) { result >= conf and result <= conf }
+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) {
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
- n.getEnclosingCallable() = c
+ getNodeEnclosingCallable(n) = c
)
}
@@ -2750,13 +2766,13 @@ private newtype TPathNode =
// ... or a step from an existing PathNode to another node.
exists(PathNodeMid mid |
pathStep(mid, node, cc, sc, ap) and
- config = mid.getConfiguration() and
- Stage4::revFlow(node, _, _, ap.getApprox(), unbind(config))
+ pragma[only_bind_into](config) = mid.getConfiguration() and
+ Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(Node node, Configuration config) {
- config.isSink(node) and
- Stage4::revFlow(node, unbind(config)) and
+ pragma[only_bind_into](config).isSink(node) and
+ Stage4::revFlow(node, pragma[only_bind_into](config)) and
(
// A sink that is also a source ...
config.isSource(node)
@@ -2764,7 +2780,7 @@ private newtype TPathNode =
// ... or a sink that can be reached from a source
exists(PathNodeMid mid |
pathStep(mid, node, _, _, TAccessPathNil(_)) and
- config = unbind(mid.getConfiguration())
+ pragma[only_bind_into](config) = mid.getConfiguration()
)
)
}
@@ -3061,7 +3077,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
private PathNodeMid getSuccMid() {
pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and
- result.getConfiguration() = unbind(this.getConfiguration())
+ result.getConfiguration() = unbindConf(this.getConfiguration())
}
override PathNodeImpl getASuccessorImpl() {
@@ -3073,7 +3089,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
mid = getSuccMid() and
mid.getNode() = sink.getNode() and
mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbind(mid.getConfiguration()) and
+ sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
result = sink
)
}
@@ -3116,7 +3132,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
conf = mid.getConfiguration() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
- localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and
ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
@@ -3304,7 +3320,7 @@ private predicate pathThroughCallable0(
) {
exists(CallContext innercc, SummaryCtx sc |
pathIntoCallable(mid, _, cc, innercc, sc, call) and
- paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration()))
+ paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration()))
)
}
@@ -3316,7 +3332,7 @@ pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node 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, unbind(mid.getConfiguration()))
+ out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
)
}
@@ -3402,14 +3418,14 @@ private module FlowExploration {
// flow out of a callable
viableReturnPosOut(_, getReturnPosition(node1), node2)
|
- c1 = node1.getEnclosingCallable() and
- c2 = node2.getEnclosingCallable() and
+ c1 = getNodeEnclosingCallable(node1) and
+ c2 = getNodeEnclosingCallable(node2) and
c1 != c2
)
}
private predicate interestingCallableSrc(DataFlowCallable c, Configuration config) {
- exists(Node n | config.isSource(n) and c = n.getEnclosingCallable())
+ exists(Node n | config.isSource(n) and c = getNodeEnclosingCallable(n))
or
exists(DataFlowCallable mid |
interestingCallableSrc(mid, config) and callableStep(mid, c, config)
@@ -3417,7 +3433,7 @@ private module FlowExploration {
}
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
- exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
+ exists(Node n | config.isSink(n) and c = getNodeEnclosingCallable(n))
or
exists(DataFlowCallable mid |
interestingCallableSink(mid, config) and callableStep(c, mid, config)
@@ -3439,20 +3455,20 @@ private module FlowExploration {
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
callableStep(c1, c2, config) and
- ce1 = TCallable(c1, config) and
- ce2 = TCallable(c2, unbind(config))
+ ce1 = TCallable(c1, pragma[only_bind_into](config)) and
+ ce2 = TCallable(c2, pragma[only_bind_into](config))
)
or
exists(Node n, Configuration config |
ce1 = TCallableSrc() and
config.isSource(n) and
- ce2 = TCallable(n.getEnclosingCallable(), config)
+ ce2 = TCallable(getNodeEnclosingCallable(n), config)
)
or
exists(Node n, Configuration config |
ce2 = TCallableSink() and
config.isSink(n) and
- ce1 = TCallable(n.getEnclosingCallable(), config)
+ ce1 = TCallable(getNodeEnclosingCallable(n), config)
)
}
@@ -3583,7 +3599,7 @@ private module FlowExploration {
exists(config.explorationLimit())
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
- distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
+ distSrc(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
@@ -3600,7 +3616,7 @@ private module FlowExploration {
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not clearsContent(node, ap.getHead()) and
not fullBarrier(node, config) and
- distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
+ distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
)
}
@@ -3659,7 +3675,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSourceDistance() {
- result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
+ result = distSrc(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
}
/**
@@ -3667,7 +3683,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSinkDistance() {
- result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
+ result = distSink(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
}
private string ppAp() {
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 59cc8d529a7..8b446d28b86 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
@@ -207,23 +207,14 @@ private predicate fullBarrier(Node node, Configuration config) {
)
}
-private class AdditionalFlowStepSource extends Node {
- AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) }
-}
-
-pragma[noinline]
-private predicate isAdditionalFlowStep(
- AdditionalFlowStepSource node1, Node node2, DataFlowCallable callable1, Configuration config
-) {
- config.isAdditionalFlowStep(node1, node2) and
- callable1 = node1.getEnclosingCallable()
-}
-
/**
* 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) and
+ (
+ simpleLocalFlowStep(node1, node2) or
+ reverseStepThroughInputOutputAlias(node1, node2)
+ ) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
@@ -234,7 +225,8 @@ private predicate localFlowStep(Node node1, Node node2, Configuration config) {
* Holds if the additional step from `node1` to `node2` does not jump between callables.
*/
private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
- isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and
+ 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
@@ -256,14 +248,12 @@ private predicate jumpStep(Node node1, Node node2, Configuration config) {
* Holds if the additional step from `node1` to `node2` jumps between callables.
*/
private predicate additionalJumpStep(Node node1, Node node2, Configuration config) {
- exists(DataFlowCallable callable1 |
- isAdditionalFlowStep(node1, node2, callable1, config) and
- node2.getEnclosingCallable() != callable1 and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, 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)
}
/**
@@ -454,8 +444,8 @@ private module Stage1 {
// read
exists(Node mid, Content c |
read(node, c, mid) and
- fwdFlowConsCand(c, unbind(config)) and
- revFlow(mid, toReturn, config)
+ fwdFlowConsCand(c, pragma[only_bind_into](config)) and
+ revFlow(mid, toReturn, pragma[only_bind_into](config))
)
or
// flow into a callable
@@ -481,18 +471,18 @@ private module Stage1 {
pragma[nomagic]
private predicate revFlowConsCand(Content c, Configuration config) {
exists(Node mid, Node node |
- fwdFlow(node, unbind(config)) and
+ fwdFlow(node, pragma[only_bind_into](config)) and
read(node, c, mid) and
- fwdFlowConsCand(c, unbind(config)) and
- revFlow(mid, _, config)
+ 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 |
- revFlow(mid, toReturn, config) and
- fwdFlowConsCand(c, unbind(config)) and
+ revFlow(mid, toReturn, pragma[only_bind_into](config)) and
+ fwdFlowConsCand(c, pragma[only_bind_into](config)) and
store(node, tc, mid, _) and
c = tc.getContent()
)
@@ -562,8 +552,8 @@ private module Stage1 {
Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
) {
exists(Content c |
- revFlowIsReadAndStored(c, config) and
- revFlow(node2, unbind(config)) and
+ revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
+ revFlow(node2, pragma[only_bind_into](config)) and
store(node1, tc, node2, contentType) and
c = tc.getContent() and
exists(ap1)
@@ -572,8 +562,8 @@ private module Stage1 {
pragma[nomagic]
predicate readStepCand(Node n1, Content c, Node n2, Configuration config) {
- revFlowIsReadAndStored(c, config) and
- revFlow(n2, unbind(config)) and
+ revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
+ revFlow(n2, pragma[only_bind_into](config)) and
read(n1, c, n2)
}
@@ -598,7 +588,7 @@ private module Stage1 {
) {
exists(ReturnNodeExt ret |
throughFlowNodeCand(ret, config) and
- callable = ret.getEnclosingCallable() and
+ callable = getNodeEnclosingCallable(ret) and
kind = ret.getKind()
)
}
@@ -611,7 +601,7 @@ private module Stage1 {
exists(ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
- p.getEnclosingCallable() = c and
+ getNodeEnclosingCallable(p) = c and
exists(ap) and
// we don't expect a parameter to return stored in itself
not exists(int pos |
@@ -636,9 +626,6 @@ private module Stage1 {
/* End: Stage 1 logic. */
}
-bindingset[result, b]
-private boolean unbindBool(boolean b) { result != b.booleanNot() }
-
pragma[noinline]
private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
Stage1::revFlow(node2, config) and
@@ -775,7 +762,7 @@ private module Stage2 {
bindingset[result, ap]
private ApApprox getApprox(Ap ap) { any() }
- private ApNil getApNil(Node node) { any() }
+ private ApNil getApNil(Node 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) }
@@ -874,16 +861,16 @@ private module Stage2 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -963,7 +950,7 @@ private module Stage2 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -982,7 +969,7 @@ private module Stage2 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1055,9 +1042,9 @@ private module Stage2 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -1069,9 +1056,9 @@ private module Stage2 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -1124,9 +1111,10 @@ private module Stage2 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -1192,9 +1180,10 @@ private module Stage2 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ unbindBool(ap2), _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -1213,13 +1202,13 @@ private module Stage2 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -1250,8 +1239,8 @@ private predicate flowOutOfCallNodeCand2(
DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
- Stage2::revFlow(node2, config) and
- Stage2::revFlow(node1, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node1, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -1260,8 +1249,8 @@ private predicate flowIntoCallNodeCand2(
Configuration config
) {
flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
- Stage2::revFlow(node2, config) and
- Stage2::revFlow(node1, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node1, pragma[only_bind_into](config))
}
private module LocalFlowBigStep {
@@ -1316,8 +1305,8 @@ private module LocalFlowBigStep {
pragma[noinline]
private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
- Stage2::revFlow(node1, _, _, false, config) and
- Stage2::revFlow(node2, _, _, false, unbind(config))
+ Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config))
}
/**
@@ -1334,7 +1323,7 @@ private module LocalFlowBigStep {
) {
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
(
- localFlowEntry(node1, config) and
+ localFlowEntry(node1, pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
@@ -1345,24 +1334,24 @@ private module LocalFlowBigStep {
t = getNodeType(node2)
) and
node1 != node2 and
- cc.relevantFor(node1.getEnclosingCallable()) and
+ cc.relevantFor(getNodeEnclosingCallable(node1)) and
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
- Stage2::revFlow(node2, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
or
exists(Node mid |
- localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
+ 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, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
)
or
exists(Node mid |
- localFlowStepPlus(node1, mid, _, _, config, cc) and
+ 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
- Stage2::revFlow(node2, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
)
)
}
@@ -1394,7 +1383,9 @@ private module Stage3 {
private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(Node node) { result = TFrontNil(getNodeType(node)) }
+ private ApNil getApNil(Node node) {
+ PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node))
+ }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
@@ -1467,6 +1458,13 @@ private module Stage3 {
PrevStage::revFlow(node, _, _, apa, config)
}
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ exists(ApApprox apa0 |
+ apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
+ )
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -1478,7 +1476,7 @@ private module Stage3 {
pragma[nomagic]
predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
- flowCand(node, unbindBool(getApprox(ap)), config) and
+ flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
@@ -1502,16 +1500,16 @@ private module Stage3 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -1556,7 +1554,7 @@ private module Stage3 {
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindBool(getApprox(ap1)), tc, node2, contentType, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1591,7 +1589,7 @@ private module Stage3 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1610,7 +1608,7 @@ private module Stage3 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1635,7 +1633,7 @@ private module Stage3 {
) {
exists(ParameterNode p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindBool(getApprox(ap)), config)
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
}
@@ -1683,9 +1681,9 @@ private module Stage3 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -1697,9 +1695,9 @@ private module Stage3 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -1752,9 +1750,10 @@ private module Stage3 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -1820,9 +1819,10 @@ private module Stage3 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ ap2, _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -1841,13 +1841,13 @@ private module Stage3 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -2087,7 +2087,9 @@ private module Stage4 {
private ApApprox getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(Node node) { result = TNil(getNodeType(node)) }
+ private ApNil getApNil(Node node) {
+ PrevStage::revFlow(node, _) and result = TNil(getNodeType(node))
+ }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
@@ -2131,8 +2133,11 @@ private module Stage4 {
bindingset[node, cc, config]
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
- localFlowEntry(node, config) and
- result = getLocalCallContext(cc, node.getEnclosingCallable())
+ exists(Cc cc0 |
+ cc = pragma[only_bind_into](cc0) and
+ localFlowEntry(node, config) and
+ result = getLocalCallContext(cc0, getNodeEnclosingCallable(node))
+ )
}
private predicate localStep(
@@ -2147,8 +2152,8 @@ private module Stage4 {
Configuration config
) {
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, _, _, _, config) and
- PrevStage::revFlow(node1, _, _, _, unbind(config))
+ PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -2157,8 +2162,8 @@ private module Stage4 {
Configuration config
) {
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, _, _, _, config) and
- PrevStage::revFlow(node1, _, _, _, unbind(config))
+ PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config))
}
bindingset[node, ap]
@@ -2173,6 +2178,13 @@ private module Stage4 {
PrevStage::revFlow(node, _, _, apa, config)
}
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ exists(ApApprox apa0 |
+ apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
+ )
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -2184,7 +2196,7 @@ private module Stage4 {
pragma[nomagic]
predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
- flowCand(node, getApprox(ap), config) and
+ flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
@@ -2208,16 +2220,16 @@ private module Stage4 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -2262,7 +2274,7 @@ private module Stage4 {
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, getApprox(ap1), tc, node2, contentType, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -2297,7 +2309,7 @@ private module Stage4 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -2316,7 +2328,7 @@ private module Stage4 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -2341,7 +2353,7 @@ private module Stage4 {
) {
exists(ParameterNode p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
}
@@ -2389,9 +2401,9 @@ private module Stage4 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -2403,9 +2415,9 @@ private module Stage4 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -2458,9 +2470,10 @@ private module Stage4 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -2526,9 +2539,10 @@ private module Stage4 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ ap2, _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -2547,13 +2561,13 @@ private module Stage4 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -2580,14 +2594,16 @@ private module Stage4 {
}
bindingset[conf, result]
-private Configuration unbind(Configuration conf) { result >= conf and result <= conf }
+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) {
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
- n.getEnclosingCallable() = c
+ getNodeEnclosingCallable(n) = c
)
}
@@ -2750,13 +2766,13 @@ private newtype TPathNode =
// ... or a step from an existing PathNode to another node.
exists(PathNodeMid mid |
pathStep(mid, node, cc, sc, ap) and
- config = mid.getConfiguration() and
- Stage4::revFlow(node, _, _, ap.getApprox(), unbind(config))
+ pragma[only_bind_into](config) = mid.getConfiguration() and
+ Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(Node node, Configuration config) {
- config.isSink(node) and
- Stage4::revFlow(node, unbind(config)) and
+ pragma[only_bind_into](config).isSink(node) and
+ Stage4::revFlow(node, pragma[only_bind_into](config)) and
(
// A sink that is also a source ...
config.isSource(node)
@@ -2764,7 +2780,7 @@ private newtype TPathNode =
// ... or a sink that can be reached from a source
exists(PathNodeMid mid |
pathStep(mid, node, _, _, TAccessPathNil(_)) and
- config = unbind(mid.getConfiguration())
+ pragma[only_bind_into](config) = mid.getConfiguration()
)
)
}
@@ -3061,7 +3077,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
private PathNodeMid getSuccMid() {
pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and
- result.getConfiguration() = unbind(this.getConfiguration())
+ result.getConfiguration() = unbindConf(this.getConfiguration())
}
override PathNodeImpl getASuccessorImpl() {
@@ -3073,7 +3089,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
mid = getSuccMid() and
mid.getNode() = sink.getNode() and
mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbind(mid.getConfiguration()) and
+ sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
result = sink
)
}
@@ -3116,7 +3132,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
conf = mid.getConfiguration() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
- localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and
ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
@@ -3304,7 +3320,7 @@ private predicate pathThroughCallable0(
) {
exists(CallContext innercc, SummaryCtx sc |
pathIntoCallable(mid, _, cc, innercc, sc, call) and
- paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration()))
+ paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration()))
)
}
@@ -3316,7 +3332,7 @@ pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node 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, unbind(mid.getConfiguration()))
+ out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
)
}
@@ -3402,14 +3418,14 @@ private module FlowExploration {
// flow out of a callable
viableReturnPosOut(_, getReturnPosition(node1), node2)
|
- c1 = node1.getEnclosingCallable() and
- c2 = node2.getEnclosingCallable() and
+ c1 = getNodeEnclosingCallable(node1) and
+ c2 = getNodeEnclosingCallable(node2) and
c1 != c2
)
}
private predicate interestingCallableSrc(DataFlowCallable c, Configuration config) {
- exists(Node n | config.isSource(n) and c = n.getEnclosingCallable())
+ exists(Node n | config.isSource(n) and c = getNodeEnclosingCallable(n))
or
exists(DataFlowCallable mid |
interestingCallableSrc(mid, config) and callableStep(mid, c, config)
@@ -3417,7 +3433,7 @@ private module FlowExploration {
}
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
- exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
+ exists(Node n | config.isSink(n) and c = getNodeEnclosingCallable(n))
or
exists(DataFlowCallable mid |
interestingCallableSink(mid, config) and callableStep(c, mid, config)
@@ -3439,20 +3455,20 @@ private module FlowExploration {
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
callableStep(c1, c2, config) and
- ce1 = TCallable(c1, config) and
- ce2 = TCallable(c2, unbind(config))
+ ce1 = TCallable(c1, pragma[only_bind_into](config)) and
+ ce2 = TCallable(c2, pragma[only_bind_into](config))
)
or
exists(Node n, Configuration config |
ce1 = TCallableSrc() and
config.isSource(n) and
- ce2 = TCallable(n.getEnclosingCallable(), config)
+ ce2 = TCallable(getNodeEnclosingCallable(n), config)
)
or
exists(Node n, Configuration config |
ce2 = TCallableSink() and
config.isSink(n) and
- ce1 = TCallable(n.getEnclosingCallable(), config)
+ ce1 = TCallable(getNodeEnclosingCallable(n), config)
)
}
@@ -3583,7 +3599,7 @@ private module FlowExploration {
exists(config.explorationLimit())
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
- distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
+ distSrc(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
@@ -3600,7 +3616,7 @@ private module FlowExploration {
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not clearsContent(node, ap.getHead()) and
not fullBarrier(node, config) and
- distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
+ distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
)
}
@@ -3659,7 +3675,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSourceDistance() {
- result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
+ result = distSrc(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
}
/**
@@ -3667,7 +3683,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSinkDistance() {
- result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
+ result = distSink(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
}
private string ppAp() {
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 59cc8d529a7..8b446d28b86 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
@@ -207,23 +207,14 @@ private predicate fullBarrier(Node node, Configuration config) {
)
}
-private class AdditionalFlowStepSource extends Node {
- AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) }
-}
-
-pragma[noinline]
-private predicate isAdditionalFlowStep(
- AdditionalFlowStepSource node1, Node node2, DataFlowCallable callable1, Configuration config
-) {
- config.isAdditionalFlowStep(node1, node2) and
- callable1 = node1.getEnclosingCallable()
-}
-
/**
* 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) and
+ (
+ simpleLocalFlowStep(node1, node2) or
+ reverseStepThroughInputOutputAlias(node1, node2)
+ ) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
@@ -234,7 +225,8 @@ private predicate localFlowStep(Node node1, Node node2, Configuration config) {
* Holds if the additional step from `node1` to `node2` does not jump between callables.
*/
private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
- isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and
+ 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
@@ -256,14 +248,12 @@ private predicate jumpStep(Node node1, Node node2, Configuration config) {
* Holds if the additional step from `node1` to `node2` jumps between callables.
*/
private predicate additionalJumpStep(Node node1, Node node2, Configuration config) {
- exists(DataFlowCallable callable1 |
- isAdditionalFlowStep(node1, node2, callable1, config) and
- node2.getEnclosingCallable() != callable1 and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, 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)
}
/**
@@ -454,8 +444,8 @@ private module Stage1 {
// read
exists(Node mid, Content c |
read(node, c, mid) and
- fwdFlowConsCand(c, unbind(config)) and
- revFlow(mid, toReturn, config)
+ fwdFlowConsCand(c, pragma[only_bind_into](config)) and
+ revFlow(mid, toReturn, pragma[only_bind_into](config))
)
or
// flow into a callable
@@ -481,18 +471,18 @@ private module Stage1 {
pragma[nomagic]
private predicate revFlowConsCand(Content c, Configuration config) {
exists(Node mid, Node node |
- fwdFlow(node, unbind(config)) and
+ fwdFlow(node, pragma[only_bind_into](config)) and
read(node, c, mid) and
- fwdFlowConsCand(c, unbind(config)) and
- revFlow(mid, _, config)
+ 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 |
- revFlow(mid, toReturn, config) and
- fwdFlowConsCand(c, unbind(config)) and
+ revFlow(mid, toReturn, pragma[only_bind_into](config)) and
+ fwdFlowConsCand(c, pragma[only_bind_into](config)) and
store(node, tc, mid, _) and
c = tc.getContent()
)
@@ -562,8 +552,8 @@ private module Stage1 {
Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
) {
exists(Content c |
- revFlowIsReadAndStored(c, config) and
- revFlow(node2, unbind(config)) and
+ revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
+ revFlow(node2, pragma[only_bind_into](config)) and
store(node1, tc, node2, contentType) and
c = tc.getContent() and
exists(ap1)
@@ -572,8 +562,8 @@ private module Stage1 {
pragma[nomagic]
predicate readStepCand(Node n1, Content c, Node n2, Configuration config) {
- revFlowIsReadAndStored(c, config) and
- revFlow(n2, unbind(config)) and
+ revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
+ revFlow(n2, pragma[only_bind_into](config)) and
read(n1, c, n2)
}
@@ -598,7 +588,7 @@ private module Stage1 {
) {
exists(ReturnNodeExt ret |
throughFlowNodeCand(ret, config) and
- callable = ret.getEnclosingCallable() and
+ callable = getNodeEnclosingCallable(ret) and
kind = ret.getKind()
)
}
@@ -611,7 +601,7 @@ private module Stage1 {
exists(ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
- p.getEnclosingCallable() = c and
+ getNodeEnclosingCallable(p) = c and
exists(ap) and
// we don't expect a parameter to return stored in itself
not exists(int pos |
@@ -636,9 +626,6 @@ private module Stage1 {
/* End: Stage 1 logic. */
}
-bindingset[result, b]
-private boolean unbindBool(boolean b) { result != b.booleanNot() }
-
pragma[noinline]
private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
Stage1::revFlow(node2, config) and
@@ -775,7 +762,7 @@ private module Stage2 {
bindingset[result, ap]
private ApApprox getApprox(Ap ap) { any() }
- private ApNil getApNil(Node node) { any() }
+ private ApNil getApNil(Node 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) }
@@ -874,16 +861,16 @@ private module Stage2 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -963,7 +950,7 @@ private module Stage2 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -982,7 +969,7 @@ private module Stage2 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1055,9 +1042,9 @@ private module Stage2 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -1069,9 +1056,9 @@ private module Stage2 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -1124,9 +1111,10 @@ private module Stage2 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -1192,9 +1180,10 @@ private module Stage2 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ unbindBool(ap2), _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -1213,13 +1202,13 @@ private module Stage2 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -1250,8 +1239,8 @@ private predicate flowOutOfCallNodeCand2(
DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
- Stage2::revFlow(node2, config) and
- Stage2::revFlow(node1, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node1, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -1260,8 +1249,8 @@ private predicate flowIntoCallNodeCand2(
Configuration config
) {
flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
- Stage2::revFlow(node2, config) and
- Stage2::revFlow(node1, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node1, pragma[only_bind_into](config))
}
private module LocalFlowBigStep {
@@ -1316,8 +1305,8 @@ private module LocalFlowBigStep {
pragma[noinline]
private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
- Stage2::revFlow(node1, _, _, false, config) and
- Stage2::revFlow(node2, _, _, false, unbind(config))
+ Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config))
}
/**
@@ -1334,7 +1323,7 @@ private module LocalFlowBigStep {
) {
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
(
- localFlowEntry(node1, config) and
+ localFlowEntry(node1, pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
@@ -1345,24 +1334,24 @@ private module LocalFlowBigStep {
t = getNodeType(node2)
) and
node1 != node2 and
- cc.relevantFor(node1.getEnclosingCallable()) and
+ cc.relevantFor(getNodeEnclosingCallable(node1)) and
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
- Stage2::revFlow(node2, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
or
exists(Node mid |
- localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
+ 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, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
)
or
exists(Node mid |
- localFlowStepPlus(node1, mid, _, _, config, cc) and
+ 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
- Stage2::revFlow(node2, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
)
)
}
@@ -1394,7 +1383,9 @@ private module Stage3 {
private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(Node node) { result = TFrontNil(getNodeType(node)) }
+ private ApNil getApNil(Node node) {
+ PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node))
+ }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
@@ -1467,6 +1458,13 @@ private module Stage3 {
PrevStage::revFlow(node, _, _, apa, config)
}
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ exists(ApApprox apa0 |
+ apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
+ )
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -1478,7 +1476,7 @@ private module Stage3 {
pragma[nomagic]
predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
- flowCand(node, unbindBool(getApprox(ap)), config) and
+ flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
@@ -1502,16 +1500,16 @@ private module Stage3 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -1556,7 +1554,7 @@ private module Stage3 {
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindBool(getApprox(ap1)), tc, node2, contentType, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1591,7 +1589,7 @@ private module Stage3 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1610,7 +1608,7 @@ private module Stage3 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1635,7 +1633,7 @@ private module Stage3 {
) {
exists(ParameterNode p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindBool(getApprox(ap)), config)
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
}
@@ -1683,9 +1681,9 @@ private module Stage3 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -1697,9 +1695,9 @@ private module Stage3 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -1752,9 +1750,10 @@ private module Stage3 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -1820,9 +1819,10 @@ private module Stage3 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ ap2, _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -1841,13 +1841,13 @@ private module Stage3 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -2087,7 +2087,9 @@ private module Stage4 {
private ApApprox getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(Node node) { result = TNil(getNodeType(node)) }
+ private ApNil getApNil(Node node) {
+ PrevStage::revFlow(node, _) and result = TNil(getNodeType(node))
+ }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
@@ -2131,8 +2133,11 @@ private module Stage4 {
bindingset[node, cc, config]
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
- localFlowEntry(node, config) and
- result = getLocalCallContext(cc, node.getEnclosingCallable())
+ exists(Cc cc0 |
+ cc = pragma[only_bind_into](cc0) and
+ localFlowEntry(node, config) and
+ result = getLocalCallContext(cc0, getNodeEnclosingCallable(node))
+ )
}
private predicate localStep(
@@ -2147,8 +2152,8 @@ private module Stage4 {
Configuration config
) {
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, _, _, _, config) and
- PrevStage::revFlow(node1, _, _, _, unbind(config))
+ PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -2157,8 +2162,8 @@ private module Stage4 {
Configuration config
) {
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, _, _, _, config) and
- PrevStage::revFlow(node1, _, _, _, unbind(config))
+ PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config))
}
bindingset[node, ap]
@@ -2173,6 +2178,13 @@ private module Stage4 {
PrevStage::revFlow(node, _, _, apa, config)
}
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ exists(ApApprox apa0 |
+ apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
+ )
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -2184,7 +2196,7 @@ private module Stage4 {
pragma[nomagic]
predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
- flowCand(node, getApprox(ap), config) and
+ flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
@@ -2208,16 +2220,16 @@ private module Stage4 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -2262,7 +2274,7 @@ private module Stage4 {
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, getApprox(ap1), tc, node2, contentType, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -2297,7 +2309,7 @@ private module Stage4 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -2316,7 +2328,7 @@ private module Stage4 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -2341,7 +2353,7 @@ private module Stage4 {
) {
exists(ParameterNode p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
}
@@ -2389,9 +2401,9 @@ private module Stage4 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -2403,9 +2415,9 @@ private module Stage4 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -2458,9 +2470,10 @@ private module Stage4 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -2526,9 +2539,10 @@ private module Stage4 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ ap2, _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -2547,13 +2561,13 @@ private module Stage4 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -2580,14 +2594,16 @@ private module Stage4 {
}
bindingset[conf, result]
-private Configuration unbind(Configuration conf) { result >= conf and result <= conf }
+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) {
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
- n.getEnclosingCallable() = c
+ getNodeEnclosingCallable(n) = c
)
}
@@ -2750,13 +2766,13 @@ private newtype TPathNode =
// ... or a step from an existing PathNode to another node.
exists(PathNodeMid mid |
pathStep(mid, node, cc, sc, ap) and
- config = mid.getConfiguration() and
- Stage4::revFlow(node, _, _, ap.getApprox(), unbind(config))
+ pragma[only_bind_into](config) = mid.getConfiguration() and
+ Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(Node node, Configuration config) {
- config.isSink(node) and
- Stage4::revFlow(node, unbind(config)) and
+ pragma[only_bind_into](config).isSink(node) and
+ Stage4::revFlow(node, pragma[only_bind_into](config)) and
(
// A sink that is also a source ...
config.isSource(node)
@@ -2764,7 +2780,7 @@ private newtype TPathNode =
// ... or a sink that can be reached from a source
exists(PathNodeMid mid |
pathStep(mid, node, _, _, TAccessPathNil(_)) and
- config = unbind(mid.getConfiguration())
+ pragma[only_bind_into](config) = mid.getConfiguration()
)
)
}
@@ -3061,7 +3077,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
private PathNodeMid getSuccMid() {
pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and
- result.getConfiguration() = unbind(this.getConfiguration())
+ result.getConfiguration() = unbindConf(this.getConfiguration())
}
override PathNodeImpl getASuccessorImpl() {
@@ -3073,7 +3089,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
mid = getSuccMid() and
mid.getNode() = sink.getNode() and
mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbind(mid.getConfiguration()) and
+ sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
result = sink
)
}
@@ -3116,7 +3132,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
conf = mid.getConfiguration() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
- localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and
ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
@@ -3304,7 +3320,7 @@ private predicate pathThroughCallable0(
) {
exists(CallContext innercc, SummaryCtx sc |
pathIntoCallable(mid, _, cc, innercc, sc, call) and
- paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration()))
+ paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration()))
)
}
@@ -3316,7 +3332,7 @@ pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node 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, unbind(mid.getConfiguration()))
+ out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
)
}
@@ -3402,14 +3418,14 @@ private module FlowExploration {
// flow out of a callable
viableReturnPosOut(_, getReturnPosition(node1), node2)
|
- c1 = node1.getEnclosingCallable() and
- c2 = node2.getEnclosingCallable() and
+ c1 = getNodeEnclosingCallable(node1) and
+ c2 = getNodeEnclosingCallable(node2) and
c1 != c2
)
}
private predicate interestingCallableSrc(DataFlowCallable c, Configuration config) {
- exists(Node n | config.isSource(n) and c = n.getEnclosingCallable())
+ exists(Node n | config.isSource(n) and c = getNodeEnclosingCallable(n))
or
exists(DataFlowCallable mid |
interestingCallableSrc(mid, config) and callableStep(mid, c, config)
@@ -3417,7 +3433,7 @@ private module FlowExploration {
}
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
- exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
+ exists(Node n | config.isSink(n) and c = getNodeEnclosingCallable(n))
or
exists(DataFlowCallable mid |
interestingCallableSink(mid, config) and callableStep(c, mid, config)
@@ -3439,20 +3455,20 @@ private module FlowExploration {
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
callableStep(c1, c2, config) and
- ce1 = TCallable(c1, config) and
- ce2 = TCallable(c2, unbind(config))
+ ce1 = TCallable(c1, pragma[only_bind_into](config)) and
+ ce2 = TCallable(c2, pragma[only_bind_into](config))
)
or
exists(Node n, Configuration config |
ce1 = TCallableSrc() and
config.isSource(n) and
- ce2 = TCallable(n.getEnclosingCallable(), config)
+ ce2 = TCallable(getNodeEnclosingCallable(n), config)
)
or
exists(Node n, Configuration config |
ce2 = TCallableSink() and
config.isSink(n) and
- ce1 = TCallable(n.getEnclosingCallable(), config)
+ ce1 = TCallable(getNodeEnclosingCallable(n), config)
)
}
@@ -3583,7 +3599,7 @@ private module FlowExploration {
exists(config.explorationLimit())
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
- distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
+ distSrc(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
@@ -3600,7 +3616,7 @@ private module FlowExploration {
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not clearsContent(node, ap.getHead()) and
not fullBarrier(node, config) and
- distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
+ distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
)
}
@@ -3659,7 +3675,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSourceDistance() {
- result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
+ result = distSrc(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
}
/**
@@ -3667,7 +3683,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSinkDistance() {
- result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
+ result = distSink(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
}
private string ppAp() {
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 59cc8d529a7..8b446d28b86 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
@@ -207,23 +207,14 @@ private predicate fullBarrier(Node node, Configuration config) {
)
}
-private class AdditionalFlowStepSource extends Node {
- AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) }
-}
-
-pragma[noinline]
-private predicate isAdditionalFlowStep(
- AdditionalFlowStepSource node1, Node node2, DataFlowCallable callable1, Configuration config
-) {
- config.isAdditionalFlowStep(node1, node2) and
- callable1 = node1.getEnclosingCallable()
-}
-
/**
* 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) and
+ (
+ simpleLocalFlowStep(node1, node2) or
+ reverseStepThroughInputOutputAlias(node1, node2)
+ ) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
@@ -234,7 +225,8 @@ private predicate localFlowStep(Node node1, Node node2, Configuration config) {
* Holds if the additional step from `node1` to `node2` does not jump between callables.
*/
private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
- isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and
+ 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
@@ -256,14 +248,12 @@ private predicate jumpStep(Node node1, Node node2, Configuration config) {
* Holds if the additional step from `node1` to `node2` jumps between callables.
*/
private predicate additionalJumpStep(Node node1, Node node2, Configuration config) {
- exists(DataFlowCallable callable1 |
- isAdditionalFlowStep(node1, node2, callable1, config) and
- node2.getEnclosingCallable() != callable1 and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, 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)
}
/**
@@ -454,8 +444,8 @@ private module Stage1 {
// read
exists(Node mid, Content c |
read(node, c, mid) and
- fwdFlowConsCand(c, unbind(config)) and
- revFlow(mid, toReturn, config)
+ fwdFlowConsCand(c, pragma[only_bind_into](config)) and
+ revFlow(mid, toReturn, pragma[only_bind_into](config))
)
or
// flow into a callable
@@ -481,18 +471,18 @@ private module Stage1 {
pragma[nomagic]
private predicate revFlowConsCand(Content c, Configuration config) {
exists(Node mid, Node node |
- fwdFlow(node, unbind(config)) and
+ fwdFlow(node, pragma[only_bind_into](config)) and
read(node, c, mid) and
- fwdFlowConsCand(c, unbind(config)) and
- revFlow(mid, _, config)
+ 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 |
- revFlow(mid, toReturn, config) and
- fwdFlowConsCand(c, unbind(config)) and
+ revFlow(mid, toReturn, pragma[only_bind_into](config)) and
+ fwdFlowConsCand(c, pragma[only_bind_into](config)) and
store(node, tc, mid, _) and
c = tc.getContent()
)
@@ -562,8 +552,8 @@ private module Stage1 {
Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
) {
exists(Content c |
- revFlowIsReadAndStored(c, config) and
- revFlow(node2, unbind(config)) and
+ revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
+ revFlow(node2, pragma[only_bind_into](config)) and
store(node1, tc, node2, contentType) and
c = tc.getContent() and
exists(ap1)
@@ -572,8 +562,8 @@ private module Stage1 {
pragma[nomagic]
predicate readStepCand(Node n1, Content c, Node n2, Configuration config) {
- revFlowIsReadAndStored(c, config) and
- revFlow(n2, unbind(config)) and
+ revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
+ revFlow(n2, pragma[only_bind_into](config)) and
read(n1, c, n2)
}
@@ -598,7 +588,7 @@ private module Stage1 {
) {
exists(ReturnNodeExt ret |
throughFlowNodeCand(ret, config) and
- callable = ret.getEnclosingCallable() and
+ callable = getNodeEnclosingCallable(ret) and
kind = ret.getKind()
)
}
@@ -611,7 +601,7 @@ private module Stage1 {
exists(ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
- p.getEnclosingCallable() = c and
+ getNodeEnclosingCallable(p) = c and
exists(ap) and
// we don't expect a parameter to return stored in itself
not exists(int pos |
@@ -636,9 +626,6 @@ private module Stage1 {
/* End: Stage 1 logic. */
}
-bindingset[result, b]
-private boolean unbindBool(boolean b) { result != b.booleanNot() }
-
pragma[noinline]
private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
Stage1::revFlow(node2, config) and
@@ -775,7 +762,7 @@ private module Stage2 {
bindingset[result, ap]
private ApApprox getApprox(Ap ap) { any() }
- private ApNil getApNil(Node node) { any() }
+ private ApNil getApNil(Node 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) }
@@ -874,16 +861,16 @@ private module Stage2 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -963,7 +950,7 @@ private module Stage2 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -982,7 +969,7 @@ private module Stage2 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1055,9 +1042,9 @@ private module Stage2 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -1069,9 +1056,9 @@ private module Stage2 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -1124,9 +1111,10 @@ private module Stage2 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -1192,9 +1180,10 @@ private module Stage2 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ unbindBool(ap2), _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -1213,13 +1202,13 @@ private module Stage2 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -1250,8 +1239,8 @@ private predicate flowOutOfCallNodeCand2(
DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
- Stage2::revFlow(node2, config) and
- Stage2::revFlow(node1, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node1, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -1260,8 +1249,8 @@ private predicate flowIntoCallNodeCand2(
Configuration config
) {
flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
- Stage2::revFlow(node2, config) and
- Stage2::revFlow(node1, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node1, pragma[only_bind_into](config))
}
private module LocalFlowBigStep {
@@ -1316,8 +1305,8 @@ private module LocalFlowBigStep {
pragma[noinline]
private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
- Stage2::revFlow(node1, _, _, false, config) and
- Stage2::revFlow(node2, _, _, false, unbind(config))
+ Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config))
}
/**
@@ -1334,7 +1323,7 @@ private module LocalFlowBigStep {
) {
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
(
- localFlowEntry(node1, config) and
+ localFlowEntry(node1, pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
@@ -1345,24 +1334,24 @@ private module LocalFlowBigStep {
t = getNodeType(node2)
) and
node1 != node2 and
- cc.relevantFor(node1.getEnclosingCallable()) and
+ cc.relevantFor(getNodeEnclosingCallable(node1)) and
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
- Stage2::revFlow(node2, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
or
exists(Node mid |
- localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
+ 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, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
)
or
exists(Node mid |
- localFlowStepPlus(node1, mid, _, _, config, cc) and
+ 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
- Stage2::revFlow(node2, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
)
)
}
@@ -1394,7 +1383,9 @@ private module Stage3 {
private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(Node node) { result = TFrontNil(getNodeType(node)) }
+ private ApNil getApNil(Node node) {
+ PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node))
+ }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
@@ -1467,6 +1458,13 @@ private module Stage3 {
PrevStage::revFlow(node, _, _, apa, config)
}
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ exists(ApApprox apa0 |
+ apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
+ )
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -1478,7 +1476,7 @@ private module Stage3 {
pragma[nomagic]
predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
- flowCand(node, unbindBool(getApprox(ap)), config) and
+ flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
@@ -1502,16 +1500,16 @@ private module Stage3 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -1556,7 +1554,7 @@ private module Stage3 {
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindBool(getApprox(ap1)), tc, node2, contentType, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1591,7 +1589,7 @@ private module Stage3 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1610,7 +1608,7 @@ private module Stage3 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1635,7 +1633,7 @@ private module Stage3 {
) {
exists(ParameterNode p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindBool(getApprox(ap)), config)
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
}
@@ -1683,9 +1681,9 @@ private module Stage3 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -1697,9 +1695,9 @@ private module Stage3 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -1752,9 +1750,10 @@ private module Stage3 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -1820,9 +1819,10 @@ private module Stage3 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ ap2, _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -1841,13 +1841,13 @@ private module Stage3 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -2087,7 +2087,9 @@ private module Stage4 {
private ApApprox getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(Node node) { result = TNil(getNodeType(node)) }
+ private ApNil getApNil(Node node) {
+ PrevStage::revFlow(node, _) and result = TNil(getNodeType(node))
+ }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
@@ -2131,8 +2133,11 @@ private module Stage4 {
bindingset[node, cc, config]
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
- localFlowEntry(node, config) and
- result = getLocalCallContext(cc, node.getEnclosingCallable())
+ exists(Cc cc0 |
+ cc = pragma[only_bind_into](cc0) and
+ localFlowEntry(node, config) and
+ result = getLocalCallContext(cc0, getNodeEnclosingCallable(node))
+ )
}
private predicate localStep(
@@ -2147,8 +2152,8 @@ private module Stage4 {
Configuration config
) {
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, _, _, _, config) and
- PrevStage::revFlow(node1, _, _, _, unbind(config))
+ PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -2157,8 +2162,8 @@ private module Stage4 {
Configuration config
) {
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, _, _, _, config) and
- PrevStage::revFlow(node1, _, _, _, unbind(config))
+ PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config))
}
bindingset[node, ap]
@@ -2173,6 +2178,13 @@ private module Stage4 {
PrevStage::revFlow(node, _, _, apa, config)
}
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ exists(ApApprox apa0 |
+ apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
+ )
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -2184,7 +2196,7 @@ private module Stage4 {
pragma[nomagic]
predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
- flowCand(node, getApprox(ap), config) and
+ flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
@@ -2208,16 +2220,16 @@ private module Stage4 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -2262,7 +2274,7 @@ private module Stage4 {
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, getApprox(ap1), tc, node2, contentType, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -2297,7 +2309,7 @@ private module Stage4 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -2316,7 +2328,7 @@ private module Stage4 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -2341,7 +2353,7 @@ private module Stage4 {
) {
exists(ParameterNode p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
}
@@ -2389,9 +2401,9 @@ private module Stage4 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -2403,9 +2415,9 @@ private module Stage4 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -2458,9 +2470,10 @@ private module Stage4 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -2526,9 +2539,10 @@ private module Stage4 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ ap2, _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -2547,13 +2561,13 @@ private module Stage4 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -2580,14 +2594,16 @@ private module Stage4 {
}
bindingset[conf, result]
-private Configuration unbind(Configuration conf) { result >= conf and result <= conf }
+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) {
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
- n.getEnclosingCallable() = c
+ getNodeEnclosingCallable(n) = c
)
}
@@ -2750,13 +2766,13 @@ private newtype TPathNode =
// ... or a step from an existing PathNode to another node.
exists(PathNodeMid mid |
pathStep(mid, node, cc, sc, ap) and
- config = mid.getConfiguration() and
- Stage4::revFlow(node, _, _, ap.getApprox(), unbind(config))
+ pragma[only_bind_into](config) = mid.getConfiguration() and
+ Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(Node node, Configuration config) {
- config.isSink(node) and
- Stage4::revFlow(node, unbind(config)) and
+ pragma[only_bind_into](config).isSink(node) and
+ Stage4::revFlow(node, pragma[only_bind_into](config)) and
(
// A sink that is also a source ...
config.isSource(node)
@@ -2764,7 +2780,7 @@ private newtype TPathNode =
// ... or a sink that can be reached from a source
exists(PathNodeMid mid |
pathStep(mid, node, _, _, TAccessPathNil(_)) and
- config = unbind(mid.getConfiguration())
+ pragma[only_bind_into](config) = mid.getConfiguration()
)
)
}
@@ -3061,7 +3077,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
private PathNodeMid getSuccMid() {
pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and
- result.getConfiguration() = unbind(this.getConfiguration())
+ result.getConfiguration() = unbindConf(this.getConfiguration())
}
override PathNodeImpl getASuccessorImpl() {
@@ -3073,7 +3089,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
mid = getSuccMid() and
mid.getNode() = sink.getNode() and
mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbind(mid.getConfiguration()) and
+ sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
result = sink
)
}
@@ -3116,7 +3132,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
conf = mid.getConfiguration() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
- localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and
ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
@@ -3304,7 +3320,7 @@ private predicate pathThroughCallable0(
) {
exists(CallContext innercc, SummaryCtx sc |
pathIntoCallable(mid, _, cc, innercc, sc, call) and
- paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration()))
+ paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration()))
)
}
@@ -3316,7 +3332,7 @@ pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node 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, unbind(mid.getConfiguration()))
+ out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
)
}
@@ -3402,14 +3418,14 @@ private module FlowExploration {
// flow out of a callable
viableReturnPosOut(_, getReturnPosition(node1), node2)
|
- c1 = node1.getEnclosingCallable() and
- c2 = node2.getEnclosingCallable() and
+ c1 = getNodeEnclosingCallable(node1) and
+ c2 = getNodeEnclosingCallable(node2) and
c1 != c2
)
}
private predicate interestingCallableSrc(DataFlowCallable c, Configuration config) {
- exists(Node n | config.isSource(n) and c = n.getEnclosingCallable())
+ exists(Node n | config.isSource(n) and c = getNodeEnclosingCallable(n))
or
exists(DataFlowCallable mid |
interestingCallableSrc(mid, config) and callableStep(mid, c, config)
@@ -3417,7 +3433,7 @@ private module FlowExploration {
}
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
- exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
+ exists(Node n | config.isSink(n) and c = getNodeEnclosingCallable(n))
or
exists(DataFlowCallable mid |
interestingCallableSink(mid, config) and callableStep(c, mid, config)
@@ -3439,20 +3455,20 @@ private module FlowExploration {
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
callableStep(c1, c2, config) and
- ce1 = TCallable(c1, config) and
- ce2 = TCallable(c2, unbind(config))
+ ce1 = TCallable(c1, pragma[only_bind_into](config)) and
+ ce2 = TCallable(c2, pragma[only_bind_into](config))
)
or
exists(Node n, Configuration config |
ce1 = TCallableSrc() and
config.isSource(n) and
- ce2 = TCallable(n.getEnclosingCallable(), config)
+ ce2 = TCallable(getNodeEnclosingCallable(n), config)
)
or
exists(Node n, Configuration config |
ce2 = TCallableSink() and
config.isSink(n) and
- ce1 = TCallable(n.getEnclosingCallable(), config)
+ ce1 = TCallable(getNodeEnclosingCallable(n), config)
)
}
@@ -3583,7 +3599,7 @@ private module FlowExploration {
exists(config.explorationLimit())
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
- distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
+ distSrc(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
@@ -3600,7 +3616,7 @@ private module FlowExploration {
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not clearsContent(node, ap.getHead()) and
not fullBarrier(node, config) and
- distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
+ distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
)
}
@@ -3659,7 +3675,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSourceDistance() {
- result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
+ result = distSrc(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
}
/**
@@ -3667,7 +3683,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSinkDistance() {
- result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
+ result = distSink(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
}
private string ppAp() {
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 1d2e9052842..a51c20c2288 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
@@ -26,15 +26,243 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) {
tupleLimit = 1000
}
+/**
+ * Provides a simple data-flow analysis for resolving lambda calls. The analysis
+ * 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
+ * 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) {
+ p.isParameterOf(viableCallable(call), i)
+ }
+
+ private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) {
+ p.isParameterOf(viableCallableLambda(call, _), i)
+ }
+
+ private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ exists(int i |
+ viableParamNonLambda(call, i, p) and
+ arg.argumentOf(call, i)
+ )
+ }
+
+ private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ exists(int i |
+ viableParamLambda(call, i, p) and
+ arg.argumentOf(call, i)
+ )
+ }
+
+ private newtype TReturnPositionSimple =
+ TReturnPositionSimple0(DataFlowCallable c, ReturnKind kind) {
+ exists(ReturnNode ret |
+ c = getNodeEnclosingCallable(ret) and
+ kind = ret.getKind()
+ )
+ }
+
+ pragma[noinline]
+ private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret, ReturnKind kind) {
+ result = TReturnPositionSimple0(getNodeEnclosingCallable(ret), kind)
+ }
+
+ pragma[nomagic]
+ private TReturnPositionSimple viableReturnPosNonLambda(DataFlowCall call, ReturnKind kind) {
+ result = TReturnPositionSimple0(viableCallable(call), kind)
+ }
+
+ pragma[nomagic]
+ private TReturnPositionSimple viableReturnPosLambda(
+ DataFlowCall call, DataFlowCallOption lastCall, ReturnKind kind
+ ) {
+ result = TReturnPositionSimple0(viableCallableLambda(call, lastCall), kind)
+ }
+
+ private predicate viableReturnPosOutNonLambda(
+ DataFlowCall call, TReturnPositionSimple pos, OutNode out
+ ) {
+ exists(ReturnKind kind |
+ pos = viableReturnPosNonLambda(call, kind) and
+ out = getAnOutNode(call, kind)
+ )
+ }
+
+ private predicate viableReturnPosOutLambda(
+ DataFlowCall call, DataFlowCallOption lastCall, TReturnPositionSimple pos, OutNode out
+ ) {
+ exists(ReturnKind kind |
+ pos = viableReturnPosLambda(call, lastCall, kind) and
+ out = getAnOutNode(call, kind)
+ )
+ }
+
+ /**
+ * Holds if data can flow (inter-procedurally) from `node` (of type `t`) to
+ * the lambda call `lambdaCall`.
+ *
+ * The parameter `toReturn` indicates whether the path from `node` to
+ * `lambdaCall` goes through a return, and `toJump` whether the path goes
+ * through a jump step.
+ *
+ * The call context `lastCall` records the last call on the path from `node`
+ * to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing
+ * callable of `lambdaCall`.
+ */
+ pragma[nomagic]
+ predicate revLambdaFlow(
+ DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
+ 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))
+ else any()
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlow0(
+ DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
+ boolean toJump, DataFlowCallOption lastCall
+ ) {
+ lambdaCall(lambdaCall, kind, node) and
+ t = getNodeType(node) and
+ toReturn = false and
+ toJump = false and
+ lastCall = TDataFlowCallNone()
+ or
+ // local flow
+ exists(Node mid, DataFlowType t0 |
+ revLambdaFlow(lambdaCall, kind, mid, t0, toReturn, toJump, lastCall)
+ |
+ simpleLocalFlowStep(node, mid) and
+ t = t0
+ or
+ exists(boolean preservesValue |
+ additionalLambdaFlowStep(node, mid, preservesValue) and
+ getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid)
+ |
+ preservesValue = false and
+ t = getNodeType(node)
+ or
+ preservesValue = true and
+ t = t0
+ )
+ )
+ or
+ // jump step
+ exists(Node mid, DataFlowType t0 |
+ revLambdaFlow(lambdaCall, kind, mid, t0, _, _, _) and
+ toReturn = false and
+ toJump = true and
+ lastCall = TDataFlowCallNone()
+ |
+ jumpStep(node, mid) and
+ t = t0
+ or
+ exists(boolean preservesValue |
+ additionalLambdaFlowStep(node, mid, preservesValue) and
+ getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid)
+ |
+ preservesValue = false and
+ t = getNodeType(node)
+ or
+ preservesValue = true and
+ t = t0
+ )
+ )
+ or
+ // flow into a callable
+ exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call |
+ revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and
+ (
+ if lastCall0 = TDataFlowCallNone() and toJump = false
+ then lastCall = TDataFlowCallSome(call)
+ else lastCall = lastCall0
+ ) and
+ toReturn = false
+ |
+ viableParamArgNonLambda(call, p, node)
+ or
+ viableParamArgLambda(call, p, node) // non-linear recursion
+ )
+ or
+ // flow out of a callable
+ exists(TReturnPositionSimple pos |
+ revLambdaFlowOut(lambdaCall, kind, pos, t, toJump, lastCall) and
+ getReturnPositionSimple(node, node.(ReturnNode).getKind()) = pos and
+ toReturn = true
+ )
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlowOutLambdaCall(
+ DataFlowCall lambdaCall, LambdaCallKind kind, OutNode out, DataFlowType t, boolean toJump,
+ DataFlowCall call, DataFlowCallOption lastCall
+ ) {
+ revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and
+ exists(ReturnKindExt rk |
+ out = rk.getAnOutNode(call) and
+ lambdaCall(call, _, _)
+ )
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlowOut(
+ DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t,
+ boolean toJump, DataFlowCallOption lastCall
+ ) {
+ exists(DataFlowCall call, OutNode out |
+ revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and
+ viableReturnPosOutNonLambda(call, pos, out)
+ or
+ // non-linear recursion
+ revLambdaFlowOutLambdaCall(lambdaCall, kind, out, t, toJump, call, lastCall) and
+ viableReturnPosOutLambda(call, _, pos, out)
+ )
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlowIn(
+ DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump,
+ DataFlowCallOption lastCall
+ ) {
+ revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall)
+ }
+}
+
+private DataFlowCallable viableCallableExt(DataFlowCall call) {
+ result = viableCallable(call)
+ or
+ result = viableCallableLambda(call, _)
+}
+
cached
private module Cached {
+ /**
+ * Gets a viable target for the lambda call `call`.
+ *
+ * `lastCall` records the call required to reach `call` in order for the result
+ * to be a viable target, if any.
+ */
+ cached
+ DataFlowCallable viableCallableLambda(DataFlowCall call, DataFlowCallOption lastCall) {
+ exists(Node creation, LambdaCallKind kind |
+ LambdaFlow::revLambdaFlow(call, kind, creation, _, _, _, lastCall) and
+ lambdaCreation(creation, kind, result)
+ )
+ }
+
/**
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
* The instance parameter is considered to have index `-1`.
*/
pragma[nomagic]
private predicate viableParam(DataFlowCall call, int i, ParameterNode p) {
- p.isParameterOf(viableCallable(call), i)
+ p.isParameterOf(viableCallableExt(call), i)
}
/**
@@ -52,7 +280,7 @@ private module Cached {
pragma[nomagic]
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
- viableCallable(call) = result.getCallable() and
+ viableCallableExt(call) = result.getCallable() and
kind = result.getKind()
}
@@ -317,6 +545,35 @@ private module Cached {
cached
private module DispatchWithCallContext {
+ /**
+ * Holds if the set of viable implementations that can be called by `call`
+ * might be improved by knowing the call context.
+ */
+ pragma[nomagic]
+ private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) {
+ mayBenefitFromCallContext(call, callable)
+ or
+ callable = call.getEnclosingCallable() and
+ exists(viableCallableLambda(call, TDataFlowCallSome(_)))
+ }
+
+ /**
+ * Gets a viable dispatch target of `call` in the context `ctx`. This is
+ * restricted to those `call`s for which a context might make a difference.
+ */
+ pragma[nomagic]
+ private DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
+ result = viableImplInCallContext(call, ctx)
+ or
+ result = viableCallableLambda(call, TDataFlowCallSome(ctx))
+ or
+ exists(DataFlowCallable enclosing |
+ mayBenefitFromCallContextExt(call, enclosing) and
+ enclosing = viableCallableExt(ctx) and
+ result = viableCallableLambda(call, TDataFlowCallNone())
+ )
+ }
+
/**
* Holds if the call context `ctx` reduces the set of viable run-time
* dispatch targets of call `call` in `c`.
@@ -324,10 +581,10 @@ private module Cached {
cached
predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) {
exists(int tgts, int ctxtgts |
- mayBenefitFromCallContext(call, c) and
- c = viableCallable(ctx) and
- ctxtgts = count(viableImplInCallContext(call, ctx)) and
- tgts = strictcount(viableCallable(call)) and
+ mayBenefitFromCallContextExt(call, c) and
+ c = viableCallableExt(ctx) and
+ ctxtgts = count(viableImplInCallContextExt(call, ctx)) and
+ tgts = strictcount(viableCallableExt(call)) and
ctxtgts < tgts
)
}
@@ -339,7 +596,7 @@ private module Cached {
*/
cached
DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
- result = viableImplInCallContext(call, ctx) and
+ result = viableImplInCallContextExt(call, ctx) and
reducedViableImplInCallContext(call, _, ctx)
}
@@ -351,10 +608,10 @@ private module Cached {
cached
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) {
exists(int tgts, int ctxtgts |
- mayBenefitFromCallContext(call, _) and
- c = viableCallable(call) and
- ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and
- tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and
+ 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
ctxtgts < tgts
)
}
@@ -367,7 +624,7 @@ private module Cached {
*/
cached
DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) {
- result = viableImplInCallContext(call, ctx) and
+ result = viableImplInCallContextExt(call, ctx) and
reducedViableImplInReturn(result, call)
}
}
@@ -415,6 +672,30 @@ private module Cached {
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
}
+ /**
+ * Holds if data can flow from `fromNode` to `toNode` because they are the post-update
+ * nodes of some function output and input respectively, where the output and input
+ * are aliases. A typical example is a function returning `this`, implementing a fluent
+ * interface.
+ */
+ cached
+ predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) {
+ exists(Node fromPre, Node toPre |
+ fromPre = fromNode.getPreUpdateNode() and
+ toPre = toNode.getPreUpdateNode()
+ |
+ exists(DataFlowCall c |
+ // 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)
+ )
+ or
+ argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre)
+ )
+ }
+
/**
* Holds if the call context `call` either improves virtual dispatch in
* `callable` or if it allows us to prune unreachable nodes in `callable`.
@@ -423,7 +704,7 @@ private module Cached {
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
reducedViableImplInCallContext(_, callable, call)
or
- exists(Node n | n.getEnclosingCallable() = callable | isUnreachableInCall(n, call))
+ exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCall(n, call))
}
cached
@@ -457,6 +738,11 @@ private module Cached {
TBooleanNone() or
TBooleanSome(boolean b) { b = true or b = false }
+ cached
+ newtype TDataFlowCallOption =
+ TDataFlowCallNone() or
+ TDataFlowCallSome(DataFlowCall call)
+
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
@@ -568,7 +854,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall {
override string toString() { result = "CcSomeCall" }
override predicate relevantFor(DataFlowCallable callable) {
- exists(ParameterNode p | p.getEnclosingCallable() = callable)
+ exists(ParameterNode p | getNodeEnclosingCallable(p) = callable)
}
override predicate matchesCall(DataFlowCall call) { any() }
@@ -613,7 +899,7 @@ class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall
}
private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) {
- exists(Node n | n.getEnclosingCallable() = callable and isUnreachableInCall(n, call))
+ exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCall(n, call))
}
/**
@@ -722,9 +1008,22 @@ class ReturnPosition extends TReturnPosition0 {
string toString() { result = "[" + kind + "] " + c }
}
+/**
+ * Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this
+ * predicate ensures that joins go from `n` to the result instead of the other
+ * way around.
+ */
+pragma[inline]
+DataFlowCallable getNodeEnclosingCallable(Node n) {
+ exists(Node n0 |
+ pragma[only_bind_into](n0) = n and
+ pragma[only_bind_into](result) = n0.getEnclosingCallable()
+ )
+}
+
pragma[noinline]
private DataFlowCallable returnNodeGetEnclosingCallable(ReturnNodeExt ret) {
- result = ret.getEnclosingCallable()
+ result = getNodeEnclosingCallable(ret)
}
pragma[noinline]
@@ -740,7 +1039,7 @@ ReturnPosition getReturnPosition(ReturnNodeExt ret) {
bindingset[cc, callable]
predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
- cc instanceof CallContextAny and callable = viableCallable(call)
+ cc instanceof CallContextAny and callable = viableCallableExt(call)
or
exists(DataFlowCallable c0, DataFlowCall call0 |
call0.getEnclosingCallable() = callable and
@@ -754,14 +1053,14 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
exists(DataFlowCall ctx | cc = TSpecificCall(ctx) |
if reducedViableImplInCallContext(call, _, ctx)
then result = prunedViableImplInCallContext(call, ctx)
- else result = viableCallable(call)
+ else result = viableCallableExt(call)
)
or
- result = viableCallable(call) and cc instanceof CallContextSomeCall
+ result = viableCallableExt(call) and cc instanceof CallContextSomeCall
or
- result = viableCallable(call) and cc instanceof CallContextAny
+ result = viableCallableExt(call) and cc instanceof CallContextAny
or
- result = viableCallable(call) and cc instanceof CallContextReturn
+ result = viableCallableExt(call) and cc instanceof CallContextReturn
}
predicate read = readStep/3;
@@ -775,6 +1074,19 @@ class BooleanOption extends TBooleanOption {
}
}
+/** An optional `DataFlowCall`. */
+class DataFlowCallOption extends TDataFlowCallOption {
+ string toString() {
+ this = TDataFlowCallNone() and
+ result = "(none)"
+ or
+ exists(DataFlowCall call |
+ this = TDataFlowCallSome(call) and
+ result = call.toString()
+ )
+ }
+}
+
/** Content tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
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 59cc8d529a7..8b446d28b86 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
@@ -207,23 +207,14 @@ private predicate fullBarrier(Node node, Configuration config) {
)
}
-private class AdditionalFlowStepSource extends Node {
- AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) }
-}
-
-pragma[noinline]
-private predicate isAdditionalFlowStep(
- AdditionalFlowStepSource node1, Node node2, DataFlowCallable callable1, Configuration config
-) {
- config.isAdditionalFlowStep(node1, node2) and
- callable1 = node1.getEnclosingCallable()
-}
-
/**
* 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) and
+ (
+ simpleLocalFlowStep(node1, node2) or
+ reverseStepThroughInputOutputAlias(node1, node2)
+ ) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
@@ -234,7 +225,8 @@ private predicate localFlowStep(Node node1, Node node2, Configuration config) {
* Holds if the additional step from `node1` to `node2` does not jump between callables.
*/
private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
- isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and
+ 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
@@ -256,14 +248,12 @@ private predicate jumpStep(Node node1, Node node2, Configuration config) {
* Holds if the additional step from `node1` to `node2` jumps between callables.
*/
private predicate additionalJumpStep(Node node1, Node node2, Configuration config) {
- exists(DataFlowCallable callable1 |
- isAdditionalFlowStep(node1, node2, callable1, config) and
- node2.getEnclosingCallable() != callable1 and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, 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)
}
/**
@@ -454,8 +444,8 @@ private module Stage1 {
// read
exists(Node mid, Content c |
read(node, c, mid) and
- fwdFlowConsCand(c, unbind(config)) and
- revFlow(mid, toReturn, config)
+ fwdFlowConsCand(c, pragma[only_bind_into](config)) and
+ revFlow(mid, toReturn, pragma[only_bind_into](config))
)
or
// flow into a callable
@@ -481,18 +471,18 @@ private module Stage1 {
pragma[nomagic]
private predicate revFlowConsCand(Content c, Configuration config) {
exists(Node mid, Node node |
- fwdFlow(node, unbind(config)) and
+ fwdFlow(node, pragma[only_bind_into](config)) and
read(node, c, mid) and
- fwdFlowConsCand(c, unbind(config)) and
- revFlow(mid, _, config)
+ 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 |
- revFlow(mid, toReturn, config) and
- fwdFlowConsCand(c, unbind(config)) and
+ revFlow(mid, toReturn, pragma[only_bind_into](config)) and
+ fwdFlowConsCand(c, pragma[only_bind_into](config)) and
store(node, tc, mid, _) and
c = tc.getContent()
)
@@ -562,8 +552,8 @@ private module Stage1 {
Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
) {
exists(Content c |
- revFlowIsReadAndStored(c, config) and
- revFlow(node2, unbind(config)) and
+ revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
+ revFlow(node2, pragma[only_bind_into](config)) and
store(node1, tc, node2, contentType) and
c = tc.getContent() and
exists(ap1)
@@ -572,8 +562,8 @@ private module Stage1 {
pragma[nomagic]
predicate readStepCand(Node n1, Content c, Node n2, Configuration config) {
- revFlowIsReadAndStored(c, config) and
- revFlow(n2, unbind(config)) and
+ revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
+ revFlow(n2, pragma[only_bind_into](config)) and
read(n1, c, n2)
}
@@ -598,7 +588,7 @@ private module Stage1 {
) {
exists(ReturnNodeExt ret |
throughFlowNodeCand(ret, config) and
- callable = ret.getEnclosingCallable() and
+ callable = getNodeEnclosingCallable(ret) and
kind = ret.getKind()
)
}
@@ -611,7 +601,7 @@ private module Stage1 {
exists(ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
- p.getEnclosingCallable() = c and
+ getNodeEnclosingCallable(p) = c and
exists(ap) and
// we don't expect a parameter to return stored in itself
not exists(int pos |
@@ -636,9 +626,6 @@ private module Stage1 {
/* End: Stage 1 logic. */
}
-bindingset[result, b]
-private boolean unbindBool(boolean b) { result != b.booleanNot() }
-
pragma[noinline]
private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
Stage1::revFlow(node2, config) and
@@ -775,7 +762,7 @@ private module Stage2 {
bindingset[result, ap]
private ApApprox getApprox(Ap ap) { any() }
- private ApNil getApNil(Node node) { any() }
+ private ApNil getApNil(Node 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) }
@@ -874,16 +861,16 @@ private module Stage2 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -963,7 +950,7 @@ private module Stage2 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -982,7 +969,7 @@ private module Stage2 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1055,9 +1042,9 @@ private module Stage2 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -1069,9 +1056,9 @@ private module Stage2 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -1124,9 +1111,10 @@ private module Stage2 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -1192,9 +1180,10 @@ private module Stage2 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ unbindBool(ap2), _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -1213,13 +1202,13 @@ private module Stage2 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -1250,8 +1239,8 @@ private predicate flowOutOfCallNodeCand2(
DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
- Stage2::revFlow(node2, config) and
- Stage2::revFlow(node1, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node1, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -1260,8 +1249,8 @@ private predicate flowIntoCallNodeCand2(
Configuration config
) {
flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
- Stage2::revFlow(node2, config) and
- Stage2::revFlow(node1, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node1, pragma[only_bind_into](config))
}
private module LocalFlowBigStep {
@@ -1316,8 +1305,8 @@ private module LocalFlowBigStep {
pragma[noinline]
private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
- Stage2::revFlow(node1, _, _, false, config) and
- Stage2::revFlow(node2, _, _, false, unbind(config))
+ Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config))
}
/**
@@ -1334,7 +1323,7 @@ private module LocalFlowBigStep {
) {
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
(
- localFlowEntry(node1, config) and
+ localFlowEntry(node1, pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
@@ -1345,24 +1334,24 @@ private module LocalFlowBigStep {
t = getNodeType(node2)
) and
node1 != node2 and
- cc.relevantFor(node1.getEnclosingCallable()) and
+ cc.relevantFor(getNodeEnclosingCallable(node1)) and
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
- Stage2::revFlow(node2, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
or
exists(Node mid |
- localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
+ 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, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
)
or
exists(Node mid |
- localFlowStepPlus(node1, mid, _, _, config, cc) and
+ 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
- Stage2::revFlow(node2, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
)
)
}
@@ -1394,7 +1383,9 @@ private module Stage3 {
private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(Node node) { result = TFrontNil(getNodeType(node)) }
+ private ApNil getApNil(Node node) {
+ PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node))
+ }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
@@ -1467,6 +1458,13 @@ private module Stage3 {
PrevStage::revFlow(node, _, _, apa, config)
}
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ exists(ApApprox apa0 |
+ apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
+ )
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -1478,7 +1476,7 @@ private module Stage3 {
pragma[nomagic]
predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
- flowCand(node, unbindBool(getApprox(ap)), config) and
+ flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
@@ -1502,16 +1500,16 @@ private module Stage3 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -1556,7 +1554,7 @@ private module Stage3 {
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindBool(getApprox(ap1)), tc, node2, contentType, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1591,7 +1589,7 @@ private module Stage3 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1610,7 +1608,7 @@ private module Stage3 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1635,7 +1633,7 @@ private module Stage3 {
) {
exists(ParameterNode p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindBool(getApprox(ap)), config)
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
}
@@ -1683,9 +1681,9 @@ private module Stage3 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -1697,9 +1695,9 @@ private module Stage3 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -1752,9 +1750,10 @@ private module Stage3 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -1820,9 +1819,10 @@ private module Stage3 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ ap2, _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -1841,13 +1841,13 @@ private module Stage3 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -2087,7 +2087,9 @@ private module Stage4 {
private ApApprox getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(Node node) { result = TNil(getNodeType(node)) }
+ private ApNil getApNil(Node node) {
+ PrevStage::revFlow(node, _) and result = TNil(getNodeType(node))
+ }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
@@ -2131,8 +2133,11 @@ private module Stage4 {
bindingset[node, cc, config]
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
- localFlowEntry(node, config) and
- result = getLocalCallContext(cc, node.getEnclosingCallable())
+ exists(Cc cc0 |
+ cc = pragma[only_bind_into](cc0) and
+ localFlowEntry(node, config) and
+ result = getLocalCallContext(cc0, getNodeEnclosingCallable(node))
+ )
}
private predicate localStep(
@@ -2147,8 +2152,8 @@ private module Stage4 {
Configuration config
) {
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, _, _, _, config) and
- PrevStage::revFlow(node1, _, _, _, unbind(config))
+ PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -2157,8 +2162,8 @@ private module Stage4 {
Configuration config
) {
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, _, _, _, config) and
- PrevStage::revFlow(node1, _, _, _, unbind(config))
+ PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config))
}
bindingset[node, ap]
@@ -2173,6 +2178,13 @@ private module Stage4 {
PrevStage::revFlow(node, _, _, apa, config)
}
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ exists(ApApprox apa0 |
+ apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
+ )
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -2184,7 +2196,7 @@ private module Stage4 {
pragma[nomagic]
predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
- flowCand(node, getApprox(ap), config) and
+ flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
@@ -2208,16 +2220,16 @@ private module Stage4 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -2262,7 +2274,7 @@ private module Stage4 {
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, getApprox(ap1), tc, node2, contentType, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -2297,7 +2309,7 @@ private module Stage4 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -2316,7 +2328,7 @@ private module Stage4 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -2341,7 +2353,7 @@ private module Stage4 {
) {
exists(ParameterNode p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
}
@@ -2389,9 +2401,9 @@ private module Stage4 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -2403,9 +2415,9 @@ private module Stage4 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -2458,9 +2470,10 @@ private module Stage4 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -2526,9 +2539,10 @@ private module Stage4 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ ap2, _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -2547,13 +2561,13 @@ private module Stage4 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -2580,14 +2594,16 @@ private module Stage4 {
}
bindingset[conf, result]
-private Configuration unbind(Configuration conf) { result >= conf and result <= conf }
+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) {
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
- n.getEnclosingCallable() = c
+ getNodeEnclosingCallable(n) = c
)
}
@@ -2750,13 +2766,13 @@ private newtype TPathNode =
// ... or a step from an existing PathNode to another node.
exists(PathNodeMid mid |
pathStep(mid, node, cc, sc, ap) and
- config = mid.getConfiguration() and
- Stage4::revFlow(node, _, _, ap.getApprox(), unbind(config))
+ pragma[only_bind_into](config) = mid.getConfiguration() and
+ Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(Node node, Configuration config) {
- config.isSink(node) and
- Stage4::revFlow(node, unbind(config)) and
+ pragma[only_bind_into](config).isSink(node) and
+ Stage4::revFlow(node, pragma[only_bind_into](config)) and
(
// A sink that is also a source ...
config.isSource(node)
@@ -2764,7 +2780,7 @@ private newtype TPathNode =
// ... or a sink that can be reached from a source
exists(PathNodeMid mid |
pathStep(mid, node, _, _, TAccessPathNil(_)) and
- config = unbind(mid.getConfiguration())
+ pragma[only_bind_into](config) = mid.getConfiguration()
)
)
}
@@ -3061,7 +3077,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
private PathNodeMid getSuccMid() {
pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and
- result.getConfiguration() = unbind(this.getConfiguration())
+ result.getConfiguration() = unbindConf(this.getConfiguration())
}
override PathNodeImpl getASuccessorImpl() {
@@ -3073,7 +3089,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
mid = getSuccMid() and
mid.getNode() = sink.getNode() and
mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbind(mid.getConfiguration()) and
+ sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
result = sink
)
}
@@ -3116,7 +3132,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
conf = mid.getConfiguration() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
- localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and
ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
@@ -3304,7 +3320,7 @@ private predicate pathThroughCallable0(
) {
exists(CallContext innercc, SummaryCtx sc |
pathIntoCallable(mid, _, cc, innercc, sc, call) and
- paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration()))
+ paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration()))
)
}
@@ -3316,7 +3332,7 @@ pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node 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, unbind(mid.getConfiguration()))
+ out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
)
}
@@ -3402,14 +3418,14 @@ private module FlowExploration {
// flow out of a callable
viableReturnPosOut(_, getReturnPosition(node1), node2)
|
- c1 = node1.getEnclosingCallable() and
- c2 = node2.getEnclosingCallable() and
+ c1 = getNodeEnclosingCallable(node1) and
+ c2 = getNodeEnclosingCallable(node2) and
c1 != c2
)
}
private predicate interestingCallableSrc(DataFlowCallable c, Configuration config) {
- exists(Node n | config.isSource(n) and c = n.getEnclosingCallable())
+ exists(Node n | config.isSource(n) and c = getNodeEnclosingCallable(n))
or
exists(DataFlowCallable mid |
interestingCallableSrc(mid, config) and callableStep(mid, c, config)
@@ -3417,7 +3433,7 @@ private module FlowExploration {
}
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
- exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
+ exists(Node n | config.isSink(n) and c = getNodeEnclosingCallable(n))
or
exists(DataFlowCallable mid |
interestingCallableSink(mid, config) and callableStep(c, mid, config)
@@ -3439,20 +3455,20 @@ private module FlowExploration {
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
callableStep(c1, c2, config) and
- ce1 = TCallable(c1, config) and
- ce2 = TCallable(c2, unbind(config))
+ ce1 = TCallable(c1, pragma[only_bind_into](config)) and
+ ce2 = TCallable(c2, pragma[only_bind_into](config))
)
or
exists(Node n, Configuration config |
ce1 = TCallableSrc() and
config.isSource(n) and
- ce2 = TCallable(n.getEnclosingCallable(), config)
+ ce2 = TCallable(getNodeEnclosingCallable(n), config)
)
or
exists(Node n, Configuration config |
ce2 = TCallableSink() and
config.isSink(n) and
- ce1 = TCallable(n.getEnclosingCallable(), config)
+ ce1 = TCallable(getNodeEnclosingCallable(n), config)
)
}
@@ -3583,7 +3599,7 @@ private module FlowExploration {
exists(config.explorationLimit())
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
- distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
+ distSrc(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
@@ -3600,7 +3616,7 @@ private module FlowExploration {
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not clearsContent(node, ap.getHead()) and
not fullBarrier(node, config) and
- distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
+ distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
)
}
@@ -3659,7 +3675,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSourceDistance() {
- result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
+ result = distSrc(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
}
/**
@@ -3667,7 +3683,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSinkDistance() {
- result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
+ result = distSink(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
}
private string ppAp() {
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 da20528760f..835e6c46451 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
@@ -312,3 +312,14 @@ predicate isImmutableOrUnobservable(Node n) {
/** Holds if `n` should be hidden from path explanations. */
predicate nodeIsHidden(Node n) { none() }
+
+class LambdaCallKind = Unit;
+
+/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */
+predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { none() }
+
+/** 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. */
+predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
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 59cc8d529a7..8b446d28b86 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
@@ -207,23 +207,14 @@ private predicate fullBarrier(Node node, Configuration config) {
)
}
-private class AdditionalFlowStepSource extends Node {
- AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) }
-}
-
-pragma[noinline]
-private predicate isAdditionalFlowStep(
- AdditionalFlowStepSource node1, Node node2, DataFlowCallable callable1, Configuration config
-) {
- config.isAdditionalFlowStep(node1, node2) and
- callable1 = node1.getEnclosingCallable()
-}
-
/**
* 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) and
+ (
+ simpleLocalFlowStep(node1, node2) or
+ reverseStepThroughInputOutputAlias(node1, node2)
+ ) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
@@ -234,7 +225,8 @@ private predicate localFlowStep(Node node1, Node node2, Configuration config) {
* Holds if the additional step from `node1` to `node2` does not jump between callables.
*/
private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
- isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and
+ 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
@@ -256,14 +248,12 @@ private predicate jumpStep(Node node1, Node node2, Configuration config) {
* Holds if the additional step from `node1` to `node2` jumps between callables.
*/
private predicate additionalJumpStep(Node node1, Node node2, Configuration config) {
- exists(DataFlowCallable callable1 |
- isAdditionalFlowStep(node1, node2, callable1, config) and
- node2.getEnclosingCallable() != callable1 and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, 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)
}
/**
@@ -454,8 +444,8 @@ private module Stage1 {
// read
exists(Node mid, Content c |
read(node, c, mid) and
- fwdFlowConsCand(c, unbind(config)) and
- revFlow(mid, toReturn, config)
+ fwdFlowConsCand(c, pragma[only_bind_into](config)) and
+ revFlow(mid, toReturn, pragma[only_bind_into](config))
)
or
// flow into a callable
@@ -481,18 +471,18 @@ private module Stage1 {
pragma[nomagic]
private predicate revFlowConsCand(Content c, Configuration config) {
exists(Node mid, Node node |
- fwdFlow(node, unbind(config)) and
+ fwdFlow(node, pragma[only_bind_into](config)) and
read(node, c, mid) and
- fwdFlowConsCand(c, unbind(config)) and
- revFlow(mid, _, config)
+ 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 |
- revFlow(mid, toReturn, config) and
- fwdFlowConsCand(c, unbind(config)) and
+ revFlow(mid, toReturn, pragma[only_bind_into](config)) and
+ fwdFlowConsCand(c, pragma[only_bind_into](config)) and
store(node, tc, mid, _) and
c = tc.getContent()
)
@@ -562,8 +552,8 @@ private module Stage1 {
Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
) {
exists(Content c |
- revFlowIsReadAndStored(c, config) and
- revFlow(node2, unbind(config)) and
+ revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
+ revFlow(node2, pragma[only_bind_into](config)) and
store(node1, tc, node2, contentType) and
c = tc.getContent() and
exists(ap1)
@@ -572,8 +562,8 @@ private module Stage1 {
pragma[nomagic]
predicate readStepCand(Node n1, Content c, Node n2, Configuration config) {
- revFlowIsReadAndStored(c, config) and
- revFlow(n2, unbind(config)) and
+ revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
+ revFlow(n2, pragma[only_bind_into](config)) and
read(n1, c, n2)
}
@@ -598,7 +588,7 @@ private module Stage1 {
) {
exists(ReturnNodeExt ret |
throughFlowNodeCand(ret, config) and
- callable = ret.getEnclosingCallable() and
+ callable = getNodeEnclosingCallable(ret) and
kind = ret.getKind()
)
}
@@ -611,7 +601,7 @@ private module Stage1 {
exists(ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
- p.getEnclosingCallable() = c and
+ getNodeEnclosingCallable(p) = c and
exists(ap) and
// we don't expect a parameter to return stored in itself
not exists(int pos |
@@ -636,9 +626,6 @@ private module Stage1 {
/* End: Stage 1 logic. */
}
-bindingset[result, b]
-private boolean unbindBool(boolean b) { result != b.booleanNot() }
-
pragma[noinline]
private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
Stage1::revFlow(node2, config) and
@@ -775,7 +762,7 @@ private module Stage2 {
bindingset[result, ap]
private ApApprox getApprox(Ap ap) { any() }
- private ApNil getApNil(Node node) { any() }
+ private ApNil getApNil(Node 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) }
@@ -874,16 +861,16 @@ private module Stage2 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -963,7 +950,7 @@ private module Stage2 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -982,7 +969,7 @@ private module Stage2 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1055,9 +1042,9 @@ private module Stage2 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -1069,9 +1056,9 @@ private module Stage2 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -1124,9 +1111,10 @@ private module Stage2 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -1192,9 +1180,10 @@ private module Stage2 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ unbindBool(ap2), _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -1213,13 +1202,13 @@ private module Stage2 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -1250,8 +1239,8 @@ private predicate flowOutOfCallNodeCand2(
DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
- Stage2::revFlow(node2, config) and
- Stage2::revFlow(node1, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node1, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -1260,8 +1249,8 @@ private predicate flowIntoCallNodeCand2(
Configuration config
) {
flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
- Stage2::revFlow(node2, config) and
- Stage2::revFlow(node1, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node1, pragma[only_bind_into](config))
}
private module LocalFlowBigStep {
@@ -1316,8 +1305,8 @@ private module LocalFlowBigStep {
pragma[noinline]
private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
- Stage2::revFlow(node1, _, _, false, config) and
- Stage2::revFlow(node2, _, _, false, unbind(config))
+ Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config))
}
/**
@@ -1334,7 +1323,7 @@ private module LocalFlowBigStep {
) {
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
(
- localFlowEntry(node1, config) and
+ localFlowEntry(node1, pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
@@ -1345,24 +1334,24 @@ private module LocalFlowBigStep {
t = getNodeType(node2)
) and
node1 != node2 and
- cc.relevantFor(node1.getEnclosingCallable()) and
+ cc.relevantFor(getNodeEnclosingCallable(node1)) and
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
- Stage2::revFlow(node2, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
or
exists(Node mid |
- localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
+ 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, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
)
or
exists(Node mid |
- localFlowStepPlus(node1, mid, _, _, config, cc) and
+ 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
- Stage2::revFlow(node2, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
)
)
}
@@ -1394,7 +1383,9 @@ private module Stage3 {
private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(Node node) { result = TFrontNil(getNodeType(node)) }
+ private ApNil getApNil(Node node) {
+ PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node))
+ }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
@@ -1467,6 +1458,13 @@ private module Stage3 {
PrevStage::revFlow(node, _, _, apa, config)
}
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ exists(ApApprox apa0 |
+ apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
+ )
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -1478,7 +1476,7 @@ private module Stage3 {
pragma[nomagic]
predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
- flowCand(node, unbindBool(getApprox(ap)), config) and
+ flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
@@ -1502,16 +1500,16 @@ private module Stage3 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -1556,7 +1554,7 @@ private module Stage3 {
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindBool(getApprox(ap1)), tc, node2, contentType, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1591,7 +1589,7 @@ private module Stage3 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1610,7 +1608,7 @@ private module Stage3 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1635,7 +1633,7 @@ private module Stage3 {
) {
exists(ParameterNode p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindBool(getApprox(ap)), config)
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
}
@@ -1683,9 +1681,9 @@ private module Stage3 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -1697,9 +1695,9 @@ private module Stage3 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -1752,9 +1750,10 @@ private module Stage3 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -1820,9 +1819,10 @@ private module Stage3 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ ap2, _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -1841,13 +1841,13 @@ private module Stage3 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -2087,7 +2087,9 @@ private module Stage4 {
private ApApprox getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(Node node) { result = TNil(getNodeType(node)) }
+ private ApNil getApNil(Node node) {
+ PrevStage::revFlow(node, _) and result = TNil(getNodeType(node))
+ }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
@@ -2131,8 +2133,11 @@ private module Stage4 {
bindingset[node, cc, config]
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
- localFlowEntry(node, config) and
- result = getLocalCallContext(cc, node.getEnclosingCallable())
+ exists(Cc cc0 |
+ cc = pragma[only_bind_into](cc0) and
+ localFlowEntry(node, config) and
+ result = getLocalCallContext(cc0, getNodeEnclosingCallable(node))
+ )
}
private predicate localStep(
@@ -2147,8 +2152,8 @@ private module Stage4 {
Configuration config
) {
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, _, _, _, config) and
- PrevStage::revFlow(node1, _, _, _, unbind(config))
+ PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -2157,8 +2162,8 @@ private module Stage4 {
Configuration config
) {
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, _, _, _, config) and
- PrevStage::revFlow(node1, _, _, _, unbind(config))
+ PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config))
}
bindingset[node, ap]
@@ -2173,6 +2178,13 @@ private module Stage4 {
PrevStage::revFlow(node, _, _, apa, config)
}
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ exists(ApApprox apa0 |
+ apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
+ )
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -2184,7 +2196,7 @@ private module Stage4 {
pragma[nomagic]
predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
- flowCand(node, getApprox(ap), config) and
+ flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
@@ -2208,16 +2220,16 @@ private module Stage4 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -2262,7 +2274,7 @@ private module Stage4 {
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, getApprox(ap1), tc, node2, contentType, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -2297,7 +2309,7 @@ private module Stage4 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -2316,7 +2328,7 @@ private module Stage4 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -2341,7 +2353,7 @@ private module Stage4 {
) {
exists(ParameterNode p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
}
@@ -2389,9 +2401,9 @@ private module Stage4 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -2403,9 +2415,9 @@ private module Stage4 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -2458,9 +2470,10 @@ private module Stage4 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -2526,9 +2539,10 @@ private module Stage4 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ ap2, _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -2547,13 +2561,13 @@ private module Stage4 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -2580,14 +2594,16 @@ private module Stage4 {
}
bindingset[conf, result]
-private Configuration unbind(Configuration conf) { result >= conf and result <= conf }
+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) {
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
- n.getEnclosingCallable() = c
+ getNodeEnclosingCallable(n) = c
)
}
@@ -2750,13 +2766,13 @@ private newtype TPathNode =
// ... or a step from an existing PathNode to another node.
exists(PathNodeMid mid |
pathStep(mid, node, cc, sc, ap) and
- config = mid.getConfiguration() and
- Stage4::revFlow(node, _, _, ap.getApprox(), unbind(config))
+ pragma[only_bind_into](config) = mid.getConfiguration() and
+ Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(Node node, Configuration config) {
- config.isSink(node) and
- Stage4::revFlow(node, unbind(config)) and
+ pragma[only_bind_into](config).isSink(node) and
+ Stage4::revFlow(node, pragma[only_bind_into](config)) and
(
// A sink that is also a source ...
config.isSource(node)
@@ -2764,7 +2780,7 @@ private newtype TPathNode =
// ... or a sink that can be reached from a source
exists(PathNodeMid mid |
pathStep(mid, node, _, _, TAccessPathNil(_)) and
- config = unbind(mid.getConfiguration())
+ pragma[only_bind_into](config) = mid.getConfiguration()
)
)
}
@@ -3061,7 +3077,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
private PathNodeMid getSuccMid() {
pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and
- result.getConfiguration() = unbind(this.getConfiguration())
+ result.getConfiguration() = unbindConf(this.getConfiguration())
}
override PathNodeImpl getASuccessorImpl() {
@@ -3073,7 +3089,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
mid = getSuccMid() and
mid.getNode() = sink.getNode() and
mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbind(mid.getConfiguration()) and
+ sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
result = sink
)
}
@@ -3116,7 +3132,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
conf = mid.getConfiguration() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
- localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and
ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
@@ -3304,7 +3320,7 @@ private predicate pathThroughCallable0(
) {
exists(CallContext innercc, SummaryCtx sc |
pathIntoCallable(mid, _, cc, innercc, sc, call) and
- paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration()))
+ paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration()))
)
}
@@ -3316,7 +3332,7 @@ pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node 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, unbind(mid.getConfiguration()))
+ out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
)
}
@@ -3402,14 +3418,14 @@ private module FlowExploration {
// flow out of a callable
viableReturnPosOut(_, getReturnPosition(node1), node2)
|
- c1 = node1.getEnclosingCallable() and
- c2 = node2.getEnclosingCallable() and
+ c1 = getNodeEnclosingCallable(node1) and
+ c2 = getNodeEnclosingCallable(node2) and
c1 != c2
)
}
private predicate interestingCallableSrc(DataFlowCallable c, Configuration config) {
- exists(Node n | config.isSource(n) and c = n.getEnclosingCallable())
+ exists(Node n | config.isSource(n) and c = getNodeEnclosingCallable(n))
or
exists(DataFlowCallable mid |
interestingCallableSrc(mid, config) and callableStep(mid, c, config)
@@ -3417,7 +3433,7 @@ private module FlowExploration {
}
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
- exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
+ exists(Node n | config.isSink(n) and c = getNodeEnclosingCallable(n))
or
exists(DataFlowCallable mid |
interestingCallableSink(mid, config) and callableStep(c, mid, config)
@@ -3439,20 +3455,20 @@ private module FlowExploration {
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
callableStep(c1, c2, config) and
- ce1 = TCallable(c1, config) and
- ce2 = TCallable(c2, unbind(config))
+ ce1 = TCallable(c1, pragma[only_bind_into](config)) and
+ ce2 = TCallable(c2, pragma[only_bind_into](config))
)
or
exists(Node n, Configuration config |
ce1 = TCallableSrc() and
config.isSource(n) and
- ce2 = TCallable(n.getEnclosingCallable(), config)
+ ce2 = TCallable(getNodeEnclosingCallable(n), config)
)
or
exists(Node n, Configuration config |
ce2 = TCallableSink() and
config.isSink(n) and
- ce1 = TCallable(n.getEnclosingCallable(), config)
+ ce1 = TCallable(getNodeEnclosingCallable(n), config)
)
}
@@ -3583,7 +3599,7 @@ private module FlowExploration {
exists(config.explorationLimit())
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
- distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
+ distSrc(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
@@ -3600,7 +3616,7 @@ private module FlowExploration {
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not clearsContent(node, ap.getHead()) and
not fullBarrier(node, config) and
- distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
+ distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
)
}
@@ -3659,7 +3675,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSourceDistance() {
- result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
+ result = distSrc(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
}
/**
@@ -3667,7 +3683,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSinkDistance() {
- result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
+ result = distSink(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
}
private string ppAp() {
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 59cc8d529a7..8b446d28b86 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
@@ -207,23 +207,14 @@ private predicate fullBarrier(Node node, Configuration config) {
)
}
-private class AdditionalFlowStepSource extends Node {
- AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) }
-}
-
-pragma[noinline]
-private predicate isAdditionalFlowStep(
- AdditionalFlowStepSource node1, Node node2, DataFlowCallable callable1, Configuration config
-) {
- config.isAdditionalFlowStep(node1, node2) and
- callable1 = node1.getEnclosingCallable()
-}
-
/**
* 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) and
+ (
+ simpleLocalFlowStep(node1, node2) or
+ reverseStepThroughInputOutputAlias(node1, node2)
+ ) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
@@ -234,7 +225,8 @@ private predicate localFlowStep(Node node1, Node node2, Configuration config) {
* Holds if the additional step from `node1` to `node2` does not jump between callables.
*/
private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
- isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and
+ 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
@@ -256,14 +248,12 @@ private predicate jumpStep(Node node1, Node node2, Configuration config) {
* Holds if the additional step from `node1` to `node2` jumps between callables.
*/
private predicate additionalJumpStep(Node node1, Node node2, Configuration config) {
- exists(DataFlowCallable callable1 |
- isAdditionalFlowStep(node1, node2, callable1, config) and
- node2.getEnclosingCallable() != callable1 and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, 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)
}
/**
@@ -454,8 +444,8 @@ private module Stage1 {
// read
exists(Node mid, Content c |
read(node, c, mid) and
- fwdFlowConsCand(c, unbind(config)) and
- revFlow(mid, toReturn, config)
+ fwdFlowConsCand(c, pragma[only_bind_into](config)) and
+ revFlow(mid, toReturn, pragma[only_bind_into](config))
)
or
// flow into a callable
@@ -481,18 +471,18 @@ private module Stage1 {
pragma[nomagic]
private predicate revFlowConsCand(Content c, Configuration config) {
exists(Node mid, Node node |
- fwdFlow(node, unbind(config)) and
+ fwdFlow(node, pragma[only_bind_into](config)) and
read(node, c, mid) and
- fwdFlowConsCand(c, unbind(config)) and
- revFlow(mid, _, config)
+ 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 |
- revFlow(mid, toReturn, config) and
- fwdFlowConsCand(c, unbind(config)) and
+ revFlow(mid, toReturn, pragma[only_bind_into](config)) and
+ fwdFlowConsCand(c, pragma[only_bind_into](config)) and
store(node, tc, mid, _) and
c = tc.getContent()
)
@@ -562,8 +552,8 @@ private module Stage1 {
Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
) {
exists(Content c |
- revFlowIsReadAndStored(c, config) and
- revFlow(node2, unbind(config)) and
+ revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
+ revFlow(node2, pragma[only_bind_into](config)) and
store(node1, tc, node2, contentType) and
c = tc.getContent() and
exists(ap1)
@@ -572,8 +562,8 @@ private module Stage1 {
pragma[nomagic]
predicate readStepCand(Node n1, Content c, Node n2, Configuration config) {
- revFlowIsReadAndStored(c, config) and
- revFlow(n2, unbind(config)) and
+ revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
+ revFlow(n2, pragma[only_bind_into](config)) and
read(n1, c, n2)
}
@@ -598,7 +588,7 @@ private module Stage1 {
) {
exists(ReturnNodeExt ret |
throughFlowNodeCand(ret, config) and
- callable = ret.getEnclosingCallable() and
+ callable = getNodeEnclosingCallable(ret) and
kind = ret.getKind()
)
}
@@ -611,7 +601,7 @@ private module Stage1 {
exists(ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
- p.getEnclosingCallable() = c and
+ getNodeEnclosingCallable(p) = c and
exists(ap) and
// we don't expect a parameter to return stored in itself
not exists(int pos |
@@ -636,9 +626,6 @@ private module Stage1 {
/* End: Stage 1 logic. */
}
-bindingset[result, b]
-private boolean unbindBool(boolean b) { result != b.booleanNot() }
-
pragma[noinline]
private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
Stage1::revFlow(node2, config) and
@@ -775,7 +762,7 @@ private module Stage2 {
bindingset[result, ap]
private ApApprox getApprox(Ap ap) { any() }
- private ApNil getApNil(Node node) { any() }
+ private ApNil getApNil(Node 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) }
@@ -874,16 +861,16 @@ private module Stage2 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -963,7 +950,7 @@ private module Stage2 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -982,7 +969,7 @@ private module Stage2 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1055,9 +1042,9 @@ private module Stage2 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -1069,9 +1056,9 @@ private module Stage2 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -1124,9 +1111,10 @@ private module Stage2 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -1192,9 +1180,10 @@ private module Stage2 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ unbindBool(ap2), _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -1213,13 +1202,13 @@ private module Stage2 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -1250,8 +1239,8 @@ private predicate flowOutOfCallNodeCand2(
DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
- Stage2::revFlow(node2, config) and
- Stage2::revFlow(node1, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node1, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -1260,8 +1249,8 @@ private predicate flowIntoCallNodeCand2(
Configuration config
) {
flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
- Stage2::revFlow(node2, config) and
- Stage2::revFlow(node1, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node1, pragma[only_bind_into](config))
}
private module LocalFlowBigStep {
@@ -1316,8 +1305,8 @@ private module LocalFlowBigStep {
pragma[noinline]
private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
- Stage2::revFlow(node1, _, _, false, config) and
- Stage2::revFlow(node2, _, _, false, unbind(config))
+ Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config))
}
/**
@@ -1334,7 +1323,7 @@ private module LocalFlowBigStep {
) {
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
(
- localFlowEntry(node1, config) and
+ localFlowEntry(node1, pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
@@ -1345,24 +1334,24 @@ private module LocalFlowBigStep {
t = getNodeType(node2)
) and
node1 != node2 and
- cc.relevantFor(node1.getEnclosingCallable()) and
+ cc.relevantFor(getNodeEnclosingCallable(node1)) and
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
- Stage2::revFlow(node2, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
or
exists(Node mid |
- localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
+ 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, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
)
or
exists(Node mid |
- localFlowStepPlus(node1, mid, _, _, config, cc) and
+ 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
- Stage2::revFlow(node2, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
)
)
}
@@ -1394,7 +1383,9 @@ private module Stage3 {
private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(Node node) { result = TFrontNil(getNodeType(node)) }
+ private ApNil getApNil(Node node) {
+ PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node))
+ }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
@@ -1467,6 +1458,13 @@ private module Stage3 {
PrevStage::revFlow(node, _, _, apa, config)
}
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ exists(ApApprox apa0 |
+ apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
+ )
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -1478,7 +1476,7 @@ private module Stage3 {
pragma[nomagic]
predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
- flowCand(node, unbindBool(getApprox(ap)), config) and
+ flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
@@ -1502,16 +1500,16 @@ private module Stage3 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -1556,7 +1554,7 @@ private module Stage3 {
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindBool(getApprox(ap1)), tc, node2, contentType, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1591,7 +1589,7 @@ private module Stage3 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1610,7 +1608,7 @@ private module Stage3 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1635,7 +1633,7 @@ private module Stage3 {
) {
exists(ParameterNode p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindBool(getApprox(ap)), config)
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
}
@@ -1683,9 +1681,9 @@ private module Stage3 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -1697,9 +1695,9 @@ private module Stage3 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -1752,9 +1750,10 @@ private module Stage3 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -1820,9 +1819,10 @@ private module Stage3 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ ap2, _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -1841,13 +1841,13 @@ private module Stage3 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -2087,7 +2087,9 @@ private module Stage4 {
private ApApprox getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(Node node) { result = TNil(getNodeType(node)) }
+ private ApNil getApNil(Node node) {
+ PrevStage::revFlow(node, _) and result = TNil(getNodeType(node))
+ }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
@@ -2131,8 +2133,11 @@ private module Stage4 {
bindingset[node, cc, config]
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
- localFlowEntry(node, config) and
- result = getLocalCallContext(cc, node.getEnclosingCallable())
+ exists(Cc cc0 |
+ cc = pragma[only_bind_into](cc0) and
+ localFlowEntry(node, config) and
+ result = getLocalCallContext(cc0, getNodeEnclosingCallable(node))
+ )
}
private predicate localStep(
@@ -2147,8 +2152,8 @@ private module Stage4 {
Configuration config
) {
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, _, _, _, config) and
- PrevStage::revFlow(node1, _, _, _, unbind(config))
+ PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -2157,8 +2162,8 @@ private module Stage4 {
Configuration config
) {
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, _, _, _, config) and
- PrevStage::revFlow(node1, _, _, _, unbind(config))
+ PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config))
}
bindingset[node, ap]
@@ -2173,6 +2178,13 @@ private module Stage4 {
PrevStage::revFlow(node, _, _, apa, config)
}
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ exists(ApApprox apa0 |
+ apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
+ )
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -2184,7 +2196,7 @@ private module Stage4 {
pragma[nomagic]
predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
- flowCand(node, getApprox(ap), config) and
+ flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
@@ -2208,16 +2220,16 @@ private module Stage4 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -2262,7 +2274,7 @@ private module Stage4 {
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, getApprox(ap1), tc, node2, contentType, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -2297,7 +2309,7 @@ private module Stage4 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -2316,7 +2328,7 @@ private module Stage4 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -2341,7 +2353,7 @@ private module Stage4 {
) {
exists(ParameterNode p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
}
@@ -2389,9 +2401,9 @@ private module Stage4 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -2403,9 +2415,9 @@ private module Stage4 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -2458,9 +2470,10 @@ private module Stage4 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -2526,9 +2539,10 @@ private module Stage4 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ ap2, _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -2547,13 +2561,13 @@ private module Stage4 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -2580,14 +2594,16 @@ private module Stage4 {
}
bindingset[conf, result]
-private Configuration unbind(Configuration conf) { result >= conf and result <= conf }
+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) {
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
- n.getEnclosingCallable() = c
+ getNodeEnclosingCallable(n) = c
)
}
@@ -2750,13 +2766,13 @@ private newtype TPathNode =
// ... or a step from an existing PathNode to another node.
exists(PathNodeMid mid |
pathStep(mid, node, cc, sc, ap) and
- config = mid.getConfiguration() and
- Stage4::revFlow(node, _, _, ap.getApprox(), unbind(config))
+ pragma[only_bind_into](config) = mid.getConfiguration() and
+ Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(Node node, Configuration config) {
- config.isSink(node) and
- Stage4::revFlow(node, unbind(config)) and
+ pragma[only_bind_into](config).isSink(node) and
+ Stage4::revFlow(node, pragma[only_bind_into](config)) and
(
// A sink that is also a source ...
config.isSource(node)
@@ -2764,7 +2780,7 @@ private newtype TPathNode =
// ... or a sink that can be reached from a source
exists(PathNodeMid mid |
pathStep(mid, node, _, _, TAccessPathNil(_)) and
- config = unbind(mid.getConfiguration())
+ pragma[only_bind_into](config) = mid.getConfiguration()
)
)
}
@@ -3061,7 +3077,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
private PathNodeMid getSuccMid() {
pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and
- result.getConfiguration() = unbind(this.getConfiguration())
+ result.getConfiguration() = unbindConf(this.getConfiguration())
}
override PathNodeImpl getASuccessorImpl() {
@@ -3073,7 +3089,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
mid = getSuccMid() and
mid.getNode() = sink.getNode() and
mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbind(mid.getConfiguration()) and
+ sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
result = sink
)
}
@@ -3116,7 +3132,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
conf = mid.getConfiguration() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
- localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and
ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
@@ -3304,7 +3320,7 @@ private predicate pathThroughCallable0(
) {
exists(CallContext innercc, SummaryCtx sc |
pathIntoCallable(mid, _, cc, innercc, sc, call) and
- paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration()))
+ paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration()))
)
}
@@ -3316,7 +3332,7 @@ pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node 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, unbind(mid.getConfiguration()))
+ out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
)
}
@@ -3402,14 +3418,14 @@ private module FlowExploration {
// flow out of a callable
viableReturnPosOut(_, getReturnPosition(node1), node2)
|
- c1 = node1.getEnclosingCallable() and
- c2 = node2.getEnclosingCallable() and
+ c1 = getNodeEnclosingCallable(node1) and
+ c2 = getNodeEnclosingCallable(node2) and
c1 != c2
)
}
private predicate interestingCallableSrc(DataFlowCallable c, Configuration config) {
- exists(Node n | config.isSource(n) and c = n.getEnclosingCallable())
+ exists(Node n | config.isSource(n) and c = getNodeEnclosingCallable(n))
or
exists(DataFlowCallable mid |
interestingCallableSrc(mid, config) and callableStep(mid, c, config)
@@ -3417,7 +3433,7 @@ private module FlowExploration {
}
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
- exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
+ exists(Node n | config.isSink(n) and c = getNodeEnclosingCallable(n))
or
exists(DataFlowCallable mid |
interestingCallableSink(mid, config) and callableStep(c, mid, config)
@@ -3439,20 +3455,20 @@ private module FlowExploration {
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
callableStep(c1, c2, config) and
- ce1 = TCallable(c1, config) and
- ce2 = TCallable(c2, unbind(config))
+ ce1 = TCallable(c1, pragma[only_bind_into](config)) and
+ ce2 = TCallable(c2, pragma[only_bind_into](config))
)
or
exists(Node n, Configuration config |
ce1 = TCallableSrc() and
config.isSource(n) and
- ce2 = TCallable(n.getEnclosingCallable(), config)
+ ce2 = TCallable(getNodeEnclosingCallable(n), config)
)
or
exists(Node n, Configuration config |
ce2 = TCallableSink() and
config.isSink(n) and
- ce1 = TCallable(n.getEnclosingCallable(), config)
+ ce1 = TCallable(getNodeEnclosingCallable(n), config)
)
}
@@ -3583,7 +3599,7 @@ private module FlowExploration {
exists(config.explorationLimit())
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
- distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
+ distSrc(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
@@ -3600,7 +3616,7 @@ private module FlowExploration {
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not clearsContent(node, ap.getHead()) and
not fullBarrier(node, config) and
- distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
+ distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
)
}
@@ -3659,7 +3675,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSourceDistance() {
- result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
+ result = distSrc(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
}
/**
@@ -3667,7 +3683,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSinkDistance() {
- result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
+ result = distSink(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
}
private string ppAp() {
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 59cc8d529a7..8b446d28b86 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
@@ -207,23 +207,14 @@ private predicate fullBarrier(Node node, Configuration config) {
)
}
-private class AdditionalFlowStepSource extends Node {
- AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) }
-}
-
-pragma[noinline]
-private predicate isAdditionalFlowStep(
- AdditionalFlowStepSource node1, Node node2, DataFlowCallable callable1, Configuration config
-) {
- config.isAdditionalFlowStep(node1, node2) and
- callable1 = node1.getEnclosingCallable()
-}
-
/**
* 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) and
+ (
+ simpleLocalFlowStep(node1, node2) or
+ reverseStepThroughInputOutputAlias(node1, node2)
+ ) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
@@ -234,7 +225,8 @@ private predicate localFlowStep(Node node1, Node node2, Configuration config) {
* Holds if the additional step from `node1` to `node2` does not jump between callables.
*/
private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
- isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and
+ 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
@@ -256,14 +248,12 @@ private predicate jumpStep(Node node1, Node node2, Configuration config) {
* Holds if the additional step from `node1` to `node2` jumps between callables.
*/
private predicate additionalJumpStep(Node node1, Node node2, Configuration config) {
- exists(DataFlowCallable callable1 |
- isAdditionalFlowStep(node1, node2, callable1, config) and
- node2.getEnclosingCallable() != callable1 and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, 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)
}
/**
@@ -454,8 +444,8 @@ private module Stage1 {
// read
exists(Node mid, Content c |
read(node, c, mid) and
- fwdFlowConsCand(c, unbind(config)) and
- revFlow(mid, toReturn, config)
+ fwdFlowConsCand(c, pragma[only_bind_into](config)) and
+ revFlow(mid, toReturn, pragma[only_bind_into](config))
)
or
// flow into a callable
@@ -481,18 +471,18 @@ private module Stage1 {
pragma[nomagic]
private predicate revFlowConsCand(Content c, Configuration config) {
exists(Node mid, Node node |
- fwdFlow(node, unbind(config)) and
+ fwdFlow(node, pragma[only_bind_into](config)) and
read(node, c, mid) and
- fwdFlowConsCand(c, unbind(config)) and
- revFlow(mid, _, config)
+ 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 |
- revFlow(mid, toReturn, config) and
- fwdFlowConsCand(c, unbind(config)) and
+ revFlow(mid, toReturn, pragma[only_bind_into](config)) and
+ fwdFlowConsCand(c, pragma[only_bind_into](config)) and
store(node, tc, mid, _) and
c = tc.getContent()
)
@@ -562,8 +552,8 @@ private module Stage1 {
Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
) {
exists(Content c |
- revFlowIsReadAndStored(c, config) and
- revFlow(node2, unbind(config)) and
+ revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
+ revFlow(node2, pragma[only_bind_into](config)) and
store(node1, tc, node2, contentType) and
c = tc.getContent() and
exists(ap1)
@@ -572,8 +562,8 @@ private module Stage1 {
pragma[nomagic]
predicate readStepCand(Node n1, Content c, Node n2, Configuration config) {
- revFlowIsReadAndStored(c, config) and
- revFlow(n2, unbind(config)) and
+ revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
+ revFlow(n2, pragma[only_bind_into](config)) and
read(n1, c, n2)
}
@@ -598,7 +588,7 @@ private module Stage1 {
) {
exists(ReturnNodeExt ret |
throughFlowNodeCand(ret, config) and
- callable = ret.getEnclosingCallable() and
+ callable = getNodeEnclosingCallable(ret) and
kind = ret.getKind()
)
}
@@ -611,7 +601,7 @@ private module Stage1 {
exists(ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
- p.getEnclosingCallable() = c and
+ getNodeEnclosingCallable(p) = c and
exists(ap) and
// we don't expect a parameter to return stored in itself
not exists(int pos |
@@ -636,9 +626,6 @@ private module Stage1 {
/* End: Stage 1 logic. */
}
-bindingset[result, b]
-private boolean unbindBool(boolean b) { result != b.booleanNot() }
-
pragma[noinline]
private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
Stage1::revFlow(node2, config) and
@@ -775,7 +762,7 @@ private module Stage2 {
bindingset[result, ap]
private ApApprox getApprox(Ap ap) { any() }
- private ApNil getApNil(Node node) { any() }
+ private ApNil getApNil(Node 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) }
@@ -874,16 +861,16 @@ private module Stage2 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -963,7 +950,7 @@ private module Stage2 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -982,7 +969,7 @@ private module Stage2 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1055,9 +1042,9 @@ private module Stage2 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -1069,9 +1056,9 @@ private module Stage2 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -1124,9 +1111,10 @@ private module Stage2 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -1192,9 +1180,10 @@ private module Stage2 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ unbindBool(ap2), _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -1213,13 +1202,13 @@ private module Stage2 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -1250,8 +1239,8 @@ private predicate flowOutOfCallNodeCand2(
DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
- Stage2::revFlow(node2, config) and
- Stage2::revFlow(node1, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node1, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -1260,8 +1249,8 @@ private predicate flowIntoCallNodeCand2(
Configuration config
) {
flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
- Stage2::revFlow(node2, config) and
- Stage2::revFlow(node1, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node1, pragma[only_bind_into](config))
}
private module LocalFlowBigStep {
@@ -1316,8 +1305,8 @@ private module LocalFlowBigStep {
pragma[noinline]
private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
- Stage2::revFlow(node1, _, _, false, config) and
- Stage2::revFlow(node2, _, _, false, unbind(config))
+ Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config))
}
/**
@@ -1334,7 +1323,7 @@ private module LocalFlowBigStep {
) {
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
(
- localFlowEntry(node1, config) and
+ localFlowEntry(node1, pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
@@ -1345,24 +1334,24 @@ private module LocalFlowBigStep {
t = getNodeType(node2)
) and
node1 != node2 and
- cc.relevantFor(node1.getEnclosingCallable()) and
+ cc.relevantFor(getNodeEnclosingCallable(node1)) and
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
- Stage2::revFlow(node2, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
or
exists(Node mid |
- localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
+ 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, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
)
or
exists(Node mid |
- localFlowStepPlus(node1, mid, _, _, config, cc) and
+ 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
- Stage2::revFlow(node2, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
)
)
}
@@ -1394,7 +1383,9 @@ private module Stage3 {
private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(Node node) { result = TFrontNil(getNodeType(node)) }
+ private ApNil getApNil(Node node) {
+ PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node))
+ }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
@@ -1467,6 +1458,13 @@ private module Stage3 {
PrevStage::revFlow(node, _, _, apa, config)
}
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ exists(ApApprox apa0 |
+ apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
+ )
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -1478,7 +1476,7 @@ private module Stage3 {
pragma[nomagic]
predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
- flowCand(node, unbindBool(getApprox(ap)), config) and
+ flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
@@ -1502,16 +1500,16 @@ private module Stage3 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -1556,7 +1554,7 @@ private module Stage3 {
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindBool(getApprox(ap1)), tc, node2, contentType, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1591,7 +1589,7 @@ private module Stage3 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1610,7 +1608,7 @@ private module Stage3 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1635,7 +1633,7 @@ private module Stage3 {
) {
exists(ParameterNode p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindBool(getApprox(ap)), config)
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
}
@@ -1683,9 +1681,9 @@ private module Stage3 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -1697,9 +1695,9 @@ private module Stage3 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -1752,9 +1750,10 @@ private module Stage3 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -1820,9 +1819,10 @@ private module Stage3 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ ap2, _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -1841,13 +1841,13 @@ private module Stage3 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -2087,7 +2087,9 @@ private module Stage4 {
private ApApprox getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(Node node) { result = TNil(getNodeType(node)) }
+ private ApNil getApNil(Node node) {
+ PrevStage::revFlow(node, _) and result = TNil(getNodeType(node))
+ }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
@@ -2131,8 +2133,11 @@ private module Stage4 {
bindingset[node, cc, config]
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
- localFlowEntry(node, config) and
- result = getLocalCallContext(cc, node.getEnclosingCallable())
+ exists(Cc cc0 |
+ cc = pragma[only_bind_into](cc0) and
+ localFlowEntry(node, config) and
+ result = getLocalCallContext(cc0, getNodeEnclosingCallable(node))
+ )
}
private predicate localStep(
@@ -2147,8 +2152,8 @@ private module Stage4 {
Configuration config
) {
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, _, _, _, config) and
- PrevStage::revFlow(node1, _, _, _, unbind(config))
+ PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -2157,8 +2162,8 @@ private module Stage4 {
Configuration config
) {
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, _, _, _, config) and
- PrevStage::revFlow(node1, _, _, _, unbind(config))
+ PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config))
}
bindingset[node, ap]
@@ -2173,6 +2178,13 @@ private module Stage4 {
PrevStage::revFlow(node, _, _, apa, config)
}
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ exists(ApApprox apa0 |
+ apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
+ )
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -2184,7 +2196,7 @@ private module Stage4 {
pragma[nomagic]
predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
- flowCand(node, getApprox(ap), config) and
+ flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
@@ -2208,16 +2220,16 @@ private module Stage4 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -2262,7 +2274,7 @@ private module Stage4 {
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, getApprox(ap1), tc, node2, contentType, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -2297,7 +2309,7 @@ private module Stage4 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -2316,7 +2328,7 @@ private module Stage4 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -2341,7 +2353,7 @@ private module Stage4 {
) {
exists(ParameterNode p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
}
@@ -2389,9 +2401,9 @@ private module Stage4 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -2403,9 +2415,9 @@ private module Stage4 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -2458,9 +2470,10 @@ private module Stage4 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -2526,9 +2539,10 @@ private module Stage4 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ ap2, _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -2547,13 +2561,13 @@ private module Stage4 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -2580,14 +2594,16 @@ private module Stage4 {
}
bindingset[conf, result]
-private Configuration unbind(Configuration conf) { result >= conf and result <= conf }
+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) {
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
- n.getEnclosingCallable() = c
+ getNodeEnclosingCallable(n) = c
)
}
@@ -2750,13 +2766,13 @@ private newtype TPathNode =
// ... or a step from an existing PathNode to another node.
exists(PathNodeMid mid |
pathStep(mid, node, cc, sc, ap) and
- config = mid.getConfiguration() and
- Stage4::revFlow(node, _, _, ap.getApprox(), unbind(config))
+ pragma[only_bind_into](config) = mid.getConfiguration() and
+ Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(Node node, Configuration config) {
- config.isSink(node) and
- Stage4::revFlow(node, unbind(config)) and
+ pragma[only_bind_into](config).isSink(node) and
+ Stage4::revFlow(node, pragma[only_bind_into](config)) and
(
// A sink that is also a source ...
config.isSource(node)
@@ -2764,7 +2780,7 @@ private newtype TPathNode =
// ... or a sink that can be reached from a source
exists(PathNodeMid mid |
pathStep(mid, node, _, _, TAccessPathNil(_)) and
- config = unbind(mid.getConfiguration())
+ pragma[only_bind_into](config) = mid.getConfiguration()
)
)
}
@@ -3061,7 +3077,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
private PathNodeMid getSuccMid() {
pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and
- result.getConfiguration() = unbind(this.getConfiguration())
+ result.getConfiguration() = unbindConf(this.getConfiguration())
}
override PathNodeImpl getASuccessorImpl() {
@@ -3073,7 +3089,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
mid = getSuccMid() and
mid.getNode() = sink.getNode() and
mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbind(mid.getConfiguration()) and
+ sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
result = sink
)
}
@@ -3116,7 +3132,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
conf = mid.getConfiguration() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
- localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and
ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
@@ -3304,7 +3320,7 @@ private predicate pathThroughCallable0(
) {
exists(CallContext innercc, SummaryCtx sc |
pathIntoCallable(mid, _, cc, innercc, sc, call) and
- paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration()))
+ paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration()))
)
}
@@ -3316,7 +3332,7 @@ pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node 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, unbind(mid.getConfiguration()))
+ out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
)
}
@@ -3402,14 +3418,14 @@ private module FlowExploration {
// flow out of a callable
viableReturnPosOut(_, getReturnPosition(node1), node2)
|
- c1 = node1.getEnclosingCallable() and
- c2 = node2.getEnclosingCallable() and
+ c1 = getNodeEnclosingCallable(node1) and
+ c2 = getNodeEnclosingCallable(node2) and
c1 != c2
)
}
private predicate interestingCallableSrc(DataFlowCallable c, Configuration config) {
- exists(Node n | config.isSource(n) and c = n.getEnclosingCallable())
+ exists(Node n | config.isSource(n) and c = getNodeEnclosingCallable(n))
or
exists(DataFlowCallable mid |
interestingCallableSrc(mid, config) and callableStep(mid, c, config)
@@ -3417,7 +3433,7 @@ private module FlowExploration {
}
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
- exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
+ exists(Node n | config.isSink(n) and c = getNodeEnclosingCallable(n))
or
exists(DataFlowCallable mid |
interestingCallableSink(mid, config) and callableStep(c, mid, config)
@@ -3439,20 +3455,20 @@ private module FlowExploration {
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
callableStep(c1, c2, config) and
- ce1 = TCallable(c1, config) and
- ce2 = TCallable(c2, unbind(config))
+ ce1 = TCallable(c1, pragma[only_bind_into](config)) and
+ ce2 = TCallable(c2, pragma[only_bind_into](config))
)
or
exists(Node n, Configuration config |
ce1 = TCallableSrc() and
config.isSource(n) and
- ce2 = TCallable(n.getEnclosingCallable(), config)
+ ce2 = TCallable(getNodeEnclosingCallable(n), config)
)
or
exists(Node n, Configuration config |
ce2 = TCallableSink() and
config.isSink(n) and
- ce1 = TCallable(n.getEnclosingCallable(), config)
+ ce1 = TCallable(getNodeEnclosingCallable(n), config)
)
}
@@ -3583,7 +3599,7 @@ private module FlowExploration {
exists(config.explorationLimit())
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
- distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
+ distSrc(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
@@ -3600,7 +3616,7 @@ private module FlowExploration {
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not clearsContent(node, ap.getHead()) and
not fullBarrier(node, config) and
- distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
+ distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
)
}
@@ -3659,7 +3675,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSourceDistance() {
- result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
+ result = distSrc(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
}
/**
@@ -3667,7 +3683,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSinkDistance() {
- result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
+ result = distSink(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
}
private string ppAp() {
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 59cc8d529a7..8b446d28b86 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
@@ -207,23 +207,14 @@ private predicate fullBarrier(Node node, Configuration config) {
)
}
-private class AdditionalFlowStepSource extends Node {
- AdditionalFlowStepSource() { any(Configuration c).isAdditionalFlowStep(this, _) }
-}
-
-pragma[noinline]
-private predicate isAdditionalFlowStep(
- AdditionalFlowStepSource node1, Node node2, DataFlowCallable callable1, Configuration config
-) {
- config.isAdditionalFlowStep(node1, node2) and
- callable1 = node1.getEnclosingCallable()
-}
-
/**
* 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) and
+ (
+ simpleLocalFlowStep(node1, node2) or
+ reverseStepThroughInputOutputAlias(node1, node2)
+ ) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and
@@ -234,7 +225,8 @@ private predicate localFlowStep(Node node1, Node node2, Configuration config) {
* Holds if the additional step from `node1` to `node2` does not jump between callables.
*/
private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration config) {
- isAdditionalFlowStep(node1, node2, node2.getEnclosingCallable(), config) and
+ 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
@@ -256,14 +248,12 @@ private predicate jumpStep(Node node1, Node node2, Configuration config) {
* Holds if the additional step from `node1` to `node2` jumps between callables.
*/
private predicate additionalJumpStep(Node node1, Node node2, Configuration config) {
- exists(DataFlowCallable callable1 |
- isAdditionalFlowStep(node1, node2, callable1, config) and
- node2.getEnclosingCallable() != callable1 and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- not fullBarrier(node1, config) and
- not fullBarrier(node2, 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)
}
/**
@@ -454,8 +444,8 @@ private module Stage1 {
// read
exists(Node mid, Content c |
read(node, c, mid) and
- fwdFlowConsCand(c, unbind(config)) and
- revFlow(mid, toReturn, config)
+ fwdFlowConsCand(c, pragma[only_bind_into](config)) and
+ revFlow(mid, toReturn, pragma[only_bind_into](config))
)
or
// flow into a callable
@@ -481,18 +471,18 @@ private module Stage1 {
pragma[nomagic]
private predicate revFlowConsCand(Content c, Configuration config) {
exists(Node mid, Node node |
- fwdFlow(node, unbind(config)) and
+ fwdFlow(node, pragma[only_bind_into](config)) and
read(node, c, mid) and
- fwdFlowConsCand(c, unbind(config)) and
- revFlow(mid, _, config)
+ 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 |
- revFlow(mid, toReturn, config) and
- fwdFlowConsCand(c, unbind(config)) and
+ revFlow(mid, toReturn, pragma[only_bind_into](config)) and
+ fwdFlowConsCand(c, pragma[only_bind_into](config)) and
store(node, tc, mid, _) and
c = tc.getContent()
)
@@ -562,8 +552,8 @@ private module Stage1 {
Node node1, Ap ap1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
) {
exists(Content c |
- revFlowIsReadAndStored(c, config) and
- revFlow(node2, unbind(config)) and
+ revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
+ revFlow(node2, pragma[only_bind_into](config)) and
store(node1, tc, node2, contentType) and
c = tc.getContent() and
exists(ap1)
@@ -572,8 +562,8 @@ private module Stage1 {
pragma[nomagic]
predicate readStepCand(Node n1, Content c, Node n2, Configuration config) {
- revFlowIsReadAndStored(c, config) and
- revFlow(n2, unbind(config)) and
+ revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
+ revFlow(n2, pragma[only_bind_into](config)) and
read(n1, c, n2)
}
@@ -598,7 +588,7 @@ private module Stage1 {
) {
exists(ReturnNodeExt ret |
throughFlowNodeCand(ret, config) and
- callable = ret.getEnclosingCallable() and
+ callable = getNodeEnclosingCallable(ret) and
kind = ret.getKind()
)
}
@@ -611,7 +601,7 @@ private module Stage1 {
exists(ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
- p.getEnclosingCallable() = c and
+ getNodeEnclosingCallable(p) = c and
exists(ap) and
// we don't expect a parameter to return stored in itself
not exists(int pos |
@@ -636,9 +626,6 @@ private module Stage1 {
/* End: Stage 1 logic. */
}
-bindingset[result, b]
-private boolean unbindBool(boolean b) { result != b.booleanNot() }
-
pragma[noinline]
private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
Stage1::revFlow(node2, config) and
@@ -775,7 +762,7 @@ private module Stage2 {
bindingset[result, ap]
private ApApprox getApprox(Ap ap) { any() }
- private ApNil getApNil(Node node) { any() }
+ private ApNil getApNil(Node 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) }
@@ -874,16 +861,16 @@ private module Stage2 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -963,7 +950,7 @@ private module Stage2 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -982,7 +969,7 @@ private module Stage2 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1055,9 +1042,9 @@ private module Stage2 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -1069,9 +1056,9 @@ private module Stage2 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -1124,9 +1111,10 @@ private module Stage2 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -1192,9 +1180,10 @@ private module Stage2 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ unbindBool(ap2), _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -1213,13 +1202,13 @@ private module Stage2 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -1250,8 +1239,8 @@ private predicate flowOutOfCallNodeCand2(
DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
- Stage2::revFlow(node2, config) and
- Stage2::revFlow(node1, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node1, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -1260,8 +1249,8 @@ private predicate flowIntoCallNodeCand2(
Configuration config
) {
flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
- Stage2::revFlow(node2, config) and
- Stage2::revFlow(node1, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node1, pragma[only_bind_into](config))
}
private module LocalFlowBigStep {
@@ -1316,8 +1305,8 @@ private module LocalFlowBigStep {
pragma[noinline]
private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
- Stage2::revFlow(node1, _, _, false, config) and
- Stage2::revFlow(node2, _, _, false, unbind(config))
+ Stage2::revFlow(node1, _, _, false, pragma[only_bind_into](config)) and
+ Stage2::revFlow(node2, _, _, false, pragma[only_bind_into](config))
}
/**
@@ -1334,7 +1323,7 @@ private module LocalFlowBigStep {
) {
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
(
- localFlowEntry(node1, config) and
+ localFlowEntry(node1, pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
@@ -1345,24 +1334,24 @@ private module LocalFlowBigStep {
t = getNodeType(node2)
) and
node1 != node2 and
- cc.relevantFor(node1.getEnclosingCallable()) and
+ cc.relevantFor(getNodeEnclosingCallable(node1)) and
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
- Stage2::revFlow(node2, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
or
exists(Node mid |
- localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
+ 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, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
)
or
exists(Node mid |
- localFlowStepPlus(node1, mid, _, _, config, cc) and
+ 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
- Stage2::revFlow(node2, unbind(config))
+ Stage2::revFlow(node2, pragma[only_bind_into](config))
)
)
}
@@ -1394,7 +1383,9 @@ private module Stage3 {
private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(Node node) { result = TFrontNil(getNodeType(node)) }
+ private ApNil getApNil(Node node) {
+ PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node))
+ }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
@@ -1467,6 +1458,13 @@ private module Stage3 {
PrevStage::revFlow(node, _, _, apa, config)
}
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ exists(ApApprox apa0 |
+ apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
+ )
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -1478,7 +1476,7 @@ private module Stage3 {
pragma[nomagic]
predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
- flowCand(node, unbindBool(getApprox(ap)), config) and
+ flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
@@ -1502,16 +1500,16 @@ private module Stage3 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -1556,7 +1554,7 @@ private module Stage3 {
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindBool(getApprox(ap1)), tc, node2, contentType, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1591,7 +1589,7 @@ private module Stage3 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -1610,7 +1608,7 @@ private module Stage3 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -1635,7 +1633,7 @@ private module Stage3 {
) {
exists(ParameterNode p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindBool(getApprox(ap)), config)
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
}
@@ -1683,9 +1681,9 @@ private module Stage3 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -1697,9 +1695,9 @@ private module Stage3 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -1752,9 +1750,10 @@ private module Stage3 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -1820,9 +1819,10 @@ private module Stage3 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ ap2, _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -1841,13 +1841,13 @@ private module Stage3 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -2087,7 +2087,9 @@ private module Stage4 {
private ApApprox getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(Node node) { result = TNil(getNodeType(node)) }
+ private ApNil getApNil(Node node) {
+ PrevStage::revFlow(node, _) and result = TNil(getNodeType(node))
+ }
bindingset[tc, tail]
private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
@@ -2131,8 +2133,11 @@ private module Stage4 {
bindingset[node, cc, config]
private LocalCc getLocalCc(Node node, Cc cc, Configuration config) {
- localFlowEntry(node, config) and
- result = getLocalCallContext(cc, node.getEnclosingCallable())
+ exists(Cc cc0 |
+ cc = pragma[only_bind_into](cc0) and
+ localFlowEntry(node, config) and
+ result = getLocalCallContext(cc0, getNodeEnclosingCallable(node))
+ )
}
private predicate localStep(
@@ -2147,8 +2152,8 @@ private module Stage4 {
Configuration config
) {
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, _, _, _, config) and
- PrevStage::revFlow(node1, _, _, _, unbind(config))
+ PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config))
}
pragma[nomagic]
@@ -2157,8 +2162,8 @@ private module Stage4 {
Configuration config
) {
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, _, _, _, config) and
- PrevStage::revFlow(node1, _, _, _, unbind(config))
+ PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::revFlow(node1, _, _, _, pragma[only_bind_into](config))
}
bindingset[node, ap]
@@ -2173,6 +2178,13 @@ private module Stage4 {
PrevStage::revFlow(node, _, _, apa, config)
}
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ exists(ApApprox apa0 |
+ apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
+ )
+ }
+
/**
* Holds if `node` is reachable with access path `ap` from a source in the
* configuration `config`.
@@ -2184,7 +2196,7 @@ private module Stage4 {
pragma[nomagic]
predicate fwdFlow(Node node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
fwdFlow0(node, cc, argAp, ap, config) and
- flowCand(node, getApprox(ap), config) and
+ flowCand(node, unbindApa(getApprox(ap)), config) and
filter(node, ap)
}
@@ -2208,16 +2220,16 @@ private module Stage4 {
)
or
exists(Node mid |
- fwdFlow(mid, _, _, ap, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, ap, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone()
)
or
exists(Node mid, ApNil nil |
- fwdFlow(mid, _, _, nil, config) and
- flowCand(node, _, unbind(config)) and
+ fwdFlow(mid, _, _, nil, pragma[only_bind_into](config)) and
+ flowCand(node, _, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
argAp = apNone() and
@@ -2262,7 +2274,7 @@ private module Stage4 {
) {
exists(DataFlowType contentType |
fwdFlow(node1, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, getApprox(ap1), tc, node2, contentType, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -2297,7 +2309,7 @@ private module Stage4 {
exists(ArgumentNode arg, boolean allowsFieldFlow |
fwdFlow(arg, outercc, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
+ innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc)
|
ap instanceof ApNil or allowsFieldFlow = true
)
@@ -2316,7 +2328,7 @@ private module Stage4 {
exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner |
fwdFlow(ret, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
+ inner = getNodeEnclosingCallable(ret) and
checkCallContextReturn(innercc, inner, call) and
ccOut = getCallContextReturn(inner, call)
|
@@ -2341,7 +2353,7 @@ private module Stage4 {
) {
exists(ParameterNode p |
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
)
}
@@ -2389,9 +2401,9 @@ private module Stage4 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, mid, false, _, config, _) and
- revFlow(mid, toReturn, returnAp, nil, config) and
+ revFlow(mid, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
@@ -2403,9 +2415,9 @@ private module Stage4 {
)
or
exists(Node mid, ApNil nil |
- fwdFlow(node, _, _, ap, config) and
+ fwdFlow(node, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
- revFlow(mid, _, _, nil, config) and
+ revFlow(pragma[only_bind_into](mid), _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnAp = apNone() and
ap instanceof ApNil
@@ -2458,9 +2470,10 @@ private module Stage4 {
*/
pragma[nomagic]
private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(Node mid |
+ exists(Node mid, Ap tail0 |
revFlow(mid, _, _, tail, config) and
- readStepFwd(_, cons, c, mid, tail, config)
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
)
}
@@ -2526,9 +2539,10 @@ private module Stage4 {
predicate readStepCand(Node node1, Content c, Node node2, Configuration config) {
exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, ap2, config) and
+ revFlow(node2, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, /*unbind*/ ap2, _, _, _, _, _, unbind(config))
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _,
+ pragma[only_bind_into](config))
)
}
@@ -2547,13 +2561,13 @@ private module Stage4 {
ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
) {
revFlow(p, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ c = getNodeEnclosingCallable(p)
}
predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos |
parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
+ c = getNodeEnclosingCallable(ret) and
revFlow(ret, true, apSome(_), ap0, config) and
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
@@ -2580,14 +2594,16 @@ private module Stage4 {
}
bindingset[conf, result]
-private Configuration unbind(Configuration conf) { result >= conf and result <= conf }
+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) {
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
- n.getEnclosingCallable() = c
+ getNodeEnclosingCallable(n) = c
)
}
@@ -2750,13 +2766,13 @@ private newtype TPathNode =
// ... or a step from an existing PathNode to another node.
exists(PathNodeMid mid |
pathStep(mid, node, cc, sc, ap) and
- config = mid.getConfiguration() and
- Stage4::revFlow(node, _, _, ap.getApprox(), unbind(config))
+ pragma[only_bind_into](config) = mid.getConfiguration() and
+ Stage4::revFlow(node, _, _, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(Node node, Configuration config) {
- config.isSink(node) and
- Stage4::revFlow(node, unbind(config)) and
+ pragma[only_bind_into](config).isSink(node) and
+ Stage4::revFlow(node, pragma[only_bind_into](config)) and
(
// A sink that is also a source ...
config.isSource(node)
@@ -2764,7 +2780,7 @@ private newtype TPathNode =
// ... or a sink that can be reached from a source
exists(PathNodeMid mid |
pathStep(mid, node, _, _, TAccessPathNil(_)) and
- config = unbind(mid.getConfiguration())
+ pragma[only_bind_into](config) = mid.getConfiguration()
)
)
}
@@ -3061,7 +3077,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
private PathNodeMid getSuccMid() {
pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and
- result.getConfiguration() = unbind(this.getConfiguration())
+ result.getConfiguration() = unbindConf(this.getConfiguration())
}
override PathNodeImpl getASuccessorImpl() {
@@ -3073,7 +3089,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
mid = getSuccMid() and
mid.getNode() = sink.getNode() and
mid.getAp() instanceof AccessPathNil and
- sink.getConfiguration() = unbind(mid.getConfiguration()) and
+ sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
result = sink
)
}
@@ -3116,7 +3132,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
conf = mid.getConfiguration() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
- localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ localCC = getLocalCallContext(cc, getNodeEnclosingCallable(midnode)) and
ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
@@ -3304,7 +3320,7 @@ private predicate pathThroughCallable0(
) {
exists(CallContext innercc, SummaryCtx sc |
pathIntoCallable(mid, _, cc, innercc, sc, call) and
- paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration()))
+ paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration()))
)
}
@@ -3316,7 +3332,7 @@ pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node 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, unbind(mid.getConfiguration()))
+ out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
)
}
@@ -3402,14 +3418,14 @@ private module FlowExploration {
// flow out of a callable
viableReturnPosOut(_, getReturnPosition(node1), node2)
|
- c1 = node1.getEnclosingCallable() and
- c2 = node2.getEnclosingCallable() and
+ c1 = getNodeEnclosingCallable(node1) and
+ c2 = getNodeEnclosingCallable(node2) and
c1 != c2
)
}
private predicate interestingCallableSrc(DataFlowCallable c, Configuration config) {
- exists(Node n | config.isSource(n) and c = n.getEnclosingCallable())
+ exists(Node n | config.isSource(n) and c = getNodeEnclosingCallable(n))
or
exists(DataFlowCallable mid |
interestingCallableSrc(mid, config) and callableStep(mid, c, config)
@@ -3417,7 +3433,7 @@ private module FlowExploration {
}
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
- exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
+ exists(Node n | config.isSink(n) and c = getNodeEnclosingCallable(n))
or
exists(DataFlowCallable mid |
interestingCallableSink(mid, config) and callableStep(c, mid, config)
@@ -3439,20 +3455,20 @@ private module FlowExploration {
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
callableStep(c1, c2, config) and
- ce1 = TCallable(c1, config) and
- ce2 = TCallable(c2, unbind(config))
+ ce1 = TCallable(c1, pragma[only_bind_into](config)) and
+ ce2 = TCallable(c2, pragma[only_bind_into](config))
)
or
exists(Node n, Configuration config |
ce1 = TCallableSrc() and
config.isSource(n) and
- ce2 = TCallable(n.getEnclosingCallable(), config)
+ ce2 = TCallable(getNodeEnclosingCallable(n), config)
)
or
exists(Node n, Configuration config |
ce2 = TCallableSink() and
config.isSink(n) and
- ce1 = TCallable(n.getEnclosingCallable(), config)
+ ce1 = TCallable(getNodeEnclosingCallable(n), config)
)
}
@@ -3583,7 +3599,7 @@ private module FlowExploration {
exists(config.explorationLimit())
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
- distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
+ distSrc(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
@@ -3600,7 +3616,7 @@ private module FlowExploration {
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not clearsContent(node, ap.getHead()) and
not fullBarrier(node, config) and
- distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
+ distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit()
)
}
@@ -3659,7 +3675,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSourceDistance() {
- result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
+ result = distSrc(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
}
/**
@@ -3667,7 +3683,7 @@ private module FlowExploration {
* of interprocedural steps.
*/
int getSinkDistance() {
- result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
+ result = distSink(getNodeEnclosingCallable(this.getNode()), this.getConfiguration())
}
private string ppAp() {
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 1d2e9052842..a51c20c2288 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
@@ -26,15 +26,243 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) {
tupleLimit = 1000
}
+/**
+ * Provides a simple data-flow analysis for resolving lambda calls. The analysis
+ * 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
+ * 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) {
+ p.isParameterOf(viableCallable(call), i)
+ }
+
+ private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) {
+ p.isParameterOf(viableCallableLambda(call, _), i)
+ }
+
+ private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ exists(int i |
+ viableParamNonLambda(call, i, p) and
+ arg.argumentOf(call, i)
+ )
+ }
+
+ private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ exists(int i |
+ viableParamLambda(call, i, p) and
+ arg.argumentOf(call, i)
+ )
+ }
+
+ private newtype TReturnPositionSimple =
+ TReturnPositionSimple0(DataFlowCallable c, ReturnKind kind) {
+ exists(ReturnNode ret |
+ c = getNodeEnclosingCallable(ret) and
+ kind = ret.getKind()
+ )
+ }
+
+ pragma[noinline]
+ private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret, ReturnKind kind) {
+ result = TReturnPositionSimple0(getNodeEnclosingCallable(ret), kind)
+ }
+
+ pragma[nomagic]
+ private TReturnPositionSimple viableReturnPosNonLambda(DataFlowCall call, ReturnKind kind) {
+ result = TReturnPositionSimple0(viableCallable(call), kind)
+ }
+
+ pragma[nomagic]
+ private TReturnPositionSimple viableReturnPosLambda(
+ DataFlowCall call, DataFlowCallOption lastCall, ReturnKind kind
+ ) {
+ result = TReturnPositionSimple0(viableCallableLambda(call, lastCall), kind)
+ }
+
+ private predicate viableReturnPosOutNonLambda(
+ DataFlowCall call, TReturnPositionSimple pos, OutNode out
+ ) {
+ exists(ReturnKind kind |
+ pos = viableReturnPosNonLambda(call, kind) and
+ out = getAnOutNode(call, kind)
+ )
+ }
+
+ private predicate viableReturnPosOutLambda(
+ DataFlowCall call, DataFlowCallOption lastCall, TReturnPositionSimple pos, OutNode out
+ ) {
+ exists(ReturnKind kind |
+ pos = viableReturnPosLambda(call, lastCall, kind) and
+ out = getAnOutNode(call, kind)
+ )
+ }
+
+ /**
+ * Holds if data can flow (inter-procedurally) from `node` (of type `t`) to
+ * the lambda call `lambdaCall`.
+ *
+ * The parameter `toReturn` indicates whether the path from `node` to
+ * `lambdaCall` goes through a return, and `toJump` whether the path goes
+ * through a jump step.
+ *
+ * The call context `lastCall` records the last call on the path from `node`
+ * to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing
+ * callable of `lambdaCall`.
+ */
+ pragma[nomagic]
+ predicate revLambdaFlow(
+ DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
+ 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))
+ else any()
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlow0(
+ DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn,
+ boolean toJump, DataFlowCallOption lastCall
+ ) {
+ lambdaCall(lambdaCall, kind, node) and
+ t = getNodeType(node) and
+ toReturn = false and
+ toJump = false and
+ lastCall = TDataFlowCallNone()
+ or
+ // local flow
+ exists(Node mid, DataFlowType t0 |
+ revLambdaFlow(lambdaCall, kind, mid, t0, toReturn, toJump, lastCall)
+ |
+ simpleLocalFlowStep(node, mid) and
+ t = t0
+ or
+ exists(boolean preservesValue |
+ additionalLambdaFlowStep(node, mid, preservesValue) and
+ getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid)
+ |
+ preservesValue = false and
+ t = getNodeType(node)
+ or
+ preservesValue = true and
+ t = t0
+ )
+ )
+ or
+ // jump step
+ exists(Node mid, DataFlowType t0 |
+ revLambdaFlow(lambdaCall, kind, mid, t0, _, _, _) and
+ toReturn = false and
+ toJump = true and
+ lastCall = TDataFlowCallNone()
+ |
+ jumpStep(node, mid) and
+ t = t0
+ or
+ exists(boolean preservesValue |
+ additionalLambdaFlowStep(node, mid, preservesValue) and
+ getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid)
+ |
+ preservesValue = false and
+ t = getNodeType(node)
+ or
+ preservesValue = true and
+ t = t0
+ )
+ )
+ or
+ // flow into a callable
+ exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call |
+ revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and
+ (
+ if lastCall0 = TDataFlowCallNone() and toJump = false
+ then lastCall = TDataFlowCallSome(call)
+ else lastCall = lastCall0
+ ) and
+ toReturn = false
+ |
+ viableParamArgNonLambda(call, p, node)
+ or
+ viableParamArgLambda(call, p, node) // non-linear recursion
+ )
+ or
+ // flow out of a callable
+ exists(TReturnPositionSimple pos |
+ revLambdaFlowOut(lambdaCall, kind, pos, t, toJump, lastCall) and
+ getReturnPositionSimple(node, node.(ReturnNode).getKind()) = pos and
+ toReturn = true
+ )
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlowOutLambdaCall(
+ DataFlowCall lambdaCall, LambdaCallKind kind, OutNode out, DataFlowType t, boolean toJump,
+ DataFlowCall call, DataFlowCallOption lastCall
+ ) {
+ revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and
+ exists(ReturnKindExt rk |
+ out = rk.getAnOutNode(call) and
+ lambdaCall(call, _, _)
+ )
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlowOut(
+ DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t,
+ boolean toJump, DataFlowCallOption lastCall
+ ) {
+ exists(DataFlowCall call, OutNode out |
+ revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and
+ viableReturnPosOutNonLambda(call, pos, out)
+ or
+ // non-linear recursion
+ revLambdaFlowOutLambdaCall(lambdaCall, kind, out, t, toJump, call, lastCall) and
+ viableReturnPosOutLambda(call, _, pos, out)
+ )
+ }
+
+ pragma[nomagic]
+ predicate revLambdaFlowIn(
+ DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump,
+ DataFlowCallOption lastCall
+ ) {
+ revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall)
+ }
+}
+
+private DataFlowCallable viableCallableExt(DataFlowCall call) {
+ result = viableCallable(call)
+ or
+ result = viableCallableLambda(call, _)
+}
+
cached
private module Cached {
+ /**
+ * Gets a viable target for the lambda call `call`.
+ *
+ * `lastCall` records the call required to reach `call` in order for the result
+ * to be a viable target, if any.
+ */
+ cached
+ DataFlowCallable viableCallableLambda(DataFlowCall call, DataFlowCallOption lastCall) {
+ exists(Node creation, LambdaCallKind kind |
+ LambdaFlow::revLambdaFlow(call, kind, creation, _, _, _, lastCall) and
+ lambdaCreation(creation, kind, result)
+ )
+ }
+
/**
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
* The instance parameter is considered to have index `-1`.
*/
pragma[nomagic]
private predicate viableParam(DataFlowCall call, int i, ParameterNode p) {
- p.isParameterOf(viableCallable(call), i)
+ p.isParameterOf(viableCallableExt(call), i)
}
/**
@@ -52,7 +280,7 @@ private module Cached {
pragma[nomagic]
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
- viableCallable(call) = result.getCallable() and
+ viableCallableExt(call) = result.getCallable() and
kind = result.getKind()
}
@@ -317,6 +545,35 @@ private module Cached {
cached
private module DispatchWithCallContext {
+ /**
+ * Holds if the set of viable implementations that can be called by `call`
+ * might be improved by knowing the call context.
+ */
+ pragma[nomagic]
+ private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) {
+ mayBenefitFromCallContext(call, callable)
+ or
+ callable = call.getEnclosingCallable() and
+ exists(viableCallableLambda(call, TDataFlowCallSome(_)))
+ }
+
+ /**
+ * Gets a viable dispatch target of `call` in the context `ctx`. This is
+ * restricted to those `call`s for which a context might make a difference.
+ */
+ pragma[nomagic]
+ private DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
+ result = viableImplInCallContext(call, ctx)
+ or
+ result = viableCallableLambda(call, TDataFlowCallSome(ctx))
+ or
+ exists(DataFlowCallable enclosing |
+ mayBenefitFromCallContextExt(call, enclosing) and
+ enclosing = viableCallableExt(ctx) and
+ result = viableCallableLambda(call, TDataFlowCallNone())
+ )
+ }
+
/**
* Holds if the call context `ctx` reduces the set of viable run-time
* dispatch targets of call `call` in `c`.
@@ -324,10 +581,10 @@ private module Cached {
cached
predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) {
exists(int tgts, int ctxtgts |
- mayBenefitFromCallContext(call, c) and
- c = viableCallable(ctx) and
- ctxtgts = count(viableImplInCallContext(call, ctx)) and
- tgts = strictcount(viableCallable(call)) and
+ mayBenefitFromCallContextExt(call, c) and
+ c = viableCallableExt(ctx) and
+ ctxtgts = count(viableImplInCallContextExt(call, ctx)) and
+ tgts = strictcount(viableCallableExt(call)) and
ctxtgts < tgts
)
}
@@ -339,7 +596,7 @@ private module Cached {
*/
cached
DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
- result = viableImplInCallContext(call, ctx) and
+ result = viableImplInCallContextExt(call, ctx) and
reducedViableImplInCallContext(call, _, ctx)
}
@@ -351,10 +608,10 @@ private module Cached {
cached
predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) {
exists(int tgts, int ctxtgts |
- mayBenefitFromCallContext(call, _) and
- c = viableCallable(call) and
- ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and
- tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and
+ 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
ctxtgts < tgts
)
}
@@ -367,7 +624,7 @@ private module Cached {
*/
cached
DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) {
- result = viableImplInCallContext(call, ctx) and
+ result = viableImplInCallContextExt(call, ctx) and
reducedViableImplInReturn(result, call)
}
}
@@ -415,6 +672,30 @@ private module Cached {
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
}
+ /**
+ * Holds if data can flow from `fromNode` to `toNode` because they are the post-update
+ * nodes of some function output and input respectively, where the output and input
+ * are aliases. A typical example is a function returning `this`, implementing a fluent
+ * interface.
+ */
+ cached
+ predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) {
+ exists(Node fromPre, Node toPre |
+ fromPre = fromNode.getPreUpdateNode() and
+ toPre = toNode.getPreUpdateNode()
+ |
+ exists(DataFlowCall c |
+ // 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)
+ )
+ or
+ argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre)
+ )
+ }
+
/**
* Holds if the call context `call` either improves virtual dispatch in
* `callable` or if it allows us to prune unreachable nodes in `callable`.
@@ -423,7 +704,7 @@ private module Cached {
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
reducedViableImplInCallContext(_, callable, call)
or
- exists(Node n | n.getEnclosingCallable() = callable | isUnreachableInCall(n, call))
+ exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCall(n, call))
}
cached
@@ -457,6 +738,11 @@ private module Cached {
TBooleanNone() or
TBooleanSome(boolean b) { b = true or b = false }
+ cached
+ newtype TDataFlowCallOption =
+ TDataFlowCallNone() or
+ TDataFlowCallSome(DataFlowCall call)
+
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
@@ -568,7 +854,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall {
override string toString() { result = "CcSomeCall" }
override predicate relevantFor(DataFlowCallable callable) {
- exists(ParameterNode p | p.getEnclosingCallable() = callable)
+ exists(ParameterNode p | getNodeEnclosingCallable(p) = callable)
}
override predicate matchesCall(DataFlowCall call) { any() }
@@ -613,7 +899,7 @@ class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall
}
private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) {
- exists(Node n | n.getEnclosingCallable() = callable and isUnreachableInCall(n, call))
+ exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCall(n, call))
}
/**
@@ -722,9 +1008,22 @@ class ReturnPosition extends TReturnPosition0 {
string toString() { result = "[" + kind + "] " + c }
}
+/**
+ * Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this
+ * predicate ensures that joins go from `n` to the result instead of the other
+ * way around.
+ */
+pragma[inline]
+DataFlowCallable getNodeEnclosingCallable(Node n) {
+ exists(Node n0 |
+ pragma[only_bind_into](n0) = n and
+ pragma[only_bind_into](result) = n0.getEnclosingCallable()
+ )
+}
+
pragma[noinline]
private DataFlowCallable returnNodeGetEnclosingCallable(ReturnNodeExt ret) {
- result = ret.getEnclosingCallable()
+ result = getNodeEnclosingCallable(ret)
}
pragma[noinline]
@@ -740,7 +1039,7 @@ ReturnPosition getReturnPosition(ReturnNodeExt ret) {
bindingset[cc, callable]
predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
- cc instanceof CallContextAny and callable = viableCallable(call)
+ cc instanceof CallContextAny and callable = viableCallableExt(call)
or
exists(DataFlowCallable c0, DataFlowCall call0 |
call0.getEnclosingCallable() = callable and
@@ -754,14 +1053,14 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
exists(DataFlowCall ctx | cc = TSpecificCall(ctx) |
if reducedViableImplInCallContext(call, _, ctx)
then result = prunedViableImplInCallContext(call, ctx)
- else result = viableCallable(call)
+ else result = viableCallableExt(call)
)
or
- result = viableCallable(call) and cc instanceof CallContextSomeCall
+ result = viableCallableExt(call) and cc instanceof CallContextSomeCall
or
- result = viableCallable(call) and cc instanceof CallContextAny
+ result = viableCallableExt(call) and cc instanceof CallContextAny
or
- result = viableCallable(call) and cc instanceof CallContextReturn
+ result = viableCallableExt(call) and cc instanceof CallContextReturn
}
predicate read = readStep/3;
@@ -775,6 +1074,19 @@ class BooleanOption extends TBooleanOption {
}
}
+/** An optional `DataFlowCall`. */
+class DataFlowCallOption extends TDataFlowCallOption {
+ string toString() {
+ this = TDataFlowCallNone() and
+ result = "(none)"
+ or
+ exists(DataFlowCall call |
+ this = TDataFlowCallSome(call) and
+ result = call.toString()
+ )
+ }
+}
+
/** Content tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
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 762ce8d47b4..3b94e574de0 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
@@ -31,9 +31,19 @@ private class PrimaryArgumentNode extends ArgumentNode {
override predicate argumentOf(DataFlowCall call, int pos) { op = call.getArgumentOperand(pos) }
override string toString() {
- result = "Argument " + op.(PositionalArgumentOperand).getIndex()
+ exists(Expr unconverted |
+ unconverted = op.getDef().getUnconvertedResultExpression() and
+ result = unconverted.toString()
+ )
or
- op instanceof ThisArgumentOperand and result = "This argument"
+ // Certain instructions don't map to an unconverted result expression. For these cases
+ // we fall back to a simpler naming scheme. This can happen in IR-generated constructors.
+ not exists(op.getDef().getUnconvertedResultExpression()) and
+ (
+ result = "Argument " + op.(PositionalArgumentOperand).getIndex()
+ or
+ op instanceof ThisArgumentOperand and result = "Argument this"
+ )
}
}
@@ -52,7 +62,18 @@ private class SideEffectArgumentNode extends ArgumentNode {
pos = getArgumentPosOfSideEffect(read.getIndex())
}
- override string toString() { result = "Argument " + read.getIndex() + " indirection" }
+ override string toString() {
+ result = read.getArgumentDef().getUnconvertedResultExpression().toString() + " indirection"
+ or
+ // Some instructions don't map to an unconverted result expression. For these cases
+ // we fall back to a simpler naming scheme. This can happen in IR-generated constructors.
+ not exists(read.getArgumentDef().getUnconvertedResultExpression()) and
+ (
+ if read.getIndex() = -1
+ then result = "Argument this indirection"
+ else result = "Argument " + read.getIndex() + " indirection"
+ )
+ }
}
private newtype TReturnKind =
@@ -526,4 +547,15 @@ predicate isImmutableOrUnobservable(Node n) {
}
/** Holds if `n` should be hidden from path explanations. */
-predicate nodeIsHidden(Node n) { n instanceof OperandNode }
+predicate nodeIsHidden(Node n) { n instanceof OperandNode and not n instanceof ArgumentNode }
+
+class LambdaCallKind = Unit;
+
+/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */
+predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { none() }
+
+/** 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. */
+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 cbc44ac920d..76ca7b215dc 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
@@ -693,7 +693,11 @@ private predicate simpleInstructionLocalFlowStep(Operand opFrom, Instruction iTo
exists(ChiInstruction chi | chi = iTo |
opFrom.getAnyDef() instanceof WriteSideEffectInstruction and
chi.getPartialOperand() = opFrom and
- not chi.isResultConflated()
+ not chi.isResultConflated() and
+ // In a call such as `set_value(&x->val);` we don't want the memory representing `x` to receive
+ // dataflow by a simple step. Instead, this is handled by field flow. If we add a simple step here
+ // we can get field-to-object flow.
+ not chi.isPartialUpdate()
)
or
// Flow through modeled functions
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 ff28ed2c1d4..af6f3d81982 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
@@ -2055,6 +2055,13 @@ class ChiInstruction extends Instruction {
final predicate getUpdatedInterval(int startBit, int endBit) {
Construction::getIntervalUpdatedByChi(this, startBit, endBit)
}
+
+ /**
+ * Holds if the `ChiPartialOperand` totally, but not exactly, overlaps with the `ChiTotalOperand`.
+ * This means that the `ChiPartialOperand` will not override the entire memory associated with the
+ * `ChiTotalOperand`.
+ */
+ final predicate isPartialUpdate() { Construction::chiOnlyPartiallyUpdatesLocation(this) }
}
/**
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 a12e35d471b..a2ce0662dc2 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
@@ -10,79 +10,32 @@ private import Imports::MemoryAccessKind
private import Imports::IRType
private import Imports::Overlap
private import Imports::OperandTag
-
-cached
-private newtype TOperand =
- TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
- defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
- not Construction::isInCycle(useInstr) and
- strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1
- } or
- TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
- useInstr.getOpcode().hasOperand(tag)
- } or
- TPhiOperand(
- PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
- ) {
- defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
- }
+private import Imports::TOperand
+private import internal.OperandInternal
/**
- * Base class for all register operands. This is a placeholder for the IPA union type that we will
- * eventually use for this purpose.
+ * An operand of an `Instruction` in this stage of the IR. Implemented as a union of the branches
+ * of `TOperand` that are used in this stage.
*/
-private class RegisterOperandBase extends TRegisterOperand {
- /** Gets a textual representation of this element. */
- abstract string toString();
-}
-
-/**
- * Returns the register operand with the specified parameters.
- */
-private RegisterOperandBase registerOperand(
- Instruction useInstr, RegisterOperandTag tag, Instruction defInstr
-) {
- result = TRegisterOperand(useInstr, tag, defInstr)
-}
-
-/**
- * Base class for all non-Phi memory operands. This is a placeholder for the IPA union type that we
- * will eventually use for this purpose.
- */
-private class NonPhiMemoryOperandBase extends TNonPhiMemoryOperand {
- /** Gets a textual representation of this element. */
- abstract string toString();
-}
-
-/**
- * Returns the non-Phi memory operand with the specified parameters.
- */
-private NonPhiMemoryOperandBase nonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
- result = TNonPhiMemoryOperand(useInstr, tag)
-}
-
-/**
- * Base class for all Phi operands. This is a placeholder for the IPA union type that we will
- * eventually use for this purpose.
- */
-private class PhiOperandBase extends TPhiOperand {
- abstract string toString();
-}
-
-/**
- * Returns the Phi operand with the specified parameters.
- */
-private PhiOperandBase phiOperand(
- Instruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
-) {
- result = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
-}
+private class TStageOperand =
+ TRegisterOperand or TNonSSAMemoryOperand or TPhiOperand or TChiOperand;
/**
* An operand of an `Instruction`. The operand represents a use of the result of one instruction
* (the defining instruction) in another instruction (the use instruction)
*/
-class Operand extends TOperand {
+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, IRBlock predecessorBlock |
+ this = phiOperand(use, def, predecessorBlock, _)
+ ) or
+ exists(Instruction use | this = chiOperand(use, _))
+ }
+
/** Gets a textual representation of this element. */
string toString() { result = "Operand" }
@@ -238,9 +191,11 @@ class Operand extends TOperand {
* An operand that consumes a memory result (e.g. the `LoadOperand` on a `Load` instruction).
*/
class MemoryOperand extends Operand {
+ cached
MemoryOperand() {
- this instanceof NonPhiMemoryOperandBase or
- this instanceof PhiOperandBase
+ this instanceof TNonSSAMemoryOperand or
+ this instanceof TPhiOperand or
+ this instanceof TChiOperand
}
/**
@@ -278,7 +233,8 @@ class NonPhiOperand extends Operand {
NonPhiOperand() {
this = registerOperand(useInstr, tag, _) or
- this = nonPhiMemoryOperand(useInstr, tag)
+ this = nonSSAMemoryOperand(useInstr, tag) or
+ this = chiOperand(useInstr, tag)
}
final override Instruction getUse() { result = useInstr }
@@ -298,10 +254,11 @@ class NonPhiOperand extends Operand {
/**
* An operand that consumes a register (non-memory) result.
*/
-class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
+class RegisterOperand extends NonPhiOperand, TRegisterOperand {
override RegisterOperandTag tag;
Instruction defInstr;
+ cached
RegisterOperand() { this = registerOperand(useInstr, tag, defInstr) }
final override string toString() { result = tag.toString() }
@@ -317,10 +274,15 @@ class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
/**
* A memory operand other than the operand of a `Phi` instruction.
*/
-class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase {
+class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOperand {
override MemoryOperandTag tag;
- NonPhiMemoryOperand() { this = nonPhiMemoryOperand(useInstr, tag) }
+ cached
+ NonPhiMemoryOperand() {
+ this = nonSSAMemoryOperand(useInstr, tag)
+ or
+ this = chiOperand(useInstr, tag)
+ }
final override string toString() { result = tag.toString() }
@@ -462,12 +424,13 @@ class SideEffectOperand extends TypedOperand {
/**
* An operand of a `PhiInstruction`.
*/
-class PhiInputOperand extends MemoryOperand, PhiOperandBase {
+class PhiInputOperand extends MemoryOperand, TPhiOperand {
PhiInstruction useInstr;
Instruction defInstr;
IRBlock predecessorBlock;
Overlap overlap;
+ cached
PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
override string toString() { result = "Phi" }
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 14774995e18..fdabee2affe 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
@@ -629,7 +629,11 @@ MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
}
/** Gets the start bit offset of a `MemoryLocation`, if any. */
-int getStartBitOffset(VariableMemoryLocation location) { result = location.getStartBitOffset() }
+int getStartBitOffset(VariableMemoryLocation location) {
+ result = location.getStartBitOffset() and Ints::hasValue(result)
+}
/** Gets the end bit offset of a `MemoryLocation`, if any. */
-int getEndBitOffset(VariableMemoryLocation location) { result = location.getEndBitOffset() }
+int getEndBitOffset(VariableMemoryLocation location) {
+ result = location.getEndBitOffset() and Ints::hasValue(result)
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/OperandImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/OperandImports.qll
index 3c781579cee..d0e013d1fba 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/OperandImports.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/OperandImports.qll
@@ -2,3 +2,4 @@ import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.internal.Overlap as Overlap
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
+import semmle.code.cpp.ir.implementation.internal.TOperand as TOperand
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/OperandInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/OperandInternal.qll
new file mode 100644
index 00000000000..b47c20e97ef
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/OperandInternal.qll
@@ -0,0 +1,2 @@
+private import semmle.code.cpp.ir.implementation.internal.TOperand
+import AliasedSSAOperands
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 8e904ee6bc4..340f524fce8 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
@@ -6,6 +6,7 @@ private import Imports::Overlap
private import Imports::TInstruction
private import Imports::RawIR as RawIR
private import SSAInstructions
+private import SSAOperands
private import NewIR
private class OldBlock = Reachability::ReachableBlock;
@@ -177,6 +178,22 @@ private module Cached {
)
}
+ /**
+ * Holds if the `ChiPartialOperand` only partially overlaps with the `ChiTotalOperand`.
+ * This means that the `ChiPartialOperand` will not override the entire memory associated
+ * with the `ChiTotalOperand`.
+ */
+ cached
+ predicate chiOnlyPartiallyUpdatesLocation(ChiInstruction chi) {
+ exists(Alias::MemoryLocation location, OldInstruction oldInstruction |
+ oldInstruction = getOldInstruction(chi.getPartial()) and
+ location = Alias::getResultMemoryLocation(oldInstruction)
+ |
+ Alias::getStartBitOffset(location) != 0 or
+ Alias::getEndBitOffset(location) != 8 * location.getType().getByteSize()
+ )
+ }
+
/**
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
* through a phi instruction and therefore should be impossible.
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll
index f347df86ba1..219180d9f4d 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll
@@ -3,3 +3,4 @@ import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
import semmle.code.cpp.ir.internal.Overlap as Overlap
import semmle.code.cpp.ir.implementation.internal.TInstruction as TInstruction
import semmle.code.cpp.ir.implementation.raw.IR as RawIR
+import semmle.code.cpp.ir.implementation.internal.TOperand as TOperand
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll
index bb068bdd489..a1ce2629cc2 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll
@@ -5,3 +5,4 @@ import semmle.code.cpp.ir.implementation.aliased_ssa.IR as NewIR
import semmle.code.cpp.ir.implementation.internal.TInstruction::AliasedSSAInstructions as SSAInstructions
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import AliasedSSA as Alias
+import semmle.code.cpp.ir.implementation.internal.TOperand::AliasedSSAOperands as SSAOperands
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
new file mode 100644
index 00000000000..1e132463cdd
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll
@@ -0,0 +1,180 @@
+private import TInstruction
+private import OperandTag
+private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as RawConstruction
+private import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction as UnaliasedConstruction
+private import semmle.code.cpp.ir.implementation.aliased_ssa.internal.SSAConstruction as AliasedConstruction
+private import semmle.code.cpp.ir.implementation.raw.IR as Raw
+private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as Unaliased
+private import semmle.code.cpp.ir.implementation.aliased_ssa.IR as Aliased
+private import semmle.code.cpp.ir.internal.Overlap
+
+/**
+ * Provides the newtype used to represent operands across all phases of the IR.
+ */
+private module Internal {
+ /**
+ * An IR operand. `TOperand` is shared across all phases of the IR. There are branches of this
+ * type for operands created directly from the AST (`TRegisterOperand` and `TNonSSAMemoryOperand`),
+ * for operands computed by each stage of SSA construction (`T*PhiOperand` and
+ * `TAliasedChiOperand`), and a placehold branch for operands that do not exist in a given
+ * stage of IR construction (`TNoOperand`).
+ */
+ cached
+ newtype TOperand =
+ // RAW
+ TRegisterOperand(TRawInstruction useInstr, RegisterOperandTag tag, TRawInstruction defInstr) {
+ defInstr = RawConstruction::getRegisterOperandDefinition(useInstr, tag) and
+ not RawConstruction::isInCycle(useInstr) and
+ strictcount(RawConstruction::getRegisterOperandDefinition(useInstr, tag)) = 1
+ } or
+ // Placeholder for Phi and Chi operands in stages that don't have the corresponding instructions
+ TNoOperand() { none() } or
+ // Can be "removed" later when there's unreachable code
+ // These operands can be reused across all three stages. They just get different defs.
+ TNonSSAMemoryOperand(Raw::Instruction useInstr, MemoryOperandTag tag) {
+ // Has no definition in raw but will get definitions later
+ useInstr.getOpcode().hasOperand(tag)
+ } or
+ TUnaliasedPhiOperand(
+ Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr,
+ Unaliased::IRBlock predecessorBlock, Overlap overlap
+ ) {
+ defInstr = UnaliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
+ } or
+ //// ALIASED
+ ////
+ // Until we share SSA, these will be all the phis there are. With SSA
+ // sharing, these will add to the ones that are already there.
+ // If we share SSA, be careful with the case where we remove all possible
+ // indirect writes to a variable because they're dead code. In that case it's
+ // 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
+ ) {
+ defInstr = AliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
+ } or
+ TAliasedChiOperand(TAliasedSSAChiInstruction useInstr, ChiOperandTag tag) { any() }
+}
+
+/**
+ * Reexports some branches from `TOperand` so they can be used in stage modules without importing
+ * `TOperand` itself.
+ */
+private module Shared {
+ class TRegisterOperand = Internal::TRegisterOperand;
+
+ /**
+ * Returns the register operand with the specified parameters.
+ */
+ TRegisterOperand registerOperand(
+ TRawInstruction useInstr, RegisterOperandTag tag, TRawInstruction defInstr
+ ) {
+ result = Internal::TRegisterOperand(useInstr, tag, defInstr)
+ }
+
+ class TNonSSAMemoryOperand = Internal::TNonSSAMemoryOperand;
+
+ /**
+ * Returns the non-Phi memory operand with the specified parameters.
+ */
+ TNonSSAMemoryOperand nonSSAMemoryOperand(TRawInstruction useInstr, MemoryOperandTag tag) {
+ result = Internal::TNonSSAMemoryOperand(useInstr, tag)
+ }
+}
+
+/**
+ * Provides wrappers for the constructors of each branch of `TOperand` that is used by the
+ * raw IR stage.
+ * These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
+ * a class alias.
+ */
+module RawOperands {
+ import Shared
+
+ class TPhiOperand = Internal::TNoOperand;
+
+ class TChiOperand = Internal::TNoOperand;
+
+ class TNonPhiMemoryOperand = TNonSSAMemoryOperand or TChiOperand;
+
+ /**
+ * Returns the Phi operand with the specified parameters.
+ */
+ TPhiOperand phiOperand(
+ Raw::PhiInstruction useInstr, Raw::Instruction defInstr, Raw::IRBlock predecessorBlock,
+ Overlap overlap
+ ) {
+ none()
+ }
+
+ /**
+ * Returns the Chi operand with the specified parameters.
+ */
+ TChiOperand chiOperand(Raw::Instruction useInstr, ChiOperandTag tag) { none() }
+}
+
+/**
+ * Provides wrappers for the constructors of each branch of `TOperand` that is used by the
+ * unaliased SSA stage.
+ * These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
+ * a class alias.
+ */
+module UnaliasedSSAOperands {
+ import Shared
+
+ class TPhiOperand = Internal::TUnaliasedPhiOperand;
+
+ class TChiOperand = Internal::TNoOperand;
+
+ class TNonPhiMemoryOperand = TNonSSAMemoryOperand or TChiOperand;
+
+ /**
+ * Returns the Phi operand with the specified parameters.
+ */
+ TPhiOperand phiOperand(
+ Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr,
+ Unaliased::IRBlock predecessorBlock, Overlap overlap
+ ) {
+ result = Internal::TUnaliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
+ }
+
+ /**
+ * Returns the Chi operand with the specified parameters.
+ */
+ TChiOperand chiOperand(Unaliased::Instruction useInstr, ChiOperandTag tag) { none() }
+}
+
+/**
+ * Provides wrappers for the constructors of each branch of `TOperand` that is used by the
+ * asliased SSA stage.
+ * These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
+ * a class alias.
+ */
+module AliasedSSAOperands {
+ import Shared
+
+ class TPhiOperand = Internal::TAliasedPhiOperand;
+
+ class TChiOperand = Internal::TAliasedChiOperand;
+
+ class TNonPhiMemoryOperand = TNonSSAMemoryOperand or TChiOperand;
+
+ /**
+ * Returns the Phi operand with the specified parameters.
+ */
+ TPhiOperand phiOperand(
+ TAliasedSSAPhiInstruction useInstr, Aliased::Instruction defInstr,
+ Aliased::IRBlock predecessorBlock, Overlap overlap
+ ) {
+ result = Internal::TAliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
+ }
+
+ /**
+ * Returns the Chi operand with the specified parameters.
+ */
+ TChiOperand chiOperand(TAliasedSSAChiInstruction useInstr, ChiOperandTag tag) {
+ result = Internal::TAliasedChiOperand(useInstr, tag)
+ }
+}
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 ff28ed2c1d4..af6f3d81982 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
@@ -2055,6 +2055,13 @@ class ChiInstruction extends Instruction {
final predicate getUpdatedInterval(int startBit, int endBit) {
Construction::getIntervalUpdatedByChi(this, startBit, endBit)
}
+
+ /**
+ * Holds if the `ChiPartialOperand` totally, but not exactly, overlaps with the `ChiTotalOperand`.
+ * This means that the `ChiPartialOperand` will not override the entire memory associated with the
+ * `ChiTotalOperand`.
+ */
+ final predicate isPartialUpdate() { Construction::chiOnlyPartiallyUpdatesLocation(this) }
}
/**
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 a12e35d471b..a2ce0662dc2 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
@@ -10,79 +10,32 @@ private import Imports::MemoryAccessKind
private import Imports::IRType
private import Imports::Overlap
private import Imports::OperandTag
-
-cached
-private newtype TOperand =
- TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
- defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
- not Construction::isInCycle(useInstr) and
- strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1
- } or
- TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
- useInstr.getOpcode().hasOperand(tag)
- } or
- TPhiOperand(
- PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
- ) {
- defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
- }
+private import Imports::TOperand
+private import internal.OperandInternal
/**
- * Base class for all register operands. This is a placeholder for the IPA union type that we will
- * eventually use for this purpose.
+ * An operand of an `Instruction` in this stage of the IR. Implemented as a union of the branches
+ * of `TOperand` that are used in this stage.
*/
-private class RegisterOperandBase extends TRegisterOperand {
- /** Gets a textual representation of this element. */
- abstract string toString();
-}
-
-/**
- * Returns the register operand with the specified parameters.
- */
-private RegisterOperandBase registerOperand(
- Instruction useInstr, RegisterOperandTag tag, Instruction defInstr
-) {
- result = TRegisterOperand(useInstr, tag, defInstr)
-}
-
-/**
- * Base class for all non-Phi memory operands. This is a placeholder for the IPA union type that we
- * will eventually use for this purpose.
- */
-private class NonPhiMemoryOperandBase extends TNonPhiMemoryOperand {
- /** Gets a textual representation of this element. */
- abstract string toString();
-}
-
-/**
- * Returns the non-Phi memory operand with the specified parameters.
- */
-private NonPhiMemoryOperandBase nonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
- result = TNonPhiMemoryOperand(useInstr, tag)
-}
-
-/**
- * Base class for all Phi operands. This is a placeholder for the IPA union type that we will
- * eventually use for this purpose.
- */
-private class PhiOperandBase extends TPhiOperand {
- abstract string toString();
-}
-
-/**
- * Returns the Phi operand with the specified parameters.
- */
-private PhiOperandBase phiOperand(
- Instruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
-) {
- result = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
-}
+private class TStageOperand =
+ TRegisterOperand or TNonSSAMemoryOperand or TPhiOperand or TChiOperand;
/**
* An operand of an `Instruction`. The operand represents a use of the result of one instruction
* (the defining instruction) in another instruction (the use instruction)
*/
-class Operand extends TOperand {
+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, IRBlock predecessorBlock |
+ this = phiOperand(use, def, predecessorBlock, _)
+ ) or
+ exists(Instruction use | this = chiOperand(use, _))
+ }
+
/** Gets a textual representation of this element. */
string toString() { result = "Operand" }
@@ -238,9 +191,11 @@ class Operand extends TOperand {
* An operand that consumes a memory result (e.g. the `LoadOperand` on a `Load` instruction).
*/
class MemoryOperand extends Operand {
+ cached
MemoryOperand() {
- this instanceof NonPhiMemoryOperandBase or
- this instanceof PhiOperandBase
+ this instanceof TNonSSAMemoryOperand or
+ this instanceof TPhiOperand or
+ this instanceof TChiOperand
}
/**
@@ -278,7 +233,8 @@ class NonPhiOperand extends Operand {
NonPhiOperand() {
this = registerOperand(useInstr, tag, _) or
- this = nonPhiMemoryOperand(useInstr, tag)
+ this = nonSSAMemoryOperand(useInstr, tag) or
+ this = chiOperand(useInstr, tag)
}
final override Instruction getUse() { result = useInstr }
@@ -298,10 +254,11 @@ class NonPhiOperand extends Operand {
/**
* An operand that consumes a register (non-memory) result.
*/
-class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
+class RegisterOperand extends NonPhiOperand, TRegisterOperand {
override RegisterOperandTag tag;
Instruction defInstr;
+ cached
RegisterOperand() { this = registerOperand(useInstr, tag, defInstr) }
final override string toString() { result = tag.toString() }
@@ -317,10 +274,15 @@ class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
/**
* A memory operand other than the operand of a `Phi` instruction.
*/
-class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase {
+class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOperand {
override MemoryOperandTag tag;
- NonPhiMemoryOperand() { this = nonPhiMemoryOperand(useInstr, tag) }
+ cached
+ NonPhiMemoryOperand() {
+ this = nonSSAMemoryOperand(useInstr, tag)
+ or
+ this = chiOperand(useInstr, tag)
+ }
final override string toString() { result = tag.toString() }
@@ -462,12 +424,13 @@ class SideEffectOperand extends TypedOperand {
/**
* An operand of a `PhiInstruction`.
*/
-class PhiInputOperand extends MemoryOperand, PhiOperandBase {
+class PhiInputOperand extends MemoryOperand, TPhiOperand {
PhiInstruction useInstr;
Instruction defInstr;
IRBlock predecessorBlock;
Overlap overlap;
+ cached
PhiInputOperand() { this = phiOperand(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 c3f2d0c052a..b0faec547f3 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
@@ -194,6 +194,8 @@ predicate getIntervalUpdatedByChi(ChiInstruction chi, int startBit, int endBit)
*/
predicate getUsedInterval(Operand operand, int startBit, int endBit) { none() }
+predicate chiOnlyPartiallyUpdatesLocation(ChiInstruction chi) { none() }
+
/** Gets a non-phi instruction that defines an operand of `instr`. */
private Instruction getNonPhiOperandDef(Instruction instr) {
result = getRegisterOperandDefinition(instr, _)
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/OperandImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/OperandImports.qll
index 3c781579cee..d0e013d1fba 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/OperandImports.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/OperandImports.qll
@@ -2,3 +2,4 @@ import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.internal.Overlap as Overlap
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
+import semmle.code.cpp.ir.implementation.internal.TOperand as TOperand
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/OperandInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/OperandInternal.qll
new file mode 100644
index 00000000000..194e21e0d93
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/OperandInternal.qll
@@ -0,0 +1,2 @@
+private import semmle.code.cpp.ir.implementation.internal.TOperand
+import RawOperands
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 56c48b100f8..1de9936ae1f 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
@@ -658,14 +658,18 @@ newtype TTranslatedElement =
t instanceof ReferenceType
) and
(
- isWrite = true or
+ isWrite = true and
+ not call.getTarget().getParameter(n).getType().isDeeplyConstBelow()
+ or
isWrite = false
)
or
not call.getTarget() instanceof SideEffectFunction and
n = -1 and
(
- isWrite = true or
+ isWrite = true and
+ not call.getTarget() instanceof ConstMemberFunction
+ or
isWrite = false
)
) and
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 ff28ed2c1d4..af6f3d81982 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
@@ -2055,6 +2055,13 @@ class ChiInstruction extends Instruction {
final predicate getUpdatedInterval(int startBit, int endBit) {
Construction::getIntervalUpdatedByChi(this, startBit, endBit)
}
+
+ /**
+ * Holds if the `ChiPartialOperand` totally, but not exactly, overlaps with the `ChiTotalOperand`.
+ * This means that the `ChiPartialOperand` will not override the entire memory associated with the
+ * `ChiTotalOperand`.
+ */
+ final predicate isPartialUpdate() { Construction::chiOnlyPartiallyUpdatesLocation(this) }
}
/**
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 a12e35d471b..a2ce0662dc2 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
@@ -10,79 +10,32 @@ private import Imports::MemoryAccessKind
private import Imports::IRType
private import Imports::Overlap
private import Imports::OperandTag
-
-cached
-private newtype TOperand =
- TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
- defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
- not Construction::isInCycle(useInstr) and
- strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1
- } or
- TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
- useInstr.getOpcode().hasOperand(tag)
- } or
- TPhiOperand(
- PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
- ) {
- defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
- }
+private import Imports::TOperand
+private import internal.OperandInternal
/**
- * Base class for all register operands. This is a placeholder for the IPA union type that we will
- * eventually use for this purpose.
+ * An operand of an `Instruction` in this stage of the IR. Implemented as a union of the branches
+ * of `TOperand` that are used in this stage.
*/
-private class RegisterOperandBase extends TRegisterOperand {
- /** Gets a textual representation of this element. */
- abstract string toString();
-}
-
-/**
- * Returns the register operand with the specified parameters.
- */
-private RegisterOperandBase registerOperand(
- Instruction useInstr, RegisterOperandTag tag, Instruction defInstr
-) {
- result = TRegisterOperand(useInstr, tag, defInstr)
-}
-
-/**
- * Base class for all non-Phi memory operands. This is a placeholder for the IPA union type that we
- * will eventually use for this purpose.
- */
-private class NonPhiMemoryOperandBase extends TNonPhiMemoryOperand {
- /** Gets a textual representation of this element. */
- abstract string toString();
-}
-
-/**
- * Returns the non-Phi memory operand with the specified parameters.
- */
-private NonPhiMemoryOperandBase nonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
- result = TNonPhiMemoryOperand(useInstr, tag)
-}
-
-/**
- * Base class for all Phi operands. This is a placeholder for the IPA union type that we will
- * eventually use for this purpose.
- */
-private class PhiOperandBase extends TPhiOperand {
- abstract string toString();
-}
-
-/**
- * Returns the Phi operand with the specified parameters.
- */
-private PhiOperandBase phiOperand(
- Instruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
-) {
- result = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
-}
+private class TStageOperand =
+ TRegisterOperand or TNonSSAMemoryOperand or TPhiOperand or TChiOperand;
/**
* An operand of an `Instruction`. The operand represents a use of the result of one instruction
* (the defining instruction) in another instruction (the use instruction)
*/
-class Operand extends TOperand {
+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, IRBlock predecessorBlock |
+ this = phiOperand(use, def, predecessorBlock, _)
+ ) or
+ exists(Instruction use | this = chiOperand(use, _))
+ }
+
/** Gets a textual representation of this element. */
string toString() { result = "Operand" }
@@ -238,9 +191,11 @@ class Operand extends TOperand {
* An operand that consumes a memory result (e.g. the `LoadOperand` on a `Load` instruction).
*/
class MemoryOperand extends Operand {
+ cached
MemoryOperand() {
- this instanceof NonPhiMemoryOperandBase or
- this instanceof PhiOperandBase
+ this instanceof TNonSSAMemoryOperand or
+ this instanceof TPhiOperand or
+ this instanceof TChiOperand
}
/**
@@ -278,7 +233,8 @@ class NonPhiOperand extends Operand {
NonPhiOperand() {
this = registerOperand(useInstr, tag, _) or
- this = nonPhiMemoryOperand(useInstr, tag)
+ this = nonSSAMemoryOperand(useInstr, tag) or
+ this = chiOperand(useInstr, tag)
}
final override Instruction getUse() { result = useInstr }
@@ -298,10 +254,11 @@ class NonPhiOperand extends Operand {
/**
* An operand that consumes a register (non-memory) result.
*/
-class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
+class RegisterOperand extends NonPhiOperand, TRegisterOperand {
override RegisterOperandTag tag;
Instruction defInstr;
+ cached
RegisterOperand() { this = registerOperand(useInstr, tag, defInstr) }
final override string toString() { result = tag.toString() }
@@ -317,10 +274,15 @@ class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
/**
* A memory operand other than the operand of a `Phi` instruction.
*/
-class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase {
+class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOperand {
override MemoryOperandTag tag;
- NonPhiMemoryOperand() { this = nonPhiMemoryOperand(useInstr, tag) }
+ cached
+ NonPhiMemoryOperand() {
+ this = nonSSAMemoryOperand(useInstr, tag)
+ or
+ this = chiOperand(useInstr, tag)
+ }
final override string toString() { result = tag.toString() }
@@ -462,12 +424,13 @@ class SideEffectOperand extends TypedOperand {
/**
* An operand of a `PhiInstruction`.
*/
-class PhiInputOperand extends MemoryOperand, PhiOperandBase {
+class PhiInputOperand extends MemoryOperand, TPhiOperand {
PhiInstruction useInstr;
Instruction defInstr;
IRBlock predecessorBlock;
Overlap overlap;
+ cached
PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
override string toString() { result = "Phi" }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/OperandImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/OperandImports.qll
index 3c781579cee..d0e013d1fba 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/OperandImports.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/OperandImports.qll
@@ -2,3 +2,4 @@ import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.internal.Overlap as Overlap
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
+import semmle.code.cpp.ir.implementation.internal.TOperand as TOperand
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/OperandInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/OperandInternal.qll
new file mode 100644
index 00000000000..80e06a381a1
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/OperandInternal.qll
@@ -0,0 +1,2 @@
+private import semmle.code.cpp.ir.implementation.internal.TOperand
+import UnaliasedSSAOperands
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 8e904ee6bc4..340f524fce8 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
@@ -6,6 +6,7 @@ private import Imports::Overlap
private import Imports::TInstruction
private import Imports::RawIR as RawIR
private import SSAInstructions
+private import SSAOperands
private import NewIR
private class OldBlock = Reachability::ReachableBlock;
@@ -177,6 +178,22 @@ private module Cached {
)
}
+ /**
+ * Holds if the `ChiPartialOperand` only partially overlaps with the `ChiTotalOperand`.
+ * This means that the `ChiPartialOperand` will not override the entire memory associated
+ * with the `ChiTotalOperand`.
+ */
+ cached
+ predicate chiOnlyPartiallyUpdatesLocation(ChiInstruction chi) {
+ exists(Alias::MemoryLocation location, OldInstruction oldInstruction |
+ oldInstruction = getOldInstruction(chi.getPartial()) and
+ location = Alias::getResultMemoryLocation(oldInstruction)
+ |
+ Alias::getStartBitOffset(location) != 0 or
+ Alias::getEndBitOffset(location) != 8 * location.getType().getByteSize()
+ )
+ }
+
/**
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
* through a phi instruction and therefore should be impossible.
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll
index f347df86ba1..219180d9f4d 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll
@@ -3,3 +3,4 @@ import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
import semmle.code.cpp.ir.internal.Overlap as Overlap
import semmle.code.cpp.ir.implementation.internal.TInstruction as TInstruction
import semmle.code.cpp.ir.implementation.raw.IR as RawIR
+import semmle.code.cpp.ir.implementation.internal.TOperand as TOperand
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll
index 73b08d1286b..70d44e03267 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll
@@ -6,3 +6,4 @@ import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as RawStage
import semmle.code.cpp.ir.implementation.internal.TInstruction::UnaliasedSSAInstructions as SSAInstructions
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import SimpleSSA as Alias
+import semmle.code.cpp.ir.implementation.internal.TOperand::UnaliasedSSAOperands as SSAOperands
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Accept.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Accept.qll
index 150f481de28..cea4598acf3 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Accept.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Accept.qll
@@ -15,9 +15,7 @@ import semmle.code.cpp.models.interfaces.SideEffect
private class Accept extends ArrayFunction, AliasFunction, TaintFunction, SideEffectFunction {
Accept() { this.hasGlobalName(["accept", "accept4", "WSAAccept"]) }
- override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
- bufParam = 1 and countParam = 2
- }
+ override predicate hasArrayWithUnknownSize(int bufParam) { bufParam = 1 }
override predicate hasArrayInput(int bufParam) { bufParam = 1 }
@@ -46,8 +44,8 @@ private class Accept extends ArrayFunction, AliasFunction, TaintFunction, SideEf
i = 1 and buffer = false
}
- override ParameterIndex getParameterSizeIndex(ParameterIndex i) { i = 1 and result = 2 }
-
+ // NOTE: The size parameter is a pointer to the size. So we can't implement `getParameterSizeIndex` for
+ // this model.
// NOTE: We implement thse two predicates as none because we can't model the low-level changes made to
// the structure pointed to by the file-descriptor argument.
override predicate hasOnlySpecificReadSideEffects() { none() }
diff --git a/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll b/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll
index 35798415078..e086b810478 100644
--- a/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll
+++ b/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll
@@ -1,6 +1,7 @@
/**
* Provides a library for writing QL tests whose success or failure is based on expected results
- * embedded in the test source code as comments, rather than a `.expected` file.
+ * embedded in the test source code as comments, rather than the contents of an `.expected` file
+ * (in that the `.expected` file should always be empty).
*
* To add this framework to a new language:
* - Add a file `InlineExpectationsTestPrivate.qll` that defines a `LineComment` class. This class
@@ -233,7 +234,9 @@ private string expectationPattern() {
exists(string tag, string tags, string value |
tag = "[A-Za-z-_][A-Za-z-_0-9]*" and
tags = "((?:" + tag + ")(?:\\s*,\\s*" + tag + ")*)" and
- value = "((?:\"[^\"]*\"|'[^']*'|\\S+)*)" and
+ // In Python, we allow both `"` and `'` for strings, as well as the prefixes `bru`.
+ // For example, `b"foo"`.
+ value = "((?:[bru]*\"[^\"]*\"|[bru]*'[^']*'|\\S+)*)" and
result = tags + "(?:=" + value + ")?"
)
}
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-020/semmle/tests/LateCheckOfFunctionArgument.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-020/semmle/tests/LateCheckOfFunctionArgument.expected
new file mode 100644
index 00000000000..8cdb76428db
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-020/semmle/tests/LateCheckOfFunctionArgument.expected
@@ -0,0 +1 @@
+| test.c:6:3:6:8 | call to memset | The value of argument '$@' appears to be checked after the call, rather than before it. | test.c:6:17:6:20 | len1 | len1 |
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-020/semmle/tests/LateCheckOfFunctionArgument.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-020/semmle/tests/LateCheckOfFunctionArgument.qlref
new file mode 100644
index 00000000000..e9107625d29
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-020/semmle/tests/LateCheckOfFunctionArgument.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-020/LateCheckOfFunctionArgument.ql
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-020/semmle/tests/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-020/semmle/tests/test.c
new file mode 100644
index 00000000000..40fb688fb20
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-020/semmle/tests/test.c
@@ -0,0 +1,8 @@
+void workFunction_0(char *s) {
+ int len = 5, len1;
+ char buf[80], buf1[8];
+ if(len<0) return;
+ memset(buf,0,len); //GOOD
+ memset(buf1,0,len1); //BAD
+ if(len1<0) return;
+}
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-14/semmle/tests/CompilerRemovalOfCodeToClearBuffers.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-14/semmle/tests/CompilerRemovalOfCodeToClearBuffers.expected
deleted file mode 100644
index 8d98d94ff46..00000000000
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-14/semmle/tests/CompilerRemovalOfCodeToClearBuffers.expected
+++ /dev/null
@@ -1,3 +0,0 @@
-| test.c:13:9:13:13 | buff1 | This variable will not be cleared. |
-| test.c:35:9:35:13 | buff1 | This variable will not be cleared. |
-| test.c:43:9:43:13 | buff1 | This variable will not be cleared. |
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-14/semmle/tests/CompilerRemovalOfCodeToClearBuffers.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-14/semmle/tests/CompilerRemovalOfCodeToClearBuffers.qlref
deleted file mode 100644
index 61d2a29b126..00000000000
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-14/semmle/tests/CompilerRemovalOfCodeToClearBuffers.qlref
+++ /dev/null
@@ -1 +0,0 @@
-experimental/Security/CWE/CWE-14/CompilerRemovalOfCodeToClearBuffers.ql
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-14/semmle/tests/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-14/semmle/tests/test.c
deleted file mode 100644
index 221072330c3..00000000000
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-14/semmle/tests/test.c
+++ /dev/null
@@ -1,201 +0,0 @@
-struct buffers
-{
- unsigned char buff1[50];
- unsigned char *buff2;
-} globalBuff1,*globalBuff2;
-
-unsigned char * globalBuff;
-void badFunc0_0(){
- unsigned char buff1[12];
- int i;
- for(i=0;i<12;i++)
- buff1[i]=13;
- memset(buff1,12,12);
-}
-void nobadFunc0_0(){
- unsigned char buff1[12];
- memset(buff1,12,12);
-}
-void nobadFunc0_1(){
- unsigned char buff1[12];
- int i;
- memset(buff1,12,12);
- for(i=0;i<12;i++)
- buff1[i]=13;
- free(buff1);
-}
-void nobadFunc1_0(){
- unsigned char * buff1;
- buff1 = (unsigned char *) malloc(12);
- memset(buff1,12,12);
-}
-void badFunc1_0(){
- unsigned char * buff1;
- buff1 = (unsigned char *) malloc(12);
- memset(buff1,12,12);
- free(buff1);
-}
-void badFunc1_1(){
- unsigned char buff1[12];
- int i;
- for(i=0;i<12;i++)
- buff1[i]=13;
- memset(buff1,12,12);
- free(buff1);
-}
-void nobadFunc2_0_0(){
- unsigned char buff1[12];
- int i;
- for(i=0;i<12;i++)
- buff1[i]=13;
- memset(buff1,12,12);
- printf(buff1);
-}
-
-void nobadFunc2_0_1(){
- unsigned char buff1[12];
- int i;
- for(i=0;i<12;i++)
- buff1[i]=13;
- memset(buff1,12,12);
- printf(buff1+3);
-}
-
-void nobadFunc2_0_2(){
- unsigned char buff1[12];
- int i;
- for(i=0;i<12;i++)
- buff1[i]=13;
- memset(buff1,12,12);
- printf(*buff1);
-}
-
-void nobadFunc2_0_3(){
- unsigned char buff1[12];
- int i;
- for(i=0;i<12;i++)
- buff1[i]=13;
- memset(buff1,12,12);
- printf(*(buff1+3));
-}
-unsigned char * nobadFunc2_0_4(){
- unsigned char buff1[12];
- int i;
- for(i=0;i<12;i++)
- buff1[i]=13;
- memset(buff1,12,12);
- return buff1;
-}
-
-unsigned char * nobadFunc2_0_5(){
- unsigned char buff1[12];
- int i;
- for(i=0;i<12;i++)
- buff1[i]=13;
- memset(buff1,12,12);
- return buff1+3;
-}
-unsigned char nobadFunc2_0_6(){
- unsigned char buff1[12];
- int i;
- for(i=0;i<12;i++)
- buff1[i]=13;
- memset(buff1,12,12);
- return *buff1;
-}
-
-unsigned char nobadFunc2_0_7(){
- unsigned char buff1[12];
- int i;
- for(i=0;i<12;i++)
- buff1[i]=13;
- memset(buff1,12,12);
- return *(buff1+3);
-}
-void nobadFunc2_1_0(){
- unsigned char buff1[12];
- int i;
- for(i=0;i<12;i++)
- buff1[i]=13;
- memset(buff1,12,12);
- if(*buff1==0)
- printf("123123");
-}
-void nobadFunc2_1_1(){
- unsigned char buff1[12];
- int i;
- for(i=0;i<12;i++)
- buff1[i]=13;
- memset(buff1,12,12);
- if(*(buff1+3)==0)
- printf("123123");
-}
-void nobadFunc2_1_2(){
- unsigned char buff1[12];
- int i;
- for(i=0;i<12;i++)
- buff1[i]=13;
- memset(buff1,12,12);
- buff1[2]=5;
-}
-void nobadFunc3_0(unsigned char * buffAll){
- unsigned char * buff1 = buffAll;
- memset(buff1,12,12);
-}
-void nobadFunc3_1(unsigned char * buffAll){
- unsigned char * buff1 = buffAll+3;
- memset(buff1,12,12);
-}
-void nobadFunc3_2(struct buffers buffAll){
- unsigned char * buff1 = buffAll.buff1;
- memset(buff1,12,12);
-}
-void nobadFunc3_3(struct buffers buffAll){
- unsigned char * buff1 = buffAll.buff2;
- memset(buff1,12,12);
-}
-void nobadFunc3_4(struct buffers buffAll){
- unsigned char * buff1 = buffAll.buff2+3;
- memset(buff1,12,12);
-}
-void nobadFunc3_5(struct buffers * buffAll){
- unsigned char * buff1 = buffAll->buff1;
- memset(buff1,12,12);
-}
-void nobadFunc3_6(struct buffers *buffAll){
- unsigned char * buff1 = buffAll->buff2;
- memset(buff1,12,12);
-}
-void nobadFunc4(){
- unsigned char * buff1 = globalBuff;
- memset(buff1,12,12);
-}
-void nobadFunc4_0(){
- unsigned char * buff1 = globalBuff;
- memset(buff1,12,12);
-}
-void nobadFunc4_1(){
- unsigned char * buff1 = globalBuff+3;
- memset(buff1,12,12);
-}
-void nobadFunc4_2(){
- unsigned char * buff1 = globalBuff1.buff1;
- memset(buff1,12,12);
-}
-void nobadFunc4_3(){
- unsigned char * buff1 = globalBuff1.buff2;
- memset(buff1,12,12);
-}
-void nobadFunc4_4(){
- unsigned char * buff1 = globalBuff1.buff2+3;
- memset(buff1,12,12);
-}
-void nobadFunc4_5(){
- unsigned char * buff1 = globalBuff2->buff1;
- memset(buff1,12,12);
-}
-void nobadFunc4_6(){
- unsigned char * buff1 = globalBuff2->buff2;
- memset(buff1,12,12);
-}
-
diff --git a/cpp/ql/test/library-tests/dataflow/fields/aliasing.cpp b/cpp/ql/test/library-tests/dataflow/fields/aliasing.cpp
index 500bbed53a9..fcdfc53cfdb 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/aliasing.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/aliasing.cpp
@@ -124,7 +124,7 @@ void pointer_deref(int* xs) {
void pointer_deref_sub(int* xs) {
taint_a_ptr(xs - 2);
- sink(*(xs - 2)); // $ ir MISSING: ast
+ sink(*(xs - 2)); // $ MISSING: ast,ir
}
void pointer_many_addrof_and_deref(int* xs) {
@@ -156,13 +156,13 @@ struct S_with_array {
void pointer_member_deref() {
S_with_array s;
taint_a_ptr(s.data);
- sink(*s.data); // $ ir,ast
+ sink(*s.data); // $ ast MISSING: ir
}
void array_member_deref() {
S_with_array s;
taint_a_ptr(s.data);
- sink(s.data[0]); // $ ir,ast
+ sink(s.data[0]); // $ ast MISSING: ir
}
struct S2 {
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 c8b70a74b3a..37a0dc3832a 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
@@ -1,28 +1,37 @@
edges
-| A.cpp:55:5:55:5 | set output argument [c] | A.cpp:56:13:56:15 | call to get |
-| A.cpp:55:12:55:19 | (C *)... | A.cpp:55:5:55:5 | set output argument [c] |
-| A.cpp:55:12:55:19 | new | A.cpp:55:5:55:5 | set output argument [c] |
-| A.cpp:57:11:57:24 | B output argument [c] | A.cpp:57:28:57:30 | call to get |
-| A.cpp:57:17:57:23 | new | A.cpp:57:11:57:24 | B output argument [c] |
+| A.cpp:55:5:55:5 | set output argument [c] | A.cpp:56:10:56:10 | b indirection [c] |
+| A.cpp:55:8:55:10 | new | A.cpp:55:5:55:5 | set output argument [c] |
+| A.cpp:55:12:55:19 | (C *)... | A.cpp:55:8:55:10 | new |
+| A.cpp:55:12:55:19 | new | A.cpp:55:8:55:10 | new |
+| A.cpp:56:10:56:10 | b indirection [c] | A.cpp:56:13:56:15 | call to get |
+| A.cpp:57:10:57:25 | new indirection [c] | A.cpp:57:28:57:30 | call to get |
+| A.cpp:57:11:57:24 | B output argument [c] | A.cpp:57:10:57:25 | new indirection [c] |
+| A.cpp:57:11:57:24 | new | A.cpp:57:11:57:24 | B output argument [c] |
+| A.cpp:57:17:57:23 | new | A.cpp:57:11:57:24 | new |
| A.cpp:98:12:98:18 | new | A.cpp:100:5:100:13 | Chi [a] |
-| A.cpp:100:5:100:13 | Chi [a] | A.cpp:103:14:103:14 | *c [a] |
+| A.cpp:100:5:100:13 | Chi [a] | A.cpp:101:8:101:9 | c1 indirection [a] |
+| A.cpp:101:8:101:9 | c1 indirection [a] | A.cpp:103:14:103:14 | *c [a] |
| A.cpp:103:14:103:14 | *c [a] | A.cpp:107:16:107:16 | a |
| A.cpp:126:5:126:5 | Chi [c] | A.cpp:131:8:131:8 | f7 output argument [c] |
| A.cpp:126:5:126:5 | set output argument [c] | A.cpp:126:5:126:5 | Chi [c] |
-| A.cpp:126:12:126:18 | new | A.cpp:126:5:126:5 | set output argument [c] |
+| A.cpp:126:8:126:10 | new | A.cpp:126:5:126:5 | set output argument [c] |
+| A.cpp:126:12:126:18 | new | A.cpp:126:8:126:10 | new |
| A.cpp:131:8:131:8 | Chi [c] | A.cpp:132:13:132:13 | c |
| A.cpp:131:8:131:8 | f7 output argument [c] | A.cpp:131:8:131:8 | Chi [c] |
| A.cpp:142:7:142:20 | Chi [c] | A.cpp:151:18:151:18 | D output argument [c] |
| A.cpp:142:14:142:20 | new | A.cpp:142:7:142:20 | Chi [c] |
| A.cpp:143:7:143:31 | Chi [b] | A.cpp:151:12:151:24 | D output argument [b] |
| A.cpp:143:25:143:31 | new | A.cpp:143:7:143:31 | Chi [b] |
-| A.cpp:150:12:150:18 | new | A.cpp:151:12:151:24 | D output argument [b] |
+| A.cpp:150:12:150:18 | new | A.cpp:151:12:151:24 | b |
| A.cpp:151:12:151:24 | Chi [b] | A.cpp:152:13:152:13 | b |
| A.cpp:151:12:151:24 | D output argument [b] | A.cpp:151:12:151:24 | Chi [b] |
+| A.cpp:151:12:151:24 | b | A.cpp:151:12:151:24 | D output argument [b] |
| A.cpp:151:18:151:18 | Chi [c] | A.cpp:154:13:154:13 | c |
| A.cpp:151:18:151:18 | D output argument [c] | A.cpp:151:18:151:18 | Chi [c] |
-| C.cpp:18:12:18:18 | C output argument [s1] | C.cpp:27:8:27:11 | *#this [s1] |
-| C.cpp:18:12:18:18 | C output argument [s3] | C.cpp:27:8:27:11 | *#this [s3] |
+| C.cpp:18:12:18:18 | C output argument [s1] | C.cpp:19:5:19:5 | c indirection [s1] |
+| C.cpp:18:12:18:18 | C output argument [s3] | C.cpp:19:5:19:5 | c indirection [s3] |
+| C.cpp:19:5:19:5 | c indirection [s1] | C.cpp:27:8:27:11 | *#this [s1] |
+| C.cpp:19:5:19:5 | c indirection [s3] | C.cpp:27:8:27:11 | *#this [s3] |
| C.cpp:22:12:22:21 | Chi [s1] | C.cpp:24:5:24:25 | Chi [s1] |
| C.cpp:22:12:22:21 | new | C.cpp:22:12:22:21 | Chi [s1] |
| C.cpp:24:5:24:25 | Chi [s1] | C.cpp:18:12:18:18 | C output argument [s1] |
@@ -50,27 +59,18 @@ edges
| 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:126:15:126:20 | 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:158:15:158:20 | taint_a_ptr output argument [array content] |
-| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:164:15:164:20 | 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:126:15:126:20 | Chi [array content] | aliasing.cpp:127:8:127:16 | * ... |
-| aliasing.cpp:126:15:126:20 | taint_a_ptr output argument [array content] | aliasing.cpp:126:15:126:20 | 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:158:15:158:20 | Chi [array content] | aliasing.cpp:159:8:159:14 | * ... |
-| aliasing.cpp:158:15:158:20 | taint_a_ptr output argument [array content] | aliasing.cpp:158:15:158:20 | Chi [array content] |
-| aliasing.cpp:164:15:164:20 | Chi [array content] | aliasing.cpp:165:8:165:16 | access to array |
-| aliasing.cpp:164:15:164:20 | taint_a_ptr output argument [array content] | aliasing.cpp:164:15:164:20 | Chi [array content] |
| 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 |
@@ -86,14 +86,22 @@ edges
| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:10:8:10:15 | * ... |
| arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:16:8:16:13 | access to array |
| arrays.cpp:36:26:36:35 | call to user_input | arrays.cpp:37:24:37:27 | data |
-| by_reference.cpp:50:3:50:3 | setDirectly output argument [a] | by_reference.cpp:51:10:51:20 | call to getDirectly |
-| by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:50:3:50:3 | setDirectly output argument [a] |
-| by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] | by_reference.cpp:57:10:57:22 | call to getIndirectly |
-| by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] |
-| by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] | by_reference.cpp:63:10:63:28 | call to getThroughNonMember |
-| by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] |
-| by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] | by_reference.cpp:69:8:69:20 | call to nonMemberGetA |
-| by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] |
+| by_reference.cpp:50:3:50:3 | setDirectly output argument [a] | by_reference.cpp:51:8:51:8 | s indirection [a] |
+| by_reference.cpp:50:5:50:15 | call to user_input | by_reference.cpp:50:3:50:3 | setDirectly output argument [a] |
+| by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:50:5:50:15 | call to user_input |
+| by_reference.cpp:51:8:51:8 | s indirection [a] | by_reference.cpp:51:10:51:20 | call to getDirectly |
+| by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] | by_reference.cpp:57:8:57:8 | s indirection [a] |
+| by_reference.cpp:56:5:56:17 | call to user_input | by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] |
+| by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:56:5:56:17 | call to user_input |
+| by_reference.cpp:57:8:57:8 | s indirection [a] | by_reference.cpp:57:10:57:22 | call to getIndirectly |
+| by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] | by_reference.cpp:63:8:63:8 | s indirection [a] |
+| by_reference.cpp:62:5:62:23 | call to user_input | by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] |
+| by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:62:5:62:23 | call to user_input |
+| by_reference.cpp:63:8:63:8 | s indirection [a] | by_reference.cpp:63:10:63:28 | call to getThroughNonMember |
+| by_reference.cpp:68:3:68:15 | call to user_input | by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] |
+| by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] | by_reference.cpp:69:22:69:23 | & ... indirection [a] |
+| by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:68:3:68:15 | call to user_input |
+| by_reference.cpp:69:22:69:23 | & ... indirection [a] | by_reference.cpp:69:8:69:20 | call to nonMemberGetA |
| by_reference.cpp:84:3:84:25 | Chi [a] | by_reference.cpp:102:21:102:39 | taint_inner_a_ptr output argument [a] |
| by_reference.cpp:84:3:84:25 | Chi [a] | by_reference.cpp:106:21:106:41 | taint_inner_a_ptr output argument [a] |
| by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:84:3:84:25 | Chi [a] |
@@ -126,91 +134,135 @@ edges
| 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 |
-| complex.cpp:40:17:40:17 | *b [a_] | complex.cpp:42:18:42:18 | call to a |
+| 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 | a output argument [b_] |
-| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:43:18:43:18 | call to b |
-| complex.cpp:42:16:42:16 | Chi [b_] | complex.cpp:43:18:43:18 | call to 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:18:43:18 | call to b |
-| complex.cpp:53:12:53:12 | Chi [a_] | complex.cpp:40:17:40:17 | *b [a_] |
-| complex.cpp:53:12:53:12 | setA output argument [a_] | complex.cpp:40:17:40:17 | *b [a_] |
+| 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:19:53:28 | call to user_input | complex.cpp:53:12:53:12 | setA output argument [a_] |
-| complex.cpp:54:12:54:12 | Chi [b_] | complex.cpp:40:17:40:17 | *b [b_] |
-| complex.cpp:54:12:54:12 | setB output argument [b_] | complex.cpp:40:17:40:17 | *b [b_] |
+| 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:19:54:28 | call to user_input | complex.cpp:54:12:54:12 | setB output argument [b_] |
-| complex.cpp:55:12:55:12 | Chi [a_] | complex.cpp:40:17:40:17 | *b [a_] |
+| 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 | setB output argument [a_] |
-| complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:40:17:40:17 | *b [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 | setB output argument [a_] |
-| complex.cpp:55:19:55:28 | call to user_input | complex.cpp:55:12:55:12 | setA output argument [a_] |
-| complex.cpp:56:12:56:12 | Chi [a_] | complex.cpp:40:17:40:17 | *b [a_] |
-| complex.cpp:56:12:56:12 | Chi [b_] | complex.cpp:40:17:40:17 | *b [b_] |
-| complex.cpp:56:12:56:12 | setB output argument [a_] | complex.cpp:40:17:40:17 | *b [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 [b_] | complex.cpp:40:17:40:17 | *b [b_] |
+| 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:19:56:28 | call to user_input | complex.cpp:56:12:56:12 | setB output argument [b_] |
-| constructors.cpp:26:15:26:15 | *f [a_] | constructors.cpp:28:12:28:12 | call to a |
-| constructors.cpp:26:15:26:15 | *f [b_] | constructors.cpp:28:10:28:10 | a output argument [b_] |
-| constructors.cpp:26:15:26:15 | *f [b_] | constructors.cpp:29:12:29:12 | call to b |
-| constructors.cpp:28:10:28:10 | a output argument [b_] | constructors.cpp:29:12:29:12 | call to b |
-| constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:34:11:34:26 | Foo output argument [a_] |
-| constructors.cpp:34:11:34:26 | Foo output argument [a_] | constructors.cpp:26:15:26:15 | *f [a_] |
-| constructors.cpp:35:11:35:26 | Foo output argument [b_] | constructors.cpp:26:15:26:15 | *f [b_] |
-| constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:35:11:35:26 | Foo output argument [b_] |
-| constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:36:11:36:37 | Foo output argument [a_] |
-| constructors.cpp:36:11:36:37 | Foo output argument [a_] | constructors.cpp:26:15:26:15 | *f [a_] |
-| constructors.cpp:36:11:36:37 | Foo output argument [b_] | constructors.cpp:26:15:26:15 | *f [b_] |
-| constructors.cpp:36:25:36:34 | call to user_input | constructors.cpp:36:11:36:37 | Foo output argument [b_] |
-| simple.cpp:26:15:26:15 | *f [a_] | simple.cpp:28:12:28:12 | call to a |
-| simple.cpp:26:15:26:15 | *f [b_] | simple.cpp:28:10:28:10 | a output argument [b_] |
-| simple.cpp:26:15:26:15 | *f [b_] | simple.cpp:29:12:29:12 | call to b |
-| simple.cpp:28:10:28:10 | a output argument [b_] | simple.cpp:29:12:29:12 | call to b |
-| simple.cpp:39:5:39:5 | setA output argument [a_] | simple.cpp:26:15:26:15 | *f [a_] |
-| simple.cpp:39:12:39:21 | call to user_input | simple.cpp:39:5:39:5 | setA output argument [a_] |
-| simple.cpp:40:5:40:5 | setB output argument [b_] | simple.cpp:26:15:26:15 | *f [b_] |
-| simple.cpp:40:12:40:21 | call to user_input | simple.cpp:40:5:40:5 | setB output argument [b_] |
-| simple.cpp:41:5:41:5 | setA output argument [a_] | simple.cpp:26:15:26:15 | *f [a_] |
-| simple.cpp:41:5:41:5 | setA output argument [a_] | simple.cpp:42:5:42:5 | setB output argument [a_] |
-| simple.cpp:41:12:41:21 | call to user_input | simple.cpp:41:5:41:5 | setA output argument [a_] |
-| simple.cpp:42:5:42:5 | setB output argument [a_] | simple.cpp:26:15:26:15 | *f [a_] |
-| simple.cpp:42:5:42:5 | setB output argument [b_] | simple.cpp:26:15:26:15 | *f [b_] |
-| simple.cpp:42:12:42:21 | call to user_input | simple.cpp:42:5:42:5 | setB output argument [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 |
+| complex.cpp:59:7:59:8 | b1 indirection [a_] | complex.cpp:40:17:40:17 | *b [a_] |
+| complex.cpp:62:7:62:8 | b2 indirection [b_] | complex.cpp:40:17:40:17 | *b [b_] |
+| complex.cpp:65:7:65:8 | b3 indirection [a_] | complex.cpp:40:17:40:17 | *b [a_] |
+| complex.cpp:65:7:65:8 | b3 indirection [b_] | complex.cpp:40:17:40:17 | *b [b_] |
+| constructors.cpp:26:15:26:15 | *f [a_] | constructors.cpp:28:10:28:10 | f indirection [a_] |
+| constructors.cpp:26:15:26:15 | *f [b_] | constructors.cpp:28:10:28:10 | f indirection [b_] |
+| constructors.cpp:26:15:26:15 | *f [b_] | constructors.cpp:29:10:29:10 | f indirection [b_] |
+| constructors.cpp:28:10:28:10 | a output argument [b_] | constructors.cpp:29:10:29:10 | f indirection [b_] |
+| constructors.cpp:28:10:28:10 | f indirection [a_] | constructors.cpp:28:12:28:12 | call to a |
+| constructors.cpp:28:10:28:10 | f indirection [b_] | constructors.cpp:28:10:28:10 | a output argument [b_] |
+| constructors.cpp:29:10:29:10 | f indirection [b_] | constructors.cpp:29:12:29:12 | call to b |
+| constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:34:11:34:26 | call to user_input |
+| constructors.cpp:34:11:34:26 | Foo output argument [a_] | constructors.cpp:40:9:40:9 | f indirection [a_] |
+| constructors.cpp:34:11:34:26 | call to user_input | constructors.cpp:34:11:34:26 | Foo output argument [a_] |
+| constructors.cpp:35:11:35:26 | Foo output argument [b_] | constructors.cpp:43:9:43:9 | g indirection [b_] |
+| constructors.cpp:35:11:35:26 | call to user_input | constructors.cpp:35:11:35:26 | Foo output argument [b_] |
+| constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:35:11:35:26 | call to user_input |
+| constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:36:11:36:37 | call to user_input |
+| constructors.cpp:36:11:36:37 | Foo output argument [a_] | constructors.cpp:46:9:46:9 | h indirection [a_] |
+| constructors.cpp:36:11:36:37 | Foo output argument [b_] | constructors.cpp:46:9:46:9 | h indirection [b_] |
+| constructors.cpp:36:11:36:37 | call to user_input | constructors.cpp:36:11:36:37 | Foo output argument [a_] |
+| constructors.cpp:36:11:36:37 | call to user_input | constructors.cpp:36:11:36:37 | Foo output argument [b_] |
+| constructors.cpp:36:25:36:34 | call to user_input | constructors.cpp:36:11:36:37 | call to user_input |
+| constructors.cpp:40:9:40:9 | f indirection [a_] | constructors.cpp:26:15:26:15 | *f [a_] |
+| constructors.cpp:43:9:43:9 | g indirection [b_] | constructors.cpp:26:15:26:15 | *f [b_] |
+| constructors.cpp:46:9:46:9 | h indirection [a_] | constructors.cpp:26:15:26:15 | *f [a_] |
+| constructors.cpp:46:9:46:9 | h indirection [b_] | constructors.cpp:26:15:26:15 | *f [b_] |
+| simple.cpp:26:15:26:15 | *f [a_] | simple.cpp:28:10:28:10 | f indirection [a_] |
+| simple.cpp:26:15:26:15 | *f [b_] | simple.cpp:28:10:28:10 | f indirection [b_] |
+| simple.cpp:26:15:26:15 | *f [b_] | simple.cpp:29:10:29:10 | f indirection [b_] |
+| simple.cpp:28:10:28:10 | a output argument [b_] | simple.cpp:29:10:29:10 | f indirection [b_] |
+| simple.cpp:28:10:28:10 | f indirection [a_] | simple.cpp:28:12:28:12 | call to a |
+| simple.cpp:28:10:28:10 | f indirection [b_] | simple.cpp:28:10:28:10 | a output argument [b_] |
+| simple.cpp:29:10:29:10 | f indirection [b_] | simple.cpp:29:12:29:12 | call to b |
+| simple.cpp:39:5:39:5 | setA output argument [a_] | simple.cpp:45:9:45:9 | f indirection [a_] |
+| simple.cpp:39:7:39:10 | call to user_input | simple.cpp:39:5:39:5 | setA output argument [a_] |
+| simple.cpp:39:12:39:21 | call to user_input | simple.cpp:39:7:39:10 | call to user_input |
+| simple.cpp:40:5:40:5 | setB output argument [b_] | simple.cpp:48:9:48:9 | g indirection [b_] |
+| simple.cpp:40:7:40:10 | call to user_input | simple.cpp:40:5:40:5 | setB output argument [b_] |
+| simple.cpp:40:12:40:21 | call to user_input | simple.cpp:40:7:40:10 | call to user_input |
+| simple.cpp:41:5:41:5 | setA output argument [a_] | simple.cpp:42:5:42:5 | h indirection [a_] |
+| simple.cpp:41:5:41:5 | setA output argument [a_] | simple.cpp:51:9:51:9 | h indirection [a_] |
+| simple.cpp:41:7:41:10 | call to user_input | simple.cpp:41:5:41:5 | setA output argument [a_] |
+| simple.cpp:41:12:41:21 | call to user_input | simple.cpp:41:7:41:10 | call to user_input |
+| simple.cpp:42:5:42:5 | h indirection [a_] | simple.cpp:42:5:42:5 | setB output argument [a_] |
+| simple.cpp:42:5:42:5 | setB output argument [a_] | simple.cpp:51:9:51:9 | h indirection [a_] |
+| simple.cpp:42:5:42:5 | setB output argument [b_] | simple.cpp:51:9:51:9 | h indirection [b_] |
+| simple.cpp:42:7:42:10 | call to user_input | simple.cpp:42:5:42:5 | setB output argument [b_] |
+| simple.cpp:42:12:42:21 | call to user_input | simple.cpp:42:7:42:10 | call to user_input |
+| simple.cpp:45:9:45:9 | f indirection [a_] | simple.cpp:26:15:26:15 | *f [a_] |
+| simple.cpp:48:9:48:9 | g indirection [b_] | simple.cpp:26:15:26:15 | *f [b_] |
+| simple.cpp:51:9:51:9 | h indirection [a_] | simple.cpp:26:15:26:15 | *f [a_] |
+| simple.cpp:51:9:51:9 | h indirection [b_] | simple.cpp:26:15:26:15 | *f [b_] |
| simple.cpp:65:5:65:22 | Store [i] | simple.cpp:66:12:66:12 | Store [i] |
| simple.cpp:65:11:65:20 | call to user_input | simple.cpp:65:5:65:22 | Store [i] |
| simple.cpp:66:12:66:12 | Store [i] | simple.cpp:67:13:67:13 | i |
-| simple.cpp:83:9:83:28 | Chi [f1] | simple.cpp:84:14:84:20 | call to getf2f1 |
+| simple.cpp:83:9:83:28 | Chi [f1] | simple.cpp:84:14:84:20 | this indirection [f1] |
| simple.cpp:83:17:83:26 | call to user_input | simple.cpp:83:9:83:28 | Chi [f1] |
+| simple.cpp:84:14:84:20 | this indirection [f1] | simple.cpp:84:14:84:20 | call to getf2f1 |
| simple.cpp:92:5:92:22 | Store [i] | simple.cpp:93:20:93:20 | Store [i] |
| simple.cpp:92:11:92:20 | call to user_input | simple.cpp:92:5:92:22 | Store [i] |
| simple.cpp:93:20:93:20 | Store [i] | simple.cpp:94:13:94:13 | i |
| struct_init.c:14:24:14:25 | *ab [a] | struct_init.c:15:12:15:12 | a |
-| struct_init.c:20:20:20:29 | Chi [a] | struct_init.c:14:24:14:25 | *ab [a] |
+| struct_init.c:20:20:20:29 | Chi [a] | struct_init.c:24:10:24:12 | & ... indirection [a] |
| struct_init.c:20:20:20:29 | call to user_input | struct_init.c:20:20:20:29 | Chi [a] |
| struct_init.c:20:20:20:29 | call to user_input | struct_init.c:22:11:22:11 | a |
-| struct_init.c:27:7:27:16 | Chi [a] | struct_init.c:14:24:14:25 | *ab [a] |
+| struct_init.c:24:10:24:12 | & ... indirection [a] | struct_init.c:14:24:14:25 | *ab [a] |
+| struct_init.c:27:7:27:16 | Chi [a] | struct_init.c:36:10:36:24 | & ... indirection [a] |
| struct_init.c:27:7:27:16 | call to user_input | struct_init.c:27:7:27:16 | Chi [a] |
| struct_init.c:27:7:27:16 | call to user_input | struct_init.c:31:23:31:23 | a |
+| struct_init.c:36:10:36:24 | & ... indirection [a] | struct_init.c:14:24:14:25 | *ab [a] |
nodes
| A.cpp:55:5:55:5 | set output argument [c] | semmle.label | set output argument [c] |
+| A.cpp:55:8:55:10 | new | semmle.label | new |
| A.cpp:55:12:55:19 | (C *)... | semmle.label | (C *)... |
| A.cpp:55:12:55:19 | new | semmle.label | new |
+| A.cpp:56:10:56:10 | b indirection [c] | semmle.label | b indirection [c] |
| A.cpp:56:13:56:15 | call to get | semmle.label | call to get |
+| A.cpp:57:10:57:25 | new indirection [c] | semmle.label | new indirection [c] |
| A.cpp:57:11:57:24 | B output argument [c] | semmle.label | B output argument [c] |
+| A.cpp:57:11:57:24 | new | semmle.label | new |
| A.cpp:57:17:57:23 | new | semmle.label | new |
| A.cpp:57:28:57:30 | call to get | semmle.label | call to get |
| A.cpp:98:12:98:18 | new | semmle.label | new |
| A.cpp:100:5:100:13 | Chi [a] | semmle.label | Chi [a] |
+| A.cpp:101:8:101:9 | c1 indirection [a] | semmle.label | c1 indirection [a] |
| A.cpp:103:14:103:14 | *c [a] | semmle.label | *c [a] |
| A.cpp:107:16:107:16 | a | semmle.label | a |
| A.cpp:126:5:126:5 | Chi [c] | semmle.label | Chi [c] |
| A.cpp:126:5:126:5 | set output argument [c] | semmle.label | set output argument [c] |
+| A.cpp:126:8:126:10 | new | semmle.label | new |
| A.cpp:126:12:126:18 | new | semmle.label | new |
| A.cpp:131:8:131:8 | Chi [c] | semmle.label | Chi [c] |
| A.cpp:131:8:131:8 | f7 output argument [c] | semmle.label | f7 output argument [c] |
@@ -222,12 +274,15 @@ nodes
| A.cpp:150:12:150:18 | new | semmle.label | new |
| A.cpp:151:12:151:24 | Chi [b] | semmle.label | Chi [b] |
| A.cpp:151:12:151:24 | D output argument [b] | semmle.label | D output argument [b] |
+| A.cpp:151:12:151:24 | b | semmle.label | b |
| A.cpp:151:18:151:18 | Chi [c] | semmle.label | Chi [c] |
| A.cpp:151:18:151:18 | D output argument [c] | semmle.label | D output argument [c] |
| A.cpp:152:13:152:13 | b | semmle.label | b |
| A.cpp:154:13:154:13 | c | semmle.label | c |
| C.cpp:18:12:18:18 | C output argument [s1] | semmle.label | C output argument [s1] |
| C.cpp:18:12:18:18 | C output argument [s3] | semmle.label | C output argument [s3] |
+| C.cpp:19:5:19:5 | c indirection [s1] | semmle.label | c indirection [s1] |
+| C.cpp:19:5:19:5 | c indirection [s3] | semmle.label | c indirection [s3] |
| C.cpp:22:12:22:21 | Chi [s1] | semmle.label | Chi [s1] |
| C.cpp:22:12:22:21 | new | semmle.label | new |
| C.cpp:24:5:24:25 | Chi [s1] | semmle.label | Chi [s1] |
@@ -270,21 +325,12 @@ nodes
| 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:122:8:122:12 | access to array | semmle.label | access to array |
-| aliasing.cpp:126:15:126:20 | Chi [array content] | semmle.label | Chi [array content] |
-| aliasing.cpp:126:15:126:20 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
-| aliasing.cpp:127:8:127:16 | * ... | semmle.label | * ... |
| 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: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:137:8:137:11 | * ... | semmle.label | * ... |
-| aliasing.cpp:158:15:158:20 | Chi [array content] | semmle.label | Chi [array content] |
-| aliasing.cpp:158:15:158:20 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
-| aliasing.cpp:159:8:159:14 | * ... | semmle.label | * ... |
-| aliasing.cpp:164:15:164:20 | Chi [array content] | semmle.label | Chi [array content] |
-| aliasing.cpp:164:15:164:20 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
-| aliasing.cpp:165:8:165:16 | access to array | semmle.label | access to array |
| 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] |
@@ -307,17 +353,25 @@ nodes
| arrays.cpp:36:26:36:35 | call to user_input | semmle.label | call to user_input |
| arrays.cpp:37:24:37:27 | data | semmle.label | data |
| by_reference.cpp:50:3:50:3 | setDirectly output argument [a] | semmle.label | setDirectly output argument [a] |
+| by_reference.cpp:50:5:50:15 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:50:17:50:26 | call to user_input | semmle.label | call to user_input |
+| by_reference.cpp:51:8:51:8 | s indirection [a] | semmle.label | s indirection [a] |
| by_reference.cpp:51:10:51:20 | call to getDirectly | semmle.label | call to getDirectly |
| by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] | semmle.label | setIndirectly output argument [a] |
+| by_reference.cpp:56:5:56:17 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:56:19:56:28 | call to user_input | semmle.label | call to user_input |
+| by_reference.cpp:57:8:57:8 | s indirection [a] | semmle.label | s indirection [a] |
| by_reference.cpp:57:10:57:22 | call to getIndirectly | semmle.label | call to getIndirectly |
| by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] | semmle.label | setThroughNonMember output argument [a] |
+| by_reference.cpp:62:5:62:23 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:62:25:62:34 | call to user_input | semmle.label | call to user_input |
+| by_reference.cpp:63:8:63:8 | s indirection [a] | semmle.label | s indirection [a] |
| by_reference.cpp:63:10:63:28 | call to getThroughNonMember | semmle.label | call to getThroughNonMember |
+| by_reference.cpp:68:3:68:15 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] | semmle.label | nonMemberSetA output argument [a] |
| by_reference.cpp:68:21:68:30 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:69:8:69:20 | call to nonMemberGetA | semmle.label | call to nonMemberGetA |
+| by_reference.cpp:69:22:69:23 | & ... indirection [a] | semmle.label | & ... indirection [a] |
| by_reference.cpp:84:3:84:25 | Chi [a] | semmle.label | Chi [a] |
| by_reference.cpp:84:14:84:23 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:88:3:88:24 | Chi [a] | semmle.label | Chi [a] |
@@ -358,49 +412,84 @@ nodes
| 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_] |
+| complex.cpp:56:14:56:17 | call to user_input | semmle.label | call to user_input |
| complex.cpp:56:19:56:28 | call to user_input | semmle.label | call to user_input |
+| complex.cpp:59:7:59:8 | b1 indirection [a_] | semmle.label | b1 indirection [a_] |
+| complex.cpp:62:7:62:8 | b2 indirection [b_] | semmle.label | b2 indirection [b_] |
+| complex.cpp:65:7:65:8 | b3 indirection [a_] | semmle.label | b3 indirection [a_] |
+| complex.cpp:65:7:65:8 | b3 indirection [b_] | semmle.label | b3 indirection [b_] |
| constructors.cpp:26:15:26:15 | *f [a_] | semmle.label | *f [a_] |
| constructors.cpp:26:15:26:15 | *f [b_] | semmle.label | *f [b_] |
| constructors.cpp:28:10:28:10 | a output argument [b_] | semmle.label | a output argument [b_] |
+| constructors.cpp:28:10:28:10 | f indirection [a_] | semmle.label | f indirection [a_] |
+| constructors.cpp:28:10:28:10 | f indirection [b_] | semmle.label | f indirection [b_] |
| constructors.cpp:28:12:28:12 | call to a | semmle.label | call to a |
+| constructors.cpp:29:10:29:10 | f indirection [b_] | semmle.label | f indirection [b_] |
| constructors.cpp:29:12:29:12 | call to b | semmle.label | call to b |
| constructors.cpp:34:11:34:20 | call to user_input | semmle.label | call to user_input |
| constructors.cpp:34:11:34:26 | Foo output argument [a_] | semmle.label | Foo output argument [a_] |
+| constructors.cpp:34:11:34:26 | call to user_input | semmle.label | call to user_input |
| constructors.cpp:35:11:35:26 | Foo output argument [b_] | semmle.label | Foo output argument [b_] |
+| constructors.cpp:35:11:35:26 | call to user_input | semmle.label | call to user_input |
| constructors.cpp:35:14:35:23 | call to user_input | semmle.label | call to user_input |
| constructors.cpp:36:11:36:20 | call to user_input | semmle.label | call to user_input |
| constructors.cpp:36:11:36:37 | Foo output argument [a_] | semmle.label | Foo output argument [a_] |
| constructors.cpp:36:11:36:37 | Foo output argument [b_] | semmle.label | Foo output argument [b_] |
+| constructors.cpp:36:11:36:37 | call to user_input | semmle.label | call to user_input |
+| constructors.cpp:36:11:36:37 | call to user_input | semmle.label | call to user_input |
| constructors.cpp:36:25:36:34 | call to user_input | semmle.label | call to user_input |
+| constructors.cpp:40:9:40:9 | f indirection [a_] | semmle.label | f indirection [a_] |
+| constructors.cpp:43:9:43:9 | g indirection [b_] | semmle.label | g indirection [b_] |
+| constructors.cpp:46:9:46:9 | h indirection [a_] | semmle.label | h indirection [a_] |
+| constructors.cpp:46:9:46:9 | h indirection [b_] | semmle.label | h indirection [b_] |
| simple.cpp:26:15:26:15 | *f [a_] | semmle.label | *f [a_] |
| simple.cpp:26:15:26:15 | *f [b_] | semmle.label | *f [b_] |
| simple.cpp:28:10:28:10 | a output argument [b_] | semmle.label | a output argument [b_] |
+| simple.cpp:28:10:28:10 | f indirection [a_] | semmle.label | f indirection [a_] |
+| simple.cpp:28:10:28:10 | f indirection [b_] | semmle.label | f indirection [b_] |
| simple.cpp:28:12:28:12 | call to a | semmle.label | call to a |
+| simple.cpp:29:10:29:10 | f indirection [b_] | semmle.label | f indirection [b_] |
| simple.cpp:29:12:29:12 | call to b | semmle.label | call to b |
| simple.cpp:39:5:39:5 | setA output argument [a_] | semmle.label | setA output argument [a_] |
+| simple.cpp:39:7:39:10 | call to user_input | semmle.label | call to user_input |
| simple.cpp:39:12:39:21 | call to user_input | semmle.label | call to user_input |
| simple.cpp:40:5:40:5 | setB output argument [b_] | semmle.label | setB output argument [b_] |
+| simple.cpp:40:7:40:10 | call to user_input | semmle.label | call to user_input |
| simple.cpp:40:12:40:21 | call to user_input | semmle.label | call to user_input |
| simple.cpp:41:5:41:5 | setA output argument [a_] | semmle.label | setA output argument [a_] |
+| simple.cpp:41:7:41:10 | call to user_input | semmle.label | call to user_input |
| simple.cpp:41:12:41:21 | call to user_input | semmle.label | call to user_input |
+| simple.cpp:42:5:42:5 | h indirection [a_] | semmle.label | h indirection [a_] |
| simple.cpp:42:5:42:5 | setB output argument [a_] | semmle.label | setB output argument [a_] |
| simple.cpp:42:5:42:5 | setB output argument [b_] | semmle.label | setB output argument [b_] |
+| simple.cpp:42:7:42:10 | call to user_input | semmle.label | call to user_input |
| simple.cpp:42:12:42:21 | call to user_input | semmle.label | call to user_input |
+| simple.cpp:45:9:45:9 | f indirection [a_] | semmle.label | f indirection [a_] |
+| simple.cpp:48:9:48:9 | g indirection [b_] | semmle.label | g indirection [b_] |
+| simple.cpp:51:9:51:9 | h indirection [a_] | semmle.label | h indirection [a_] |
+| simple.cpp:51:9:51:9 | h indirection [b_] | semmle.label | h indirection [b_] |
| simple.cpp:65:5:65:22 | Store [i] | semmle.label | Store [i] |
| simple.cpp:65:11:65:20 | call to user_input | semmle.label | call to user_input |
| simple.cpp:66:12:66:12 | Store [i] | semmle.label | Store [i] |
@@ -408,6 +497,7 @@ nodes
| simple.cpp:83:9:83:28 | Chi [f1] | semmle.label | Chi [f1] |
| simple.cpp:83:17:83:26 | call to user_input | semmle.label | call to user_input |
| simple.cpp:84:14:84:20 | call to getf2f1 | semmle.label | call to getf2f1 |
+| simple.cpp:84:14:84:20 | this indirection [f1] | semmle.label | this indirection [f1] |
| simple.cpp:92:5:92:22 | Store [i] | semmle.label | Store [i] |
| simple.cpp:92:11:92:20 | call to user_input | semmle.label | call to user_input |
| simple.cpp:93:20:93:20 | Store [i] | semmle.label | Store [i] |
@@ -417,9 +507,11 @@ nodes
| struct_init.c:20:20:20:29 | Chi [a] | semmle.label | Chi [a] |
| struct_init.c:20:20:20:29 | call to user_input | semmle.label | call to user_input |
| struct_init.c:22:11:22:11 | a | semmle.label | a |
+| struct_init.c:24:10:24:12 | & ... indirection [a] | semmle.label | & ... indirection [a] |
| struct_init.c:27:7:27:16 | Chi [a] | semmle.label | Chi [a] |
| struct_init.c:27:7:27:16 | call to user_input | semmle.label | call to user_input |
| struct_init.c:31:23:31:23 | a | semmle.label | a |
+| struct_init.c:36:10:36:24 | & ... indirection [a] | semmle.label | & ... indirection [a] |
#select
| A.cpp:56:13:56:15 | call to get | A.cpp:55:12:55:19 | (C *)... | A.cpp:56:13:56:15 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | (C *)... | (C *)... |
| A.cpp:56:13:56:15 | call to get | A.cpp:55:12:55:19 | new | A.cpp:56:13:56:15 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | new | new |
@@ -441,11 +533,8 @@ nodes
| aliasing.cpp:93:12:93:13 | m1 | aliasing.cpp:92:12:92:21 | call to user_input | aliasing.cpp:93:12:93:13 | m1 | m1 flows from $@ | aliasing.cpp:92:12:92:21 | call to user_input | call to user_input |
| aliasing.cpp:102:8:102:10 | * ... | aliasing.cpp:98:10:98:19 | call to user_input | aliasing.cpp:102:8:102:10 | * ... | * ... flows from $@ | aliasing.cpp:98:10:98:19 | call to user_input | call to user_input |
| aliasing.cpp:122:8:122:12 | access to array | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:122:8:122:12 | access to array | access to array flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
-| aliasing.cpp:127:8:127:16 | * ... | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:127:8:127:16 | * ... | * ... flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
| aliasing.cpp:132:8:132:14 | * ... | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:132:8:132:14 | * ... | * ... flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
| aliasing.cpp:137:8:137:11 | * ... | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:137:8:137:11 | * ... | * ... flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
-| aliasing.cpp:159:8:159:14 | * ... | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:159:8:159:14 | * ... | * ... flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
-| aliasing.cpp:165:8:165:16 | access to array | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:165:8:165:16 | access to array | access to array flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
| aliasing.cpp:176:13:176:14 | m1 | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:176:13:176:14 | m1 | m1 flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
| aliasing.cpp:189:15:189:16 | m1 | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:189:15:189:16 | m1 | m1 flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
| aliasing.cpp:201:15:201:16 | m1 | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:201:15:201:16 | m1 | m1 flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/bsd.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/bsd.cpp
index 4846420a5a5..fe138a8d8fc 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/bsd.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/bsd.cpp
@@ -19,6 +19,6 @@ void test_accept() {
int size = sizeof(sockaddr);
int a = accept(s, &addr, &size);
- sink(a); // $ ast=17:11 SPURIOUS: ast=18:12 MISSING: ir
- sink(addr); // $ ast MISSING: ir
+ sink(a); // $ ast=17:11 ir SPURIOUS: ast=18:12
+ sink(addr); // $ ast,ir
}
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 2b5ce44aca6..4444ea13267 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
@@ -6245,6 +6245,14 @@
| taint.cpp:657:12:657:15 | call to data | taint.cpp:657:3:657:8 | call to memcpy | |
| taint.cpp:657:20:657:25 | source | taint.cpp:657:3:657:8 | call to memcpy | TAINT |
| taint.cpp:657:20:657:25 | source | taint.cpp:657:12:657:15 | ref arg call to data | TAINT |
+| taint.cpp:668:14:668:14 | s | taint.cpp:669:18:669:18 | s | |
+| taint.cpp:668:14:668:14 | s | taint.cpp:671:7:671:7 | s | |
+| taint.cpp:668:14:668:14 | s | taint.cpp:672:7:672:7 | s | |
+| taint.cpp:668:14:668:14 | s | taint.cpp:673:7:673:7 | s | |
+| taint.cpp:669:18:669:18 | s [post update] | taint.cpp:671:7:671:7 | s | |
+| taint.cpp:669:18:669:18 | s [post update] | taint.cpp:672:7:672:7 | s | |
+| taint.cpp:669:18:669:18 | s [post update] | taint.cpp:673:7:673:7 | s | |
+| taint.cpp:672:7:672:7 | s [post update] | taint.cpp:673:7:673:7 | s | |
| vector.cpp:16:43:16:49 | source1 | vector.cpp:17:26:17:32 | source1 | |
| vector.cpp:16:43:16:49 | source1 | vector.cpp:31:38:31:44 | source1 | |
| vector.cpp:17:21:17:33 | call to vector | vector.cpp:19:14:19:14 | v | |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
index 6db77f37ac1..5ba2425300f 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
@@ -656,4 +656,19 @@ void test_with_const_member(char* source) {
C_const_member_function c;
memcpy(c.data(), source, 16);
sink(c.data()); // $ ast MISSING: ir
+}
+
+void argument_source(void*);
+
+struct two_members {
+ char *x, *y;
+};
+
+void test_argument_source_field_to_obj() {
+ two_members s;
+ argument_source(s.x);
+
+ sink(s); // $ SPURIOUS: ast
+ sink(s.x); // $ ast MISSING: ir
+ sink(s.y); // clean
}
\ No newline at end of file
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.ql b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.ql
index 4f5548bae41..6bb8e2a5a9b 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.ql
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.ql
@@ -53,6 +53,11 @@ module ASTTest {
or
// Track uninitialized variables
exists(source.asUninitialized())
+ or
+ exists(FunctionCall fc |
+ fc.getAnArgument() = source.asDefiningArgument() and
+ fc.getTarget().hasName("argument_source")
+ )
}
override predicate isSink(DataFlow::Node sink) {
@@ -80,6 +85,11 @@ module IRTest {
source.(DataFlow::ExprNode).getConvertedExpr().(FunctionCall).getTarget().getName() = "source"
or
source.asParameter().getName().matches("source%")
+ or
+ exists(FunctionCall fc |
+ fc.getAnArgument() = source.asDefiningArgument() and
+ fc.getTarget().hasName("argument_source")
+ )
}
override predicate isSink(DataFlow::Node sink) {
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 acdf71d5e15..a7dac56cc96 100644
--- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected
+++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected
@@ -3310,8 +3310,7 @@ ir.cpp:
# 585| mu585_8(unknown) = ^CallSideEffect : ~m?
# 585| v585_9(void) = ^BufferReadSideEffect[0] : &:r585_3, ~m?
# 585| v585_10(void) = ^BufferReadSideEffect[2] : &:r585_6, ~m?
-# 585| mu585_11(unknown) = ^BufferMayWriteSideEffect[0] : &:r585_3
-# 585| mu585_12(unknown) = ^BufferMayWriteSideEffect[2] : &:r585_6
+# 585| mu585_11(unknown) = ^BufferMayWriteSideEffect[2] : &:r585_6
# 586| v586_1(void) = NoOp :
# 584| v584_4(void) = ReturnVoid :
# 584| v584_5(void) = AliasedUse : ~m?
@@ -3365,7 +3364,6 @@ ir.cpp:
# 617| mu617_7(unknown) = ^CallSideEffect : ~m?
# 617| mu617_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r617_1
# 617| v617_9(void) = ^BufferReadSideEffect[0] : &:r617_5, ~m?
-# 617| mu617_10(unknown) = ^BufferMayWriteSideEffect[0] : &:r617_5
# 618| r618_1(glval) = VariableAddress[s3] :
# 618| r618_2(glval) = FunctionAddress[ReturnObject] :
# 618| r618_3(String) = Call[ReturnObject] : func:r618_2
@@ -3380,7 +3378,6 @@ ir.cpp:
# 619| mu619_7(unknown) = ^CallSideEffect : ~m?
# 619| mu619_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r619_1
# 619| v619_9(void) = ^BufferReadSideEffect[0] : &:r619_5, ~m?
-# 619| mu619_10(unknown) = ^BufferMayWriteSideEffect[0] : &:r619_5
# 620| v620_1(void) = NoOp :
# 615| v615_4(void) = ReturnVoid :
# 615| v615_5(void) = AliasedUse : ~m?
@@ -3409,7 +3406,6 @@ ir.cpp:
# 623| r623_6(char *) = Call[c_str] : func:r623_5, this:r623_4
# 623| mu623_7(unknown) = ^CallSideEffect : ~m?
# 623| v623_8(void) = ^BufferReadSideEffect[-1] : &:r623_4, ~m?
-# 623| mu623_9(String) = ^IndirectMayWriteSideEffect[-1] : &:r623_4
# 624| r624_1(glval) = VariableAddress[p] :
# 624| r624_2(String *) = Load[p] : &:r624_1, ~m?
# 624| r624_3(String *) = Convert : r624_2
@@ -3424,7 +3420,6 @@ ir.cpp:
# 625| r625_4(char *) = Call[c_str] : func:r625_3, this:r625_2
# 625| mu625_5(unknown) = ^CallSideEffect : ~m?
# 625| v625_6(void) = ^BufferReadSideEffect[-1] : &:r625_2, ~m?
-# 625| mu625_7(String) = ^IndirectMayWriteSideEffect[-1] : &:r625_2
# 626| v626_1(void) = NoOp :
# 622| v622_14(void) = ReturnIndirection[r] : &:r622_6, ~m?
# 622| v622_15(void) = ReturnIndirection[p] : &:r622_10, ~m?
@@ -3636,7 +3631,6 @@ ir.cpp:
# 662| mu662_6(unknown) = ^CallSideEffect : ~m?
# 662| mu662_7(String) = ^IndirectMayWriteSideEffect[-1] : &:r662_1
# 662| v662_8(void) = ^BufferReadSideEffect[0] : &:r662_4, ~m?
-# 662| mu662_9(unknown) = ^BufferMayWriteSideEffect[0] : &:r662_4
# 664| v664_1(void) = NoOp :
# 658| v658_8(void) = ReturnIndirection[#this] : &:r658_6, ~m?
# 658| v658_9(void) = ReturnVoid :
@@ -3933,8 +3927,7 @@ ir.cpp:
# 731| mu731_17(unknown) = ^CallSideEffect : ~m?
# 731| mu731_18(String) = ^IndirectMayWriteSideEffect[-1] : &:r731_11
# 731| v731_19(void) = ^BufferReadSideEffect[0] : &:r731_15, ~m?
-# 731| mu731_20(unknown) = ^BufferMayWriteSideEffect[0] : &:r731_15
-# 731| v731_21(void) = ThrowValue : &:r731_11, ~m?
+# 731| v731_20(void) = ThrowValue : &:r731_11, ~m?
#-----| Exception -> Block 9
# 733| Block 8
@@ -3962,8 +3955,7 @@ ir.cpp:
# 736| mu736_7(unknown) = ^CallSideEffect : ~m?
# 736| mu736_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r736_1
# 736| v736_9(void) = ^BufferReadSideEffect[0] : &:r736_5, ~m?
-# 736| mu736_10(unknown) = ^BufferMayWriteSideEffect[0] : &:r736_5
-# 736| v736_11(void) = ThrowValue : &:r736_1, ~m?
+# 736| v736_10(void) = ThrowValue : &:r736_1, ~m?
#-----| Exception -> Block 2
# 738| Block 11
@@ -4017,16 +4009,15 @@ ir.cpp:
# 745| v745_18(void) = ^BufferReadSideEffect[-1] : &:r745_11, ~m?
#-----| v0_7(void) = ^BufferReadSideEffect[0] : &:r0_6, ~m?
# 745| mu745_19(String) = ^IndirectMayWriteSideEffect[-1] : &:r745_11
-#-----| mu0_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_6
-#-----| r0_9(glval) = CopyValue : r745_16
-#-----| r0_10(glval ) = VariableAddress[#return] :
-#-----| r0_11(glval) = VariableAddress[#this] :
-#-----| r0_12(Base *) = Load[#this] : &:r0_11, ~m?
-#-----| r0_13(glval ) = CopyValue : r0_12
-#-----| r0_14(Base &) = CopyValue : r0_13
-#-----| mu0_15(Base &) = Store[#return] : &:r0_10, r0_14
+#-----| r0_8(glval) = CopyValue : r745_16
+#-----| r0_9(glval ) = VariableAddress[#return] :
+#-----| r0_10(glval) = VariableAddress[#this] :
+#-----| r0_11(Base *) = Load[#this] : &:r0_10, ~m?
+#-----| r0_12(glval ) = CopyValue : r0_11
+#-----| r0_13(Base &) = CopyValue : r0_12
+#-----| mu0_14(Base &) = Store[#return] : &:r0_9, r0_13
# 745| v745_20(void) = ReturnIndirection[#this] : &:r745_6, ~m?
-#-----| v0_16(void) = ReturnIndirection[(unnamed parameter 0)] : &:r0_3, ~m?
+#-----| v0_15(void) = ReturnIndirection[(unnamed parameter 0)] : &:r0_3, ~m?
# 745| r745_21(glval ) = VariableAddress[#return] :
# 745| v745_22(void) = ReturnValue : &:r745_21, ~m?
# 745| v745_23(void) = AliasedUse : ~m?
@@ -4125,8 +4116,7 @@ ir.cpp:
#-----| v0_9(void) = ^BufferReadSideEffect[-1] : &:r0_5, ~m?
#-----| v0_10(void) = ^BufferReadSideEffect[0] : &:r0_8, ~m?
#-----| mu0_11(Base) = ^IndirectMayWriteSideEffect[-1] : &:r0_5
-#-----| mu0_12(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_8
-#-----| r0_13(glval ) = CopyValue : r754_15
+#-----| r0_12(glval ) = CopyValue : r754_15
# 754| r754_17(glval) = VariableAddress[#this] :
# 754| r754_18(Middle *) = Load[#this] : &:r754_17, ~m?
# 754| r754_19(glval) = FieldAddress[middle_s] : r754_18
@@ -4134,24 +4124,23 @@ ir.cpp:
# 754| r754_21(glval) = FunctionAddress[operator=] :
# 754| r754_22(glval) = VariableAddress[(unnamed parameter 0)] :
# 754| r754_23(Middle &) = Load[(unnamed parameter 0)] : &:r754_22, ~m?
-#-----| r0_14(glval) = CopyValue : r754_23
-# 754| r754_24(glval) = FieldAddress[middle_s] : r0_14
-#-----| r0_15(String &) = CopyValue : r754_24
-# 754| r754_25(String &) = Call[operator=] : func:r754_21, this:r754_20, 0:r0_15
+#-----| r0_13(glval) = CopyValue : r754_23
+# 754| r754_24(glval) = FieldAddress[middle_s] : r0_13
+#-----| r0_14(String &) = CopyValue : r754_24
+# 754| r754_25(String &) = Call[operator=] : func:r754_21, this:r754_20, 0:r0_14
# 754| mu754_26(unknown) = ^CallSideEffect : ~m?
# 754| v754_27(void) = ^BufferReadSideEffect[-1] : &:r754_20, ~m?
-#-----| v0_16(void) = ^BufferReadSideEffect[0] : &:r0_15, ~m?
+#-----| v0_15(void) = ^BufferReadSideEffect[0] : &:r0_14, ~m?
# 754| mu754_28(String) = ^IndirectMayWriteSideEffect[-1] : &:r754_20
-#-----| mu0_17(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_15
-#-----| r0_18(glval) = CopyValue : r754_25
-#-----| r0_19(glval) = VariableAddress[#return] :
-#-----| r0_20(glval) = VariableAddress[#this] :
-#-----| r0_21(Middle *) = Load[#this] : &:r0_20, ~m?
-#-----| r0_22(glval) = CopyValue : r0_21
-#-----| r0_23(Middle &) = CopyValue : r0_22
-#-----| mu0_24(Middle &) = Store[#return] : &:r0_19, r0_23
+#-----| r0_16(glval) = CopyValue : r754_25
+#-----| r0_17(glval) = VariableAddress[#return] :
+#-----| r0_18(glval) = VariableAddress[#this] :
+#-----| r0_19(Middle *) = Load[#this] : &:r0_18, ~m?
+#-----| r0_20(glval) = CopyValue : r0_19
+#-----| r0_21(Middle &) = CopyValue : r0_20
+#-----| mu0_22(Middle &) = Store[#return] : &:r0_17, r0_21
# 754| v754_29(void) = ReturnIndirection[#this] : &:r754_6, ~m?
-#-----| v0_25(void) = ReturnIndirection[(unnamed parameter 0)] : &:r0_3, ~m?
+#-----| v0_23(void) = ReturnIndirection[(unnamed parameter 0)] : &:r0_3, ~m?
# 754| r754_30(glval) = VariableAddress[#return] :
# 754| v754_31(void) = ReturnValue : &:r754_30, ~m?
# 754| v754_32(void) = AliasedUse : ~m?
@@ -4234,8 +4223,7 @@ ir.cpp:
#-----| v0_9(void) = ^BufferReadSideEffect[-1] : &:r0_5, ~m?
#-----| v0_10(void) = ^BufferReadSideEffect[0] : &:r0_8, ~m?
#-----| mu0_11(Middle) = ^IndirectMayWriteSideEffect[-1] : &:r0_5
-#-----| mu0_12(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_8
-#-----| r0_13(glval) = CopyValue : r763_15
+#-----| r0_12(glval) = CopyValue : r763_15
# 763| r763_17(glval) = VariableAddress[#this] :
# 763| r763_18(Derived *) = Load[#this] : &:r763_17, ~m?
# 763| r763_19(glval) = FieldAddress[derived_s] : r763_18
@@ -4243,24 +4231,23 @@ ir.cpp:
# 763| r763_21(glval) = FunctionAddress[operator=] :
# 763| r763_22(glval) = VariableAddress[(unnamed parameter 0)] :
# 763| r763_23(Derived &) = Load[(unnamed parameter 0)] : &:r763_22, ~m?
-#-----| r0_14(glval) = CopyValue : r763_23
-# 763| r763_24(glval) = FieldAddress[derived_s] : r0_14
-#-----| r0_15(String &) = CopyValue : r763_24
-# 763| r763_25(String &) = Call[operator=] : func:r763_21, this:r763_20, 0:r0_15
+#-----| r0_13(glval) = CopyValue : r763_23
+# 763| r763_24(glval) = FieldAddress[derived_s] : r0_13
+#-----| r0_14(String &) = CopyValue : r763_24
+# 763| r763_25(String &) = Call[operator=] : func:r763_21, this:r763_20, 0:r0_14
# 763| mu763_26(unknown) = ^CallSideEffect : ~m?
# 763| v763_27(void) = ^BufferReadSideEffect[-1] : &:r763_20, ~m?
-#-----| v0_16(void) = ^BufferReadSideEffect[0] : &:r0_15, ~m?
+#-----| v0_15(void) = ^BufferReadSideEffect[0] : &:r0_14, ~m?
# 763| mu763_28(String) = ^IndirectMayWriteSideEffect[-1] : &:r763_20
-#-----| mu0_17(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_15
-#-----| r0_18(glval) = CopyValue : r763_25
-#-----| r0_19(glval) = VariableAddress[#return] :
-#-----| r0_20(glval) = VariableAddress[#this] :
-#-----| r0_21(Derived *) = Load[#this] : &:r0_20, ~m?
-#-----| r0_22(glval) = CopyValue : r0_21
-#-----| r0_23(Derived &) = CopyValue : r0_22
-#-----| mu0_24(Derived &) = Store[#return] : &:r0_19, r0_23
+#-----| r0_16(glval) = CopyValue : r763_25
+#-----| r0_17(glval) = VariableAddress[#return] :
+#-----| r0_18(glval) = VariableAddress[#this] :
+#-----| r0_19(Derived *) = Load[#this] : &:r0_18, ~m?
+#-----| r0_20(glval) = CopyValue : r0_19
+#-----| r0_21(Derived &) = CopyValue : r0_20
+#-----| mu0_22(Derived &) = Store[#return] : &:r0_17, r0_21
# 763| v763_29(void) = ReturnIndirection[#this] : &:r763_6, ~m?
-#-----| v0_25(void) = ReturnIndirection[(unnamed parameter 0)] : &:r0_3, ~m?
+#-----| v0_23(void) = ReturnIndirection[(unnamed parameter 0)] : &:r0_3, ~m?
# 763| r763_30(glval) = VariableAddress[#return] :
# 763| v763_31(void) = ReturnValue : &:r763_30, ~m?
# 763| v763_32(void) = AliasedUse : ~m?
@@ -4521,8 +4508,7 @@ ir.cpp:
# 808| v808_8(void) = ^BufferReadSideEffect[-1] : &:r808_1, ~m?
# 808| v808_9(void) = ^BufferReadSideEffect[0] : &:r808_5, ~m?
# 808| mu808_10(Base) = ^IndirectMayWriteSideEffect[-1] : &:r808_1
-# 808| mu808_11(unknown) = ^BufferMayWriteSideEffect[0] : &:r808_5
-# 808| r808_12(glval ) = CopyValue : r808_6
+# 808| r808_11(glval ) = CopyValue : r808_6
# 809| r809_1(glval ) = VariableAddress[b] :
# 809| r809_2(glval) = FunctionAddress[operator=] :
# 809| r809_3(glval ) = VariableAddress[#temp809:7] :
@@ -4535,16 +4521,14 @@ ir.cpp:
# 809| mu809_10(unknown) = ^CallSideEffect : ~m?
# 809| mu809_11(Base) = ^IndirectMayWriteSideEffect[-1] : &:r809_3
# 809| v809_12(void) = ^BufferReadSideEffect[0] : &:r809_8, ~m?
-# 809| mu809_13(unknown) = ^BufferMayWriteSideEffect[0] : &:r809_8
-# 809| r809_14(glval ) = Convert : r809_3
-# 809| r809_15(Base &) = CopyValue : r809_14
-# 809| r809_16(Base &) = Call[operator=] : func:r809_2, this:r809_1, 0:r809_15
-# 809| mu809_17(unknown) = ^CallSideEffect : ~m?
-# 809| v809_18(void) = ^BufferReadSideEffect[-1] : &:r809_1, ~m?
-# 809| v809_19(void) = ^BufferReadSideEffect[0] : &:r809_15, ~m?
-# 809| mu809_20(Base) = ^IndirectMayWriteSideEffect[-1] : &:r809_1
-# 809| mu809_21(unknown) = ^BufferMayWriteSideEffect[0] : &:r809_15
-# 809| r809_22(glval ) = CopyValue : r809_16
+# 809| r809_13(glval ) = Convert : r809_3
+# 809| r809_14(Base &) = CopyValue : r809_13
+# 809| r809_15(Base &) = Call[operator=] : func:r809_2, this:r809_1, 0:r809_14
+# 809| mu809_16(unknown) = ^CallSideEffect : ~m?
+# 809| v809_17(void) = ^BufferReadSideEffect[-1] : &:r809_1, ~m?
+# 809| v809_18(void) = ^BufferReadSideEffect[0] : &:r809_14, ~m?
+# 809| mu809_19(Base) = ^IndirectMayWriteSideEffect[-1] : &:r809_1
+# 809| r809_20(glval ) = CopyValue : r809_15
# 810| r810_1(glval ) = VariableAddress[b] :
# 810| r810_2(glval) = FunctionAddress[operator=] :
# 810| r810_3(glval ) = VariableAddress[#temp810:7] :
@@ -4557,16 +4541,14 @@ ir.cpp:
# 810| mu810_10(unknown) = ^CallSideEffect : ~m?
# 810| mu810_11(Base) = ^IndirectMayWriteSideEffect[-1] : &:r810_3
# 810| v810_12(void) = ^BufferReadSideEffect[0] : &:r810_8, ~m?
-# 810| mu810_13(unknown) = ^BufferMayWriteSideEffect[0] : &:r810_8
-# 810| r810_14(glval ) = Convert : r810_3
-# 810| r810_15(Base &) = CopyValue : r810_14
-# 810| r810_16(Base &) = Call[operator=] : func:r810_2, this:r810_1, 0:r810_15
-# 810| mu810_17(unknown) = ^CallSideEffect : ~m?
-# 810| v810_18(void) = ^BufferReadSideEffect[-1] : &:r810_1, ~m?
-# 810| v810_19(void) = ^BufferReadSideEffect[0] : &:r810_15, ~m?
-# 810| mu810_20(Base) = ^IndirectMayWriteSideEffect[-1] : &:r810_1
-# 810| mu810_21(unknown) = ^BufferMayWriteSideEffect[0] : &:r810_15
-# 810| r810_22(glval ) = CopyValue : r810_16
+# 810| r810_13(glval ) = Convert : r810_3
+# 810| r810_14(Base &) = CopyValue : r810_13
+# 810| r810_15(Base &) = Call[operator=] : func:r810_2, this:r810_1, 0:r810_14
+# 810| mu810_16(unknown) = ^CallSideEffect : ~m?
+# 810| v810_17(void) = ^BufferReadSideEffect[-1] : &:r810_1, ~m?
+# 810| v810_18(void) = ^BufferReadSideEffect[0] : &:r810_14, ~m?
+# 810| mu810_19(Base) = ^IndirectMayWriteSideEffect[-1] : &:r810_1
+# 810| r810_20(glval ) = CopyValue : r810_15
# 811| r811_1(glval) = VariableAddress[pm] :
# 811| r811_2(Middle *) = Load[pm] : &:r811_1, ~m?
# 811| r811_3(Base *) = ConvertToNonVirtualBase[Middle : Base] : r811_2
@@ -4598,8 +4580,7 @@ ir.cpp:
# 816| v816_9(void) = ^BufferReadSideEffect[-1] : &:r816_1, ~m?
# 816| v816_10(void) = ^BufferReadSideEffect[0] : &:r816_6, ~m?
# 816| mu816_11(Middle) = ^IndirectMayWriteSideEffect[-1] : &:r816_1
-# 816| mu816_12(unknown) = ^BufferMayWriteSideEffect[0] : &:r816_6
-# 816| r816_13(glval) = CopyValue : r816_7
+# 816| r816_12(glval) = CopyValue : r816_7
# 817| r817_1(glval) = VariableAddress[m] :
# 817| r817_2(glval) = FunctionAddress[operator=] :
# 817| r817_3(glval ) = VariableAddress[b] :
@@ -4611,8 +4592,7 @@ ir.cpp:
# 817| v817_9(void) = ^BufferReadSideEffect[-1] : &:r817_1, ~m?
# 817| v817_10(void) = ^BufferReadSideEffect[0] : &:r817_6, ~m?
# 817| mu817_11(Middle) = ^IndirectMayWriteSideEffect[-1] : &:r817_1
-# 817| mu817_12(unknown) = ^BufferMayWriteSideEffect[0] : &:r817_6
-# 817| r817_13(glval) = CopyValue : r817_7
+# 817| r817_12(glval) = CopyValue : r817_7
# 818| r818_1(glval ) = VariableAddress[pb] :
# 818| r818_2(Base *) = Load[pb] : &:r818_1, ~m?
# 818| r818_3(Middle *) = ConvertToDerived[Middle : Base] : r818_2
@@ -4639,8 +4619,7 @@ ir.cpp:
# 822| v822_9(void) = ^BufferReadSideEffect[-1] : &:r822_1, ~m?
# 822| v822_10(void) = ^BufferReadSideEffect[0] : &:r822_6, ~m?
# 822| mu822_11(Base) = ^IndirectMayWriteSideEffect[-1] : &:r822_1
-# 822| mu822_12(unknown) = ^BufferMayWriteSideEffect[0] : &:r822_6
-# 822| r822_13(glval ) = CopyValue : r822_7
+# 822| r822_12(glval ) = CopyValue : r822_7
# 823| r823_1(glval ) = VariableAddress[b] :
# 823| r823_2(glval) = FunctionAddress[operator=] :
# 823| r823_3(glval ) = VariableAddress[#temp823:7] :
@@ -4654,16 +4633,14 @@ ir.cpp:
# 823| mu823_11(unknown) = ^CallSideEffect : ~m?
# 823| mu823_12(Base) = ^IndirectMayWriteSideEffect[-1] : &:r823_3
# 823| v823_13(void) = ^BufferReadSideEffect[0] : &:r823_9, ~m?
-# 823| mu823_14(unknown) = ^BufferMayWriteSideEffect[0] : &:r823_9
-# 823| r823_15(glval ) = Convert : r823_3
-# 823| r823_16(Base &) = CopyValue : r823_15
-# 823| r823_17(Base &) = Call[operator=] : func:r823_2, this:r823_1, 0:r823_16
-# 823| mu823_18(unknown) = ^CallSideEffect : ~m?
-# 823| v823_19(void) = ^BufferReadSideEffect[-1] : &:r823_1, ~m?
-# 823| v823_20(void) = ^BufferReadSideEffect[0] : &:r823_16, ~m?
-# 823| mu823_21(Base) = ^IndirectMayWriteSideEffect[-1] : &:r823_1
-# 823| mu823_22(unknown) = ^BufferMayWriteSideEffect[0] : &:r823_16
-# 823| r823_23(glval ) = CopyValue : r823_17
+# 823| r823_14(glval ) = Convert : r823_3
+# 823| r823_15(Base &) = CopyValue : r823_14
+# 823| r823_16(Base &) = Call[operator=] : func:r823_2, this:r823_1, 0:r823_15
+# 823| mu823_17(unknown) = ^CallSideEffect : ~m?
+# 823| v823_18(void) = ^BufferReadSideEffect[-1] : &:r823_1, ~m?
+# 823| v823_19(void) = ^BufferReadSideEffect[0] : &:r823_15, ~m?
+# 823| mu823_20(Base) = ^IndirectMayWriteSideEffect[-1] : &:r823_1
+# 823| r823_21(glval ) = CopyValue : r823_16
# 824| r824_1(glval ) = VariableAddress[b] :
# 824| r824_2(glval) = FunctionAddress[operator=] :
# 824| r824_3(glval ) = VariableAddress[#temp824:7] :
@@ -4677,16 +4654,14 @@ ir.cpp:
# 824| mu824_11(unknown) = ^CallSideEffect : ~m?
# 824| mu824_12(Base) = ^IndirectMayWriteSideEffect[-1] : &:r824_3
# 824| v824_13(void) = ^BufferReadSideEffect[0] : &:r824_9, ~m?
-# 824| mu824_14(unknown) = ^BufferMayWriteSideEffect[0] : &:r824_9
-# 824| r824_15(glval ) = Convert : r824_3
-# 824| r824_16(Base &) = CopyValue : r824_15
-# 824| r824_17(Base &) = Call[operator=] : func:r824_2, this:r824_1, 0:r824_16
-# 824| mu824_18(unknown) = ^CallSideEffect : ~m?
-# 824| v824_19(void) = ^BufferReadSideEffect[-1] : &:r824_1, ~m?
-# 824| v824_20(void) = ^BufferReadSideEffect[0] : &:r824_16, ~m?
-# 824| mu824_21(Base) = ^IndirectMayWriteSideEffect[-1] : &:r824_1
-# 824| mu824_22(unknown) = ^BufferMayWriteSideEffect[0] : &:r824_16
-# 824| r824_23(glval ) = CopyValue : r824_17
+# 824| r824_14(glval ) = Convert : r824_3
+# 824| r824_15(Base &) = CopyValue : r824_14
+# 824| r824_16(Base &) = Call[operator=] : func:r824_2, this:r824_1, 0:r824_15
+# 824| mu824_17(unknown) = ^CallSideEffect : ~m?
+# 824| v824_18(void) = ^BufferReadSideEffect[-1] : &:r824_1, ~m?
+# 824| v824_19(void) = ^BufferReadSideEffect[0] : &:r824_15, ~m?
+# 824| mu824_20(Base) = ^IndirectMayWriteSideEffect[-1] : &:r824_1
+# 824| r824_21(glval ) = CopyValue : r824_16
# 825| r825_1(glval) = VariableAddress[pd] :
# 825| r825_2(Derived *) = Load[pd] : &:r825_1, ~m?
# 825| r825_3(Middle *) = ConvertToNonVirtualBase[Derived : Middle] : r825_2
@@ -4722,8 +4697,7 @@ ir.cpp:
# 830| v830_10(void) = ^BufferReadSideEffect[-1] : &:r830_1, ~m?
# 830| v830_11(void) = ^BufferReadSideEffect[0] : &:r830_7, ~m?
# 830| mu830_12(Derived) = ^IndirectMayWriteSideEffect[-1] : &:r830_1
-# 830| mu830_13(unknown) = ^BufferMayWriteSideEffect[0] : &:r830_7
-# 830| r830_14(glval) = CopyValue : r830_8
+# 830| r830_13(glval) = CopyValue : r830_8
# 831| r831_1(glval) = VariableAddress[d] :
# 831| r831_2(glval) = FunctionAddress[operator=] :
# 831| r831_3(glval ) = VariableAddress[b] :
@@ -4736,8 +4710,7 @@ ir.cpp:
# 831| v831_10(void) = ^BufferReadSideEffect[-1] : &:r831_1, ~m?
# 831| v831_11(void) = ^BufferReadSideEffect[0] : &:r831_7, ~m?
# 831| mu831_12(Derived) = ^IndirectMayWriteSideEffect[-1] : &:r831_1
-# 831| mu831_13(unknown) = ^BufferMayWriteSideEffect[0] : &:r831_7
-# 831| r831_14(glval) = CopyValue : r831_8
+# 831| r831_13(glval) = CopyValue : r831_8
# 832| r832_1(glval ) = VariableAddress[pb] :
# 832| r832_2(Base *) = Load[pb] : &:r832_1, ~m?
# 832| r832_3(Middle *) = ConvertToDerived[Middle : Base] : r832_2
@@ -4906,7 +4879,6 @@ ir.cpp:
# 868| mu868_5(unknown) = ^CallSideEffect : ~m?
# 868| mu868_6(String) = ^IndirectMayWriteSideEffect[-1] : &:mu867_5
# 868| v868_7(void) = ^BufferReadSideEffect[0] : &:r868_3, ~m?
-# 868| mu868_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r868_3
# 869| v869_1(void) = NoOp :
# 867| v867_8(void) = ReturnIndirection[#this] : &:r867_6, ~m?
# 867| v867_9(void) = ReturnVoid :
@@ -5208,7 +5180,6 @@ ir.cpp:
# 954| mu954_12(unknown) = ^CallSideEffect : ~m?
# 954| mu954_13(String) = ^IndirectMayWriteSideEffect[-1] : &:r954_7
# 954| v954_14(void) = ^BufferReadSideEffect[0] : &:r954_10, ~m?
-# 954| mu954_15(unknown) = ^BufferMayWriteSideEffect[0] : &:r954_10
# 955| r955_1(glval) = FunctionAddress[operator new] :
# 955| r955_2(unsigned long) = Constant[256] :
# 955| r955_3(align_val_t) = Constant[128] :
@@ -5718,7 +5689,6 @@ ir.cpp:
# 1044| r1044_5(char) = Call[operator()] : func:r1044_3, this:r1044_2, 0:r1044_4
# 1044| mu1044_6(unknown) = ^CallSideEffect : ~m?
# 1044| v1044_7(void) = ^BufferReadSideEffect[-1] : &:r1044_2, ~m?
-# 1044| mu1044_8(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r1044_2
# 1045| r1045_1(glval) = VariableAddress[lambda_val] :
# 1045| r1045_2(glval) = VariableAddress[#temp1045:20] :
# 1045| mu1045_3(decltype([...](...){...})) = Uninitialized[#temp1045:20] : &:r1045_2
@@ -5740,7 +5710,6 @@ ir.cpp:
# 1046| r1046_5(char) = Call[operator()] : func:r1046_3, this:r1046_2, 0:r1046_4
# 1046| mu1046_6(unknown) = ^CallSideEffect : ~m?
# 1046| v1046_7(void) = ^BufferReadSideEffect[-1] : &:r1046_2, ~m?
-# 1046| mu1046_8(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r1046_2
# 1047| r1047_1(glval) = VariableAddress[lambda_ref_explicit] :
# 1047| r1047_2(glval) = VariableAddress[#temp1047:29] :
# 1047| mu1047_3(decltype([...](...){...})) = Uninitialized[#temp1047:29] : &:r1047_2
@@ -5759,7 +5728,6 @@ ir.cpp:
# 1048| r1048_5(char) = Call[operator()] : func:r1048_3, this:r1048_2, 0:r1048_4
# 1048| mu1048_6(unknown) = ^CallSideEffect : ~m?
# 1048| v1048_7(void) = ^BufferReadSideEffect[-1] : &:r1048_2, ~m?
-# 1048| mu1048_8(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r1048_2
# 1049| r1049_1(glval) = VariableAddress[lambda_val_explicit] :
# 1049| r1049_2(glval) = VariableAddress[#temp1049:29] :
# 1049| mu1049_3(decltype([...](...){...})) = Uninitialized[#temp1049:29] : &:r1049_2
@@ -5777,7 +5745,6 @@ ir.cpp:
# 1050| r1050_5(char) = Call[operator()] : func:r1050_3, this:r1050_2, 0:r1050_4
# 1050| mu1050_6(unknown) = ^CallSideEffect : ~m?
# 1050| v1050_7(void) = ^BufferReadSideEffect[-1] : &:r1050_2, ~m?
-# 1050| mu1050_8(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r1050_2
# 1051| r1051_1(glval) = VariableAddress[lambda_mixed_explicit] :
# 1051| r1051_2(glval) = VariableAddress[#temp1051:31] :
# 1051| mu1051_3(decltype([...](...){...})) = Uninitialized[#temp1051:31] : &:r1051_2
@@ -5800,7 +5767,6 @@ ir.cpp:
# 1052| r1052_5(char) = Call[operator()] : func:r1052_3, this:r1052_2, 0:r1052_4
# 1052| mu1052_6(unknown) = ^CallSideEffect : ~m?
# 1052| v1052_7(void) = ^BufferReadSideEffect[-1] : &:r1052_2, ~m?
-# 1052| mu1052_8(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r1052_2
# 1053| r1053_1(glval) = VariableAddress[r] :
# 1053| r1053_2(glval) = VariableAddress[x] :
# 1053| r1053_3(int) = Load[x] : &:r1053_2, ~m?
@@ -5839,7 +5805,6 @@ ir.cpp:
# 1055| r1055_5(char) = Call[operator()] : func:r1055_3, this:r1055_2, 0:r1055_4
# 1055| mu1055_6(unknown) = ^CallSideEffect : ~m?
# 1055| v1055_7(void) = ^BufferReadSideEffect[-1] : &:r1055_2, ~m?
-# 1055| mu1055_8(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r1055_2
# 1056| v1056_1(void) = NoOp :
# 1040| v1040_10(void) = ReturnIndirection[s] : &:r1040_8, ~m?
# 1040| v1040_11(void) = ReturnVoid :
@@ -5886,39 +5851,38 @@ ir.cpp:
# 1043| char (void Lambda(int, String const&))::(lambda [] type at line 1043, col. 21)::operator()(float) const
# 1043| Block 0
-# 1043| v1043_1(void) = EnterFunction :
-# 1043| mu1043_2(unknown) = AliasedDefinition :
-# 1043| mu1043_3(unknown) = InitializeNonLocal :
-# 1043| r1043_4(glval) = VariableAddress[#this] :
-# 1043| mu1043_5(glval) = InitializeParameter[#this] : &:r1043_4
-# 1043| r1043_6(glval) = Load[#this] : &:r1043_4, ~m?
-# 1043| mu1043_7(decltype([...](...){...})) = InitializeIndirection[#this] : &:r1043_6
-# 1043| r1043_8(glval) = VariableAddress[f] :
-# 1043| mu1043_9(float) = InitializeParameter[f] : &:r1043_8
-# 1043| r1043_10(glval) = VariableAddress[#return] :
-# 1043| r1043_11(glval) = VariableAddress[#this] :
-# 1043| r1043_12(lambda [] type at line 1043, col. 21 *) = Load[#this] : &:r1043_11, ~m?
-# 1043| r1043_13(glval) = FieldAddress[s] : r1043_12
-# 1043| r1043_14(String &) = Load[?] : &:r1043_13, ~m?
-# 1043| r1043_15(glval) = CopyValue : r1043_14
-# 1043| r1043_16(glval) = FunctionAddress[c_str] :
-# 1043| r1043_17(char *) = Call[c_str] : func:r1043_16, this:r1043_15
-# 1043| mu1043_18(unknown) = ^CallSideEffect : ~m?
-# 1043| v1043_19(void) = ^BufferReadSideEffect[-1] : &:r1043_15, ~m?
-# 1043| mu1043_20(String) = ^IndirectMayWriteSideEffect[-1] : &:r1043_15
-# 1043| r1043_21(glval) = VariableAddress[#this] :
-# 1043| r1043_22(lambda [] type at line 1043, col. 21 *) = Load[#this] : &:r1043_21, ~m?
-# 1043| r1043_23(glval) = FieldAddress[x] : r1043_22
-# 1043| r1043_24(int &) = Load[?] : &:r1043_23, ~m?
-# 1043| r1043_25(int) = Load[?] : &:r1043_24, ~m?
-# 1043| r1043_26(glval) = PointerAdd[1] : r1043_17, r1043_25
-# 1043| r1043_27(char) = Load[?] : &:r1043_26, ~m?
-# 1043| mu1043_28(char) = Store[#return] : &:r1043_10, r1043_27
-# 1043| v1043_29(void) = ReturnIndirection[#this] : &:r1043_6, ~m?
-# 1043| r1043_30(glval) = VariableAddress[#return] :
-# 1043| v1043_31(void) = ReturnValue : &:r1043_30, ~m?
-# 1043| v1043_32(void) = AliasedUse : ~m?
-# 1043| v1043_33(void) = ExitFunction :
+# 1043| v1043_1(void) = EnterFunction :
+# 1043| mu1043_2(unknown) = AliasedDefinition :
+# 1043| mu1043_3(unknown) = InitializeNonLocal :
+# 1043| r1043_4(glval) = VariableAddress[#this] :
+# 1043| mu1043_5(glval) = InitializeParameter[#this] : &:r1043_4
+# 1043| r1043_6(glval) = Load[#this] : &:r1043_4, ~m?
+# 1043| mu1043_7(decltype([...](...){...})) = InitializeIndirection[#this] : &:r1043_6
+# 1043| r1043_8(glval) = VariableAddress[f] :
+# 1043| mu1043_9(float) = InitializeParameter[f] : &:r1043_8
+# 1043| r1043_10(glval) = VariableAddress[#return] :
+# 1043| r1043_11(glval) = VariableAddress[#this] :
+# 1043| r1043_12(lambda [] type at line 1043, col. 21 *) = Load[#this] : &:r1043_11, ~m?
+# 1043| r1043_13(glval) = FieldAddress[s] : r1043_12
+# 1043| r1043_14(String &) = Load[?] : &:r1043_13, ~m?
+# 1043| r1043_15(glval) = CopyValue : r1043_14
+# 1043| r1043_16(glval) = FunctionAddress[c_str] :
+# 1043| r1043_17(char *) = Call[c_str] : func:r1043_16, this:r1043_15
+# 1043| mu1043_18(unknown) = ^CallSideEffect : ~m?
+# 1043| v1043_19(void) = ^BufferReadSideEffect[-1] : &:r1043_15, ~m?
+# 1043| r1043_20(glval) = VariableAddress[#this] :
+# 1043| r1043_21(lambda [] type at line 1043, col. 21 *) = Load[#this] : &:r1043_20, ~m?
+# 1043| r1043_22(glval) = FieldAddress[x] : r1043_21
+# 1043| r1043_23(int &) = Load[?] : &:r1043_22, ~m?
+# 1043| r1043_24(int) = Load[?] : &:r1043_23, ~m?
+# 1043| r1043_25(glval) = PointerAdd[1] : r1043_17, r1043_24
+# 1043| r1043_26(char) = Load[?] : &:r1043_25, ~m?
+# 1043| mu1043_27(char) = Store[#return] : &:r1043_10, r1043_26
+# 1043| v1043_28(void) = ReturnIndirection[#this] : &:r1043_6, ~m?
+# 1043| r1043_29(glval) = VariableAddress[#return] :
+# 1043| v1043_30(void) = ReturnValue : &:r1043_29, ~m?
+# 1043| v1043_31(void) = AliasedUse : ~m?
+# 1043| v1043_32(void) = ExitFunction :
# 1045| void (void Lambda(int, String const&))::(lambda [] type at line 1045, col. 21)::~()
# 1045| Block 0
@@ -5941,68 +5905,66 @@ ir.cpp:
# 1045| char (void Lambda(int, String const&))::(lambda [] type at line 1045, col. 21)::operator()(float) const
# 1045| Block 0
-# 1045| v1045_1(void) = EnterFunction :
-# 1045| mu1045_2(unknown) = AliasedDefinition :
-# 1045| mu1045_3(unknown) = InitializeNonLocal :
-# 1045| r1045_4(glval) = VariableAddress[#this] :
-# 1045| mu1045_5(glval) = InitializeParameter[#this] : &:r1045_4
-# 1045| r1045_6(glval) = Load[#this] : &:r1045_4, ~m?
-# 1045| mu1045_7(decltype([...](...){...})) = InitializeIndirection[#this] : &:r1045_6
-# 1045| r1045_8(glval) = VariableAddress[f] :
-# 1045| mu1045_9(float) = InitializeParameter[f] : &:r1045_8
-# 1045| r1045_10(glval) = VariableAddress[#return] :
-# 1045| r1045_11(glval) = VariableAddress[#this] :
-# 1045| r1045_12(lambda [] type at line 1045, col. 21 *) = Load[#this] : &:r1045_11, ~m?
-# 1045| r1045_13(glval) = FieldAddress[s] : r1045_12
-# 1045| r1045_14(glval) = FunctionAddress[c_str] :
-# 1045| r1045_15(char *) = Call[c_str] : func:r1045_14, this:r1045_13
-# 1045| mu1045_16(unknown) = ^CallSideEffect : ~m?
-# 1045| v1045_17(void) = ^BufferReadSideEffect[-1] : &:r1045_13, ~m?
-# 1045| mu1045_18(String) = ^IndirectMayWriteSideEffect[-1] : &:r1045_13
-# 1045| r1045_19(glval) = VariableAddress[#this] :
-# 1045| r1045_20(lambda [] type at line 1045, col. 21 *) = Load[#this] : &:r1045_19, ~m?
-# 1045| r1045_21(glval) = FieldAddress[x] : r1045_20
-# 1045| r1045_22(int) = Load[?] : &:r1045_21, ~m?
-# 1045| r1045_23(glval) = PointerAdd[1] : r1045_15, r1045_22
-# 1045| r1045_24(char) = Load[?] : &:r1045_23, ~m?
-# 1045| mu1045_25(char) = Store[#return] : &:r1045_10, r1045_24
-# 1045| v1045_26(void) = ReturnIndirection[#this] : &:r1045_6, ~m?
-# 1045| r1045_27(glval) = VariableAddress[#return] :
-# 1045| v1045_28(void) = ReturnValue : &:r1045_27, ~m?
-# 1045| v1045_29(void) = AliasedUse : ~m?
-# 1045| v1045_30(void) = ExitFunction :
+# 1045| v1045_1(void) = EnterFunction :
+# 1045| mu1045_2(unknown) = AliasedDefinition :
+# 1045| mu1045_3(unknown) = InitializeNonLocal :
+# 1045| r1045_4(glval) = VariableAddress[#this] :
+# 1045| mu1045_5(glval) = InitializeParameter[#this] : &:r1045_4
+# 1045| r1045_6(glval) = Load[#this] : &:r1045_4, ~m?
+# 1045| mu1045_7(decltype([...](...){...})) = InitializeIndirection[#this] : &:r1045_6
+# 1045| r1045_8(glval) = VariableAddress[f] :
+# 1045| mu1045_9(float) = InitializeParameter[f] : &:r1045_8
+# 1045| r1045_10(glval) = VariableAddress[#return] :
+# 1045| r1045_11(glval) = VariableAddress[#this] :
+# 1045| r1045_12(lambda [] type at line 1045, col. 21 *) = Load[#this] : &:r1045_11, ~m?
+# 1045| r1045_13(glval) = FieldAddress[s] : r1045_12
+# 1045| r1045_14(glval) = FunctionAddress[c_str] :
+# 1045| r1045_15(char *) = Call[c_str] : func:r1045_14, this:r1045_13
+# 1045| mu1045_16(unknown) = ^CallSideEffect : ~m?
+# 1045| v1045_17(void) = ^BufferReadSideEffect[-1] : &:r1045_13, ~m?
+# 1045| r1045_18(glval) = VariableAddress[#this] :
+# 1045| r1045_19(lambda [] type at line 1045, col. 21 *) = Load[#this] : &:r1045_18, ~m?
+# 1045| r1045_20(glval) = FieldAddress[x] : r1045_19
+# 1045| r1045_21(int) = Load[?] : &:r1045_20, ~m?
+# 1045| r1045_22(glval) = PointerAdd[1] : r1045_15, r1045_21
+# 1045| r1045_23(char) = Load[?] : &:r1045_22, ~m?
+# 1045| mu1045_24(char) = Store[#return] : &:r1045_10, r1045_23
+# 1045| v1045_25(void) = ReturnIndirection[#this] : &:r1045_6, ~m?
+# 1045| r1045_26(glval) = VariableAddress[#return] :
+# 1045| v1045_27(void) = ReturnValue : &:r1045_26, ~m?
+# 1045| v1045_28(void) = AliasedUse : ~m?
+# 1045| v1045_29(void) = ExitFunction :
# 1047| char (void Lambda(int, String const&))::(lambda [] type at line 1047, col. 30)::operator()(float) const
# 1047| Block 0
-# 1047| v1047_1(void) = EnterFunction :
-# 1047| mu1047_2(unknown) = AliasedDefinition :
-# 1047| mu1047_3(unknown) = InitializeNonLocal :
-# 1047| r1047_4(glval) = VariableAddress[#this] :
-# 1047| mu1047_5(glval) = InitializeParameter[#this] : &:r1047_4
-# 1047| r1047_6(glval) = Load[#this] : &:r1047_4, ~m?
-# 1047| mu1047_7(decltype([...](...){...})) = InitializeIndirection[#this] : &:r1047_6
-# 1047| r1047_8(glval) = VariableAddress[f] :
-# 1047| mu1047_9(float) = InitializeParameter[f] : &:r1047_8
-# 1047| r1047_10(glval) = VariableAddress[#return] :
-# 1047| r1047_11(glval) = VariableAddress[#this] :
-# 1047| r1047_12(lambda [] type at line 1047, col. 30 *) = Load[#this] : &:r1047_11, ~m?
-# 1047| r1047_13(glval) = FieldAddress[s] : r1047_12
-# 1047| r1047_14(String &) = Load[?] : &:r1047_13, ~m?
-# 1047| r1047_15(glval) = CopyValue : r1047_14
-# 1047| r1047_16(glval) = FunctionAddress[c_str] :
-# 1047| r1047_17(char *) = Call[c_str] : func:r1047_16, this:r1047_15
-# 1047| mu1047_18(unknown) = ^CallSideEffect : ~m?
-# 1047| v1047_19(void) = ^BufferReadSideEffect[-1] : &:r1047_15, ~m?
-# 1047| mu1047_20(String) = ^IndirectMayWriteSideEffect[-1] : &:r1047_15
-# 1047| r1047_21(int) = Constant[0] :
-# 1047| r1047_22(glval) = PointerAdd[1] : r1047_17, r1047_21
-# 1047| r1047_23(char) = Load[?] : &:r1047_22, ~m?
-# 1047| mu1047_24(char) = Store[#return] : &:r1047_10, r1047_23
-# 1047| v1047_25(void) = ReturnIndirection[#this] : &:r1047_6, ~m?
-# 1047| r1047_26(glval) = VariableAddress[#return] :
-# 1047| v1047_27(void) = ReturnValue : &:r1047_26, ~m?
-# 1047| v1047_28(void) = AliasedUse : ~m?
-# 1047| v1047_29(void) = ExitFunction :
+# 1047| v1047_1(void) = EnterFunction :
+# 1047| mu1047_2(unknown) = AliasedDefinition :
+# 1047| mu1047_3(unknown) = InitializeNonLocal :
+# 1047| r1047_4(glval) = VariableAddress[#this] :
+# 1047| mu1047_5(glval) = InitializeParameter[#this] : &:r1047_4
+# 1047| r1047_6(glval) = Load[#this] : &:r1047_4, ~m?
+# 1047| mu1047_7(decltype([...](...){...})) = InitializeIndirection[#this] : &:r1047_6
+# 1047| r1047_8(glval) = VariableAddress[f] :
+# 1047| mu1047_9(float) = InitializeParameter[f] : &:r1047_8
+# 1047| r1047_10(glval) = VariableAddress[#return] :
+# 1047| r1047_11(glval) = VariableAddress[#this] :
+# 1047| r1047_12(lambda [] type at line 1047, col. 30 *) = Load[#this] : &:r1047_11, ~m?
+# 1047| r1047_13(glval) = FieldAddress[s] : r1047_12
+# 1047| r1047_14(String &) = Load[?] : &:r1047_13, ~m?
+# 1047| r1047_15(glval) = CopyValue : r1047_14
+# 1047| r1047_16(glval) = FunctionAddress[c_str] :
+# 1047| r1047_17(char *) = Call[c_str] : func:r1047_16, this:r1047_15
+# 1047| mu1047_18(unknown) = ^CallSideEffect : ~m?
+# 1047| v1047_19(void) = ^BufferReadSideEffect[-1] : &:r1047_15, ~m?
+# 1047| r1047_20(int) = Constant[0] :
+# 1047| r1047_21(glval) = PointerAdd[1] : r1047_17, r1047_20
+# 1047| r1047_22(char) = Load[?] : &:r1047_21, ~m?
+# 1047| mu1047_23(char) = Store[#return] : &:r1047_10, r1047_22
+# 1047| v1047_24(void) = ReturnIndirection[#this] : &:r1047_6, ~m?
+# 1047| r1047_25(glval) = VariableAddress[#return] :
+# 1047| v1047_26(void) = ReturnValue : &:r1047_25, ~m?
+# 1047| v1047_27(void) = AliasedUse : ~m?
+# 1047| v1047_28(void) = ExitFunction :
# 1049| void (void Lambda(int, String const&))::(lambda [] type at line 1049, col. 30)::~()
# 1049| Block 0
@@ -6025,182 +5987,175 @@ ir.cpp:
# 1049| char (void Lambda(int, String const&))::(lambda [] type at line 1049, col. 30)::operator()(float) const
# 1049| Block 0
-# 1049| v1049_1(void) = EnterFunction :
-# 1049| mu1049_2(unknown) = AliasedDefinition :
-# 1049| mu1049_3(unknown) = InitializeNonLocal :
-# 1049| r1049_4(glval) = VariableAddress[#this] :
-# 1049| mu1049_5(glval) = InitializeParameter[#this] : &:r1049_4
-# 1049| r1049_6(glval) = Load[#this] : &:r1049_4, ~m?
-# 1049| mu1049_7(decltype([...](...){...})) = InitializeIndirection[#this] : &:r1049_6
-# 1049| r1049_8(glval) = VariableAddress[f] :
-# 1049| mu1049_9(float) = InitializeParameter[f] : &:r1049_8
-# 1049| r1049_10(glval) = VariableAddress[#return] :
-# 1049| r1049_11(glval) = VariableAddress[#this] :
-# 1049| r1049_12(lambda [] type at line 1049, col. 30 *) = Load[#this] : &:r1049_11, ~m?
-# 1049| r1049_13(glval) = FieldAddress[s] : r1049_12
-# 1049| r1049_14(glval) = FunctionAddress[c_str] :
-# 1049| r1049_15(char *) = Call[c_str] : func:r1049_14, this:r1049_13
-# 1049| mu1049_16(unknown) = ^CallSideEffect : ~m?
-# 1049| v1049_17(void) = ^BufferReadSideEffect[-1] : &:r1049_13, ~m?
-# 1049| mu1049_18(String) = ^IndirectMayWriteSideEffect[-1] : &:r1049_13
-# 1049| r1049_19(int) = Constant[0] :
-# 1049| r1049_20(glval) = PointerAdd[1] : r1049_15, r1049_19
-# 1049| r1049_21(char) = Load[?] : &:r1049_20, ~m?
-# 1049| mu1049_22(char) = Store[#return] : &:r1049_10, r1049_21
-# 1049| v1049_23(void) = ReturnIndirection[#this] : &:r1049_6, ~m?
-# 1049| r1049_24(glval) = VariableAddress[#return] :
-# 1049| v1049_25(void) = ReturnValue : &:r1049_24, ~m?
-# 1049| v1049_26(void) = AliasedUse : ~m?
-# 1049| v1049_27(void) = ExitFunction :
+# 1049| v1049_1(void) = EnterFunction :
+# 1049| mu1049_2(unknown) = AliasedDefinition :
+# 1049| mu1049_3(unknown) = InitializeNonLocal :
+# 1049| r1049_4(glval) = VariableAddress[#this] :
+# 1049| mu1049_5(glval) = InitializeParameter[#this] : &:r1049_4
+# 1049| r1049_6(glval) = Load[#this] : &:r1049_4, ~m?
+# 1049| mu1049_7(decltype([...](...){...})) = InitializeIndirection[#this] : &:r1049_6
+# 1049| r1049_8(glval) = VariableAddress[f] :
+# 1049| mu1049_9(float) = InitializeParameter[f] : &:r1049_8
+# 1049| r1049_10(glval) = VariableAddress[#return] :
+# 1049| r1049_11(glval) = VariableAddress[#this] :
+# 1049| r1049_12(lambda [] type at line 1049, col. 30 *) = Load[#this] : &:r1049_11, ~m?
+# 1049| r1049_13(glval) = FieldAddress[s] : r1049_12
+# 1049| r1049_14(glval) = FunctionAddress[c_str] :
+# 1049| r1049_15(char *) = Call[c_str] : func:r1049_14, this:r1049_13
+# 1049| mu1049_16(unknown) = ^CallSideEffect : ~m?
+# 1049| v1049_17(void) = ^BufferReadSideEffect[-1] : &:r1049_13, ~m?
+# 1049| r1049_18(int) = Constant[0] :
+# 1049| r1049_19(glval) = PointerAdd[1] : r1049_15, r1049_18
+# 1049| r1049_20(char) = Load[?] : &:r1049_19, ~m?
+# 1049| mu1049_21(char) = Store[#return] : &:r1049_10, r1049_20
+# 1049| v1049_22(void) = ReturnIndirection[#this] : &:r1049_6, ~m?
+# 1049| r1049_23(glval) = VariableAddress[#return] :
+# 1049| v1049_24(void) = ReturnValue : &:r1049_23, ~m?
+# 1049| v1049_25(void) = AliasedUse : ~m?
+# 1049| v1049_26(void) = ExitFunction :
# 1051| char (void Lambda(int, String const&))::(lambda [] type at line 1051, col. 32)::operator()(float) const
# 1051| Block 0
-# 1051| v1051_1(void) = EnterFunction :
-# 1051| mu1051_2(unknown) = AliasedDefinition :
-# 1051| mu1051_3(unknown) = InitializeNonLocal :
-# 1051| r1051_4(glval) = VariableAddress[#this] :
-# 1051| mu1051_5(glval) = InitializeParameter[#this] : &:r1051_4
-# 1051| r1051_6(glval) = Load[#this] : &:r1051_4, ~m?
-# 1051| mu1051_7(decltype([...](...){...})) = InitializeIndirection[#this] : &:r1051_6
-# 1051| r1051_8(glval) = VariableAddress[f] :
-# 1051| mu1051_9(float) = InitializeParameter[f] : &:r1051_8
-# 1051| r1051_10(glval) = VariableAddress[#return] :
-# 1051| r1051_11(glval) = VariableAddress[#this] :
-# 1051| r1051_12(lambda [] type at line 1051, col. 32 *) = Load[#this] : &:r1051_11, ~m?
-# 1051| r1051_13(glval) = FieldAddress[s] : r1051_12
-# 1051| r1051_14(String &) = Load[?] : &:r1051_13, ~m?
-# 1051| r1051_15(glval) = CopyValue : r1051_14
-# 1051| r1051_16(glval) = FunctionAddress[c_str] :
-# 1051| r1051_17(char *) = Call[c_str] : func:r1051_16, this:r1051_15
-# 1051| mu1051_18(unknown) = ^CallSideEffect : ~m?
-# 1051| v1051_19(void) = ^BufferReadSideEffect[-1] : &:r1051_15, ~m?
-# 1051| mu1051_20(String) = ^IndirectMayWriteSideEffect[-1] : &:r1051_15
-# 1051| r1051_21(glval) = VariableAddress[#this] :
-# 1051| r1051_22(lambda [] type at line 1051, col. 32 *) = Load[#this] : &:r1051_21, ~m?
-# 1051| r1051_23(glval) = FieldAddress[x] : r1051_22
-# 1051| r1051_24(int) = Load[?] : &:r1051_23, ~m?
-# 1051| r1051_25(glval) = PointerAdd[1] : r1051_17, r1051_24
-# 1051| r1051_26(char) = Load[?] : &:r1051_25, ~m?
-# 1051| mu1051_27(char) = Store[#return] : &:r1051_10, r1051_26
-# 1051| v1051_28(void) = ReturnIndirection[#this] : &:r1051_6, ~m?
-# 1051| r1051_29(glval) = VariableAddress[#return] :
-# 1051| v1051_30(void) = ReturnValue : &:r1051_29, ~m?
-# 1051| v1051_31(void) = AliasedUse : ~m?
-# 1051| v1051_32(void) = ExitFunction :
+# 1051| v1051_1(void) = EnterFunction :
+# 1051| mu1051_2(unknown) = AliasedDefinition :
+# 1051| mu1051_3(unknown) = InitializeNonLocal :
+# 1051| r1051_4(glval) = VariableAddress[#this] :
+# 1051| mu1051_5(glval) = InitializeParameter[#this] : &:r1051_4
+# 1051| r1051_6(glval) = Load[#this] : &:r1051_4, ~m?
+# 1051| mu1051_7(decltype([...](...){...})) = InitializeIndirection[#this] : &:r1051_6
+# 1051| r1051_8(glval) = VariableAddress[f] :
+# 1051| mu1051_9(float) = InitializeParameter[f] : &:r1051_8
+# 1051| r1051_10(glval) = VariableAddress[#return] :
+# 1051| r1051_11(glval) = VariableAddress[#this] :
+# 1051| r1051_12(lambda [] type at line 1051, col. 32 *) = Load[#this] : &:r1051_11, ~m?
+# 1051| r1051_13(glval) = FieldAddress[s] : r1051_12
+# 1051| r1051_14(String &) = Load[?] : &:r1051_13, ~m?
+# 1051| r1051_15(glval) = CopyValue : r1051_14
+# 1051| r1051_16(glval) = FunctionAddress[c_str] :
+# 1051| r1051_17(char *) = Call[c_str] : func:r1051_16, this:r1051_15
+# 1051| mu1051_18(unknown) = ^CallSideEffect : ~m?
+# 1051| v1051_19(void) = ^BufferReadSideEffect[-1] : &:r1051_15, ~m?
+# 1051| r1051_20(glval) = VariableAddress[#this] :
+# 1051| r1051_21(lambda [] type at line 1051, col. 32 *) = Load[#this] : &:r1051_20, ~m?
+# 1051| r1051_22(glval) = FieldAddress[x] : r1051_21
+# 1051| r1051_23(int) = Load[?] : &:r1051_22, ~m?
+# 1051| r1051_24(glval) = PointerAdd[1] : r1051_17, r1051_23
+# 1051| r1051_25(char) = Load[?] : &:r1051_24, ~m?
+# 1051| mu1051_26(char) = Store[#return] : &:r1051_10, r1051_25
+# 1051| v1051_27(void) = ReturnIndirection[#this] : &:r1051_6, ~m?
+# 1051| r1051_28(glval) = VariableAddress[#return] :
+# 1051| v1051_29(void) = ReturnValue : &:r1051_28, ~m?
+# 1051| v1051_30(void) = AliasedUse : ~m?
+# 1051| v1051_31(void) = ExitFunction :
# 1054| char (void Lambda(int, String const&))::(lambda [] type at line 1054, col. 23)::operator()(float) const
# 1054| Block 0
-# 1054| v1054_1(void) = EnterFunction :
-# 1054| mu1054_2(unknown) = AliasedDefinition :
-# 1054| mu1054_3(unknown) = InitializeNonLocal :
-# 1054| r1054_4(glval) = VariableAddress[#this] :
-# 1054| mu1054_5(glval) = InitializeParameter[#this] : &:r1054_4
-# 1054| r1054_6(glval) = Load[#this] : &:r1054_4, ~m?
-# 1054| mu1054_7(decltype([...](...){...})) = InitializeIndirection[#this] : &:r1054_6
-# 1054| r1054_8(glval) = VariableAddress[f] :
-# 1054| mu1054_9(float) = InitializeParameter[f] : &:r1054_8
-# 1054| r1054_10(glval) = VariableAddress[#return] :
-# 1054| r1054_11(glval) = VariableAddress[#this] :
-# 1054| r1054_12(lambda [] type at line 1054, col. 23 *) = Load[#this] : &:r1054_11, ~m?
-# 1054| r1054_13(glval) = FieldAddress[s] : r1054_12
-# 1054| r1054_14(String &) = Load[?] : &:r1054_13, ~m?
-# 1054| r1054_15(glval) = CopyValue : r1054_14
-# 1054| r1054_16(glval) = FunctionAddress[c_str] :
-# 1054| r1054_17(char *) = Call[c_str] : func:r1054_16, this:r1054_15
-# 1054| mu1054_18(unknown) = ^CallSideEffect : ~m?
-# 1054| v1054_19(void) = ^BufferReadSideEffect[-1] : &:r1054_15, ~m?
-# 1054| mu1054_20(String) = ^IndirectMayWriteSideEffect[-1] : &:r1054_15
-# 1054| r1054_21(glval) = VariableAddress[#this] :
-# 1054| r1054_22(lambda [] type at line 1054, col. 23 *) = Load[#this] : &:r1054_21, ~m?
-# 1054| r1054_23(glval) = FieldAddress[x] : r1054_22
-# 1054| r1054_24(int) = Load[?] : &:r1054_23, ~m?
-# 1054| r1054_25(glval) = VariableAddress[#this] :
-# 1054| r1054_26(lambda [] type at line 1054, col. 23 *) = Load[#this] : &:r1054_25, ~m?
-# 1054| r1054_27(glval) = FieldAddress[i] : r1054_26
-# 1054| r1054_28(int) = Load[?] : &:r1054_27, ~m?
-# 1054| r1054_29(int) = Add : r1054_24, r1054_28
-# 1054| r1054_30(glval) = VariableAddress[#this] :
-# 1054| r1054_31(lambda [] type at line 1054, col. 23 *) = Load[#this] : &:r1054_30, ~m?
-# 1054| r1054_32(glval) = FieldAddress[j] : r1054_31
-# 1054| r1054_33(int &) = Load[?] : &:r1054_32, ~m?
-# 1054| r1054_34(int) = Load[?] : &:r1054_33, ~m?
-# 1054| r1054_35(int) = Sub : r1054_29, r1054_34
-# 1054| r1054_36(glval) = PointerAdd[1] : r1054_17, r1054_35
-# 1054| r1054_37(char) = Load[?] : &:r1054_36, ~m?
-# 1054| mu1054_38(char) = Store[#return] : &:r1054_10, r1054_37
-# 1054| v1054_39(void) = ReturnIndirection[#this] : &:r1054_6, ~m?
-# 1054| r1054_40(glval) = VariableAddress[#return] :
-# 1054| v1054_41(void) = ReturnValue : &:r1054_40, ~m?
-# 1054| v1054_42(void) = AliasedUse : ~m?
-# 1054| v1054_43(void) = ExitFunction :
+# 1054| v1054_1(void) = EnterFunction :
+# 1054| mu1054_2(unknown) = AliasedDefinition :
+# 1054| mu1054_3(unknown) = InitializeNonLocal :
+# 1054| r1054_4(glval) = VariableAddress[#this] :
+# 1054| mu1054_5(glval) = InitializeParameter[#this] : &:r1054_4
+# 1054| r1054_6(glval) = Load[#this] : &:r1054_4, ~m?
+# 1054| mu1054_7(decltype([...](...){...})) = InitializeIndirection[#this] : &:r1054_6
+# 1054| r1054_8(glval) = VariableAddress[f] :
+# 1054| mu1054_9(float) = InitializeParameter[f] : &:r1054_8
+# 1054| r1054_10(glval) = VariableAddress[#return] :
+# 1054| r1054_11(glval