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 @@ Exe - netcoreapp3.1 + net5.0 false win-x64;linux-x64;osx-x64 enable diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj b/cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj index aadcc07568d..1d6459d77e3 100644 --- a/cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj +++ b/cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net5.0 Semmle.Autobuild.Cpp Semmle.Autobuild.Cpp @@ -17,7 +17,7 @@ - + diff --git a/cpp/change-notes/2021-02-24-memset-may-be-deleted.md b/cpp/change-notes/2021-02-24-memset-may-be-deleted.md new file mode 100644 index 00000000000..3c6ec850ca6 --- /dev/null +++ b/cpp/change-notes/2021-02-24-memset-may-be-deleted.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* A new query (`cpp/memset-may-be-deleted`) is added to the default query suite. The query finds calls to `memset` that may be removed by the compiler. This behavior can make information-leak vulnerabilities easier to exploit. This query was originally [submitted as an experimental query by @ihsinme](https://github.com/github/codeql/pull/4953). diff --git a/cpp/change-notes/2021-03-01-fluent-interface-data-flow.md b/cpp/change-notes/2021-03-01-fluent-interface-data-flow.md new file mode 100644 index 00000000000..f1492afcf31 --- /dev/null +++ b/cpp/change-notes/2021-03-01-fluent-interface-data-flow.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The data-flow library now recognises more side-effects of method chaining (e.g. `someObject.setX(clean).setY(tainted).setZ...` having a side-effect on `someObject`), as well as other related circumstances where a function input is directly passed to its output. All queries that use data-flow analysis, including most security queries, may return more results accordingly. diff --git a/cpp/change-notes/2021-03-11-failed-extractions.md b/cpp/change-notes/2021-03-11-failed-extractions.md new file mode 100644 index 00000000000..30c6a510a08 --- /dev/null +++ b/cpp/change-notes/2021-03-11-failed-extractions.md @@ -0,0 +1,2 @@ +codescanning +* Added cpp/diagnostics/failed-extractions. This query gives information about which extractions did not run to completion. diff --git a/cpp/change-notes/2021-03-17-av-rule-79.md b/cpp/change-notes/2021-03-17-av-rule-79.md new file mode 100644 index 00000000000..4d20b5c125b --- /dev/null +++ b/cpp/change-notes/2021-03-17-av-rule-79.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The 'Resource not released in destructor' (cpp/resource-not-released-in-destructor) query has been improved to recognize more releases of resources. \ No newline at end of file diff --git a/cpp/ql/src/Critical/DeadCodeCondition.qhelp b/cpp/ql/src/Critical/DeadCodeCondition.qhelp index c0c65c9e017..4c8a2a2f519 100644 --- a/cpp/ql/src/Critical/DeadCodeCondition.qhelp +++ b/cpp/ql/src/Critical/DeadCodeCondition.qhelp @@ -9,7 +9,7 @@ It is likely that these conditions indicate an error in the branching condition. Alternatively, the conditions may have been left behind after debugging.

- + diff --git a/cpp/ql/src/Critical/DeadCodeFunction.qhelp b/cpp/ql/src/Critical/DeadCodeFunction.qhelp index f5c4b432836..3faa7820e82 100644 --- a/cpp/ql/src/Critical/DeadCodeFunction.qhelp +++ b/cpp/ql/src/Critical/DeadCodeFunction.qhelp @@ -13,7 +13,7 @@ If left in the code base they increase object code size, decrease code comprehen This type of function may be part of the program's API and could be used by external programs.

- + diff --git a/cpp/ql/src/Critical/DescriptorMayNotBeClosed.qhelp b/cpp/ql/src/Critical/DescriptorMayNotBeClosed.qhelp index 1e0dcc2173e..2016e05d936 100644 --- a/cpp/ql/src/Critical/DescriptorMayNotBeClosed.qhelp +++ b/cpp/ql/src/Critical/DescriptorMayNotBeClosed.qhelp @@ -10,7 +10,7 @@ This query looks at functions that return file or socket descriptors, but may re This can occur when an operation performed on the open descriptor fails, and the function returns with an error before it closes the open resource. An improperly handled error could cause the function to leak resource descriptors. Failing to close resources in the function that opened them also makes it more difficult to detect leaks.

- + diff --git a/cpp/ql/src/Critical/DescriptorNeverClosed.qhelp b/cpp/ql/src/Critical/DescriptorNeverClosed.qhelp index a63c85f3399..c64bd47ff43 100644 --- a/cpp/ql/src/Critical/DescriptorNeverClosed.qhelp +++ b/cpp/ql/src/Critical/DescriptorNeverClosed.qhelp @@ -10,7 +10,7 @@ This rule finds calls to socket where there is no corresponding - + diff --git a/cpp/ql/src/Critical/FileMayNotBeClosed.qhelp b/cpp/ql/src/Critical/FileMayNotBeClosed.qhelp index b05af84c9bf..05791c01ab9 100644 --- a/cpp/ql/src/Critical/FileMayNotBeClosed.qhelp +++ b/cpp/ql/src/Critical/FileMayNotBeClosed.qhelp @@ -10,7 +10,7 @@ This rule looks at functions that return a 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.

- + diff --git a/cpp/ql/src/Critical/FileNeverClosed.qhelp b/cpp/ql/src/Critical/FileNeverClosed.qhelp index b870e9b50c3..6dc91049037 100644 --- a/cpp/ql/src/Critical/FileNeverClosed.qhelp +++ b/cpp/ql/src/Critical/FileNeverClosed.qhelp @@ -10,7 +10,7 @@ This rule finds calls to fopen with no corresponding fclose - + 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) = 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| r1054_20(glval) = VariableAddress[#this] : +# 1054| r1054_21(lambda [] type at line 1054, col. 23 *) = Load[#this] : &:r1054_20, ~m? +# 1054| r1054_22(glval) = FieldAddress[x] : r1054_21 +# 1054| r1054_23(int) = Load[?] : &:r1054_22, ~m? +# 1054| r1054_24(glval) = VariableAddress[#this] : +# 1054| r1054_25(lambda [] type at line 1054, col. 23 *) = Load[#this] : &:r1054_24, ~m? +# 1054| r1054_26(glval) = FieldAddress[i] : r1054_25 +# 1054| r1054_27(int) = Load[?] : &:r1054_26, ~m? +# 1054| r1054_28(int) = Add : r1054_23, r1054_27 +# 1054| r1054_29(glval) = VariableAddress[#this] : +# 1054| r1054_30(lambda [] type at line 1054, col. 23 *) = Load[#this] : &:r1054_29, ~m? +# 1054| r1054_31(glval) = FieldAddress[j] : r1054_30 +# 1054| r1054_32(int &) = Load[?] : &:r1054_31, ~m? +# 1054| r1054_33(int) = Load[?] : &:r1054_32, ~m? +# 1054| r1054_34(int) = Sub : r1054_28, r1054_33 +# 1054| r1054_35(glval) = PointerAdd[1] : r1054_17, r1054_34 +# 1054| r1054_36(char) = Load[?] : &:r1054_35, ~m? +# 1054| mu1054_37(char) = Store[#return] : &:r1054_10, r1054_36 +# 1054| v1054_38(void) = ReturnIndirection[#this] : &:r1054_6, ~m? +# 1054| r1054_39(glval) = VariableAddress[#return] : +# 1054| v1054_40(void) = ReturnValue : &:r1054_39, ~m? +# 1054| v1054_41(void) = AliasedUse : ~m? +# 1054| v1054_42(void) = ExitFunction : # 1077| void RangeBasedFor(vector const&) # 1077| Block 0 -# 1077| v1077_1(void) = EnterFunction : -# 1077| mu1077_2(unknown) = AliasedDefinition : -# 1077| mu1077_3(unknown) = InitializeNonLocal : -# 1077| r1077_4(glval &>) = VariableAddress[v] : -# 1077| mu1077_5(vector &) = InitializeParameter[v] : &:r1077_4 -# 1077| r1077_6(vector &) = Load[v] : &:r1077_4, ~m? -# 1077| mu1077_7(unknown) = InitializeIndirection[v] : &:r1077_6 -# 1078| r1078_1(glval &>) = VariableAddress[(__range)] : -# 1078| r1078_2(glval &>) = VariableAddress[v] : -# 1078| r1078_3(vector &) = Load[v] : &:r1078_2, ~m? -# 1078| r1078_4(glval>) = CopyValue : r1078_3 -# 1078| r1078_5(vector &) = CopyValue : r1078_4 -# 1078| mu1078_6(vector &) = Store[(__range)] : &:r1078_1, r1078_5 -# 1078| r1078_7(glval) = VariableAddress[(__begin)] : -# 1078| r1078_8(glval &>) = VariableAddress[(__range)] : -# 1078| r1078_9(vector &) = Load[(__range)] : &:r1078_8, ~m? -#-----| r0_1(glval>) = CopyValue : r1078_9 -# 1078| r1078_10(glval) = FunctionAddress[begin] : -# 1078| r1078_11(iterator) = Call[begin] : func:r1078_10, this:r0_1 -# 1078| mu1078_12(unknown) = ^CallSideEffect : ~m? -#-----| v0_2(void) = ^BufferReadSideEffect[-1] : &:r0_1, ~m? -#-----| mu0_3(vector) = ^IndirectMayWriteSideEffect[-1] : &:r0_1 -# 1078| mu1078_13(iterator) = Store[(__begin)] : &:r1078_7, r1078_11 -# 1078| r1078_14(glval) = VariableAddress[(__end)] : -# 1078| r1078_15(glval &>) = VariableAddress[(__range)] : -# 1078| r1078_16(vector &) = Load[(__range)] : &:r1078_15, ~m? -#-----| r0_4(glval>) = CopyValue : r1078_16 -# 1078| r1078_17(glval) = FunctionAddress[end] : -# 1078| r1078_18(iterator) = Call[end] : func:r1078_17, this:r0_4 -# 1078| mu1078_19(unknown) = ^CallSideEffect : ~m? -#-----| v0_5(void) = ^BufferReadSideEffect[-1] : &:r0_4, ~m? -#-----| mu0_6(vector) = ^IndirectMayWriteSideEffect[-1] : &:r0_4 -# 1078| mu1078_20(iterator) = Store[(__end)] : &:r1078_14, r1078_18 +# 1077| v1077_1(void) = EnterFunction : +# 1077| mu1077_2(unknown) = AliasedDefinition : +# 1077| mu1077_3(unknown) = InitializeNonLocal : +# 1077| r1077_4(glval &>) = VariableAddress[v] : +# 1077| mu1077_5(vector &) = InitializeParameter[v] : &:r1077_4 +# 1077| r1077_6(vector &) = Load[v] : &:r1077_4, ~m? +# 1077| mu1077_7(unknown) = InitializeIndirection[v] : &:r1077_6 +# 1078| r1078_1(glval &>) = VariableAddress[(__range)] : +# 1078| r1078_2(glval &>) = VariableAddress[v] : +# 1078| r1078_3(vector &) = Load[v] : &:r1078_2, ~m? +# 1078| r1078_4(glval>) = CopyValue : r1078_3 +# 1078| r1078_5(vector &) = CopyValue : r1078_4 +# 1078| mu1078_6(vector &) = Store[(__range)] : &:r1078_1, r1078_5 +# 1078| r1078_7(glval) = VariableAddress[(__begin)] : +# 1078| r1078_8(glval &>) = VariableAddress[(__range)] : +# 1078| r1078_9(vector &) = Load[(__range)] : &:r1078_8, ~m? +#-----| r0_1(glval>) = CopyValue : r1078_9 +# 1078| r1078_10(glval) = FunctionAddress[begin] : +# 1078| r1078_11(iterator) = Call[begin] : func:r1078_10, this:r0_1 +# 1078| mu1078_12(unknown) = ^CallSideEffect : ~m? +#-----| v0_2(void) = ^BufferReadSideEffect[-1] : &:r0_1, ~m? +# 1078| mu1078_13(iterator) = Store[(__begin)] : &:r1078_7, r1078_11 +# 1078| r1078_14(glval) = VariableAddress[(__end)] : +# 1078| r1078_15(glval &>) = VariableAddress[(__range)] : +# 1078| r1078_16(vector &) = Load[(__range)] : &:r1078_15, ~m? +#-----| r0_3(glval>) = CopyValue : r1078_16 +# 1078| r1078_17(glval) = FunctionAddress[end] : +# 1078| r1078_18(iterator) = Call[end] : func:r1078_17, this:r0_3 +# 1078| mu1078_19(unknown) = ^CallSideEffect : ~m? +#-----| v0_4(void) = ^BufferReadSideEffect[-1] : &:r0_3, ~m? +# 1078| mu1078_20(iterator) = Store[(__end)] : &:r1078_14, r1078_18 #-----| Goto -> Block 1 # 1078| Block 1 -# 1078| r1078_21(glval) = VariableAddress[(__begin)] : -#-----| r0_7(glval) = Convert : r1078_21 -# 1078| r1078_22(glval) = FunctionAddress[operator!=] : -# 1078| r1078_23(glval) = VariableAddress[(__end)] : -# 1078| r1078_24(iterator) = Load[(__end)] : &:r1078_23, ~m? -# 1078| r1078_25(bool) = Call[operator!=] : func:r1078_22, this:r0_7, 0:r1078_24 -# 1078| mu1078_26(unknown) = ^CallSideEffect : ~m? -#-----| v0_8(void) = ^BufferReadSideEffect[-1] : &:r0_7, ~m? -#-----| mu0_9(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r0_7 -# 1078| v1078_27(void) = ConditionalBranch : r1078_25 +# 1078| r1078_21(glval) = VariableAddress[(__begin)] : +#-----| r0_5(glval) = Convert : r1078_21 +# 1078| r1078_22(glval) = FunctionAddress[operator!=] : +# 1078| r1078_23(glval) = VariableAddress[(__end)] : +# 1078| r1078_24(iterator) = Load[(__end)] : &:r1078_23, ~m? +# 1078| r1078_25(bool) = Call[operator!=] : func:r1078_22, this:r0_5, 0:r1078_24 +# 1078| mu1078_26(unknown) = ^CallSideEffect : ~m? +#-----| v0_6(void) = ^BufferReadSideEffect[-1] : &:r0_5, ~m? +# 1078| v1078_27(void) = ConditionalBranch : r1078_25 #-----| False -> Block 5 #-----| True -> Block 2 # 1078| Block 2 -# 1078| r1078_28(glval) = VariableAddress[e] : -# 1078| r1078_29(glval) = VariableAddress[(__begin)] : -#-----| r0_10(glval) = Convert : r1078_29 -# 1078| r1078_30(glval) = FunctionAddress[operator*] : -# 1078| r1078_31(int &) = Call[operator*] : func:r1078_30, this:r0_10 -# 1078| mu1078_32(unknown) = ^CallSideEffect : ~m? -#-----| v0_11(void) = ^BufferReadSideEffect[-1] : &:r0_10, ~m? -#-----| mu0_12(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r0_10 -# 1078| r1078_33(int) = Load[?] : &:r1078_31, ~m? -# 1078| mu1078_34(int) = Store[e] : &:r1078_28, r1078_33 -# 1079| r1079_1(glval) = VariableAddress[e] : -# 1079| r1079_2(int) = Load[e] : &:r1079_1, ~m? -# 1079| r1079_3(int) = Constant[0] : -# 1079| r1079_4(bool) = CompareGT : r1079_2, r1079_3 -# 1079| v1079_5(void) = ConditionalBranch : r1079_4 +# 1078| r1078_28(glval) = VariableAddress[e] : +# 1078| r1078_29(glval) = VariableAddress[(__begin)] : +#-----| r0_7(glval) = Convert : r1078_29 +# 1078| r1078_30(glval) = FunctionAddress[operator*] : +# 1078| r1078_31(int &) = Call[operator*] : func:r1078_30, this:r0_7 +# 1078| mu1078_32(unknown) = ^CallSideEffect : ~m? +#-----| v0_8(void) = ^BufferReadSideEffect[-1] : &:r0_7, ~m? +# 1078| r1078_33(int) = Load[?] : &:r1078_31, ~m? +# 1078| mu1078_34(int) = Store[e] : &:r1078_28, r1078_33 +# 1079| r1079_1(glval) = VariableAddress[e] : +# 1079| r1079_2(int) = Load[e] : &:r1079_1, ~m? +# 1079| r1079_3(int) = Constant[0] : +# 1079| r1079_4(bool) = CompareGT : r1079_2, r1079_3 +# 1079| v1079_5(void) = ConditionalBranch : r1079_4 #-----| False -> Block 4 #-----| True -> Block 3 @@ -6220,45 +6175,42 @@ ir.cpp: #-----| Goto (back edge) -> Block 1 # 1084| Block 5 -# 1084| r1084_1(glval &>) = VariableAddress[(__range)] : -# 1084| r1084_2(glval &>) = VariableAddress[v] : -# 1084| r1084_3(vector &) = Load[v] : &:r1084_2, ~m? -# 1084| r1084_4(glval>) = CopyValue : r1084_3 -# 1084| r1084_5(vector &) = CopyValue : r1084_4 -# 1084| mu1084_6(vector &) = Store[(__range)] : &:r1084_1, r1084_5 -# 1084| r1084_7(glval) = VariableAddress[(__begin)] : -# 1084| r1084_8(glval &>) = VariableAddress[(__range)] : -# 1084| r1084_9(vector &) = Load[(__range)] : &:r1084_8, ~m? -#-----| r0_13(glval>) = CopyValue : r1084_9 -# 1084| r1084_10(glval) = FunctionAddress[begin] : -# 1084| r1084_11(iterator) = Call[begin] : func:r1084_10, this:r0_13 -# 1084| mu1084_12(unknown) = ^CallSideEffect : ~m? -#-----| v0_14(void) = ^BufferReadSideEffect[-1] : &:r0_13, ~m? -#-----| mu0_15(vector) = ^IndirectMayWriteSideEffect[-1] : &:r0_13 -# 1084| mu1084_13(iterator) = Store[(__begin)] : &:r1084_7, r1084_11 -# 1084| r1084_14(glval) = VariableAddress[(__end)] : -# 1084| r1084_15(glval &>) = VariableAddress[(__range)] : -# 1084| r1084_16(vector &) = Load[(__range)] : &:r1084_15, ~m? -#-----| r0_16(glval>) = CopyValue : r1084_16 -# 1084| r1084_17(glval) = FunctionAddress[end] : -# 1084| r1084_18(iterator) = Call[end] : func:r1084_17, this:r0_16 -# 1084| mu1084_19(unknown) = ^CallSideEffect : ~m? -#-----| v0_17(void) = ^BufferReadSideEffect[-1] : &:r0_16, ~m? -#-----| mu0_18(vector) = ^IndirectMayWriteSideEffect[-1] : &:r0_16 -# 1084| mu1084_20(iterator) = Store[(__end)] : &:r1084_14, r1084_18 +# 1084| r1084_1(glval &>) = VariableAddress[(__range)] : +# 1084| r1084_2(glval &>) = VariableAddress[v] : +# 1084| r1084_3(vector &) = Load[v] : &:r1084_2, ~m? +# 1084| r1084_4(glval>) = CopyValue : r1084_3 +# 1084| r1084_5(vector &) = CopyValue : r1084_4 +# 1084| mu1084_6(vector &) = Store[(__range)] : &:r1084_1, r1084_5 +# 1084| r1084_7(glval) = VariableAddress[(__begin)] : +# 1084| r1084_8(glval &>) = VariableAddress[(__range)] : +# 1084| r1084_9(vector &) = Load[(__range)] : &:r1084_8, ~m? +#-----| r0_9(glval>) = CopyValue : r1084_9 +# 1084| r1084_10(glval) = FunctionAddress[begin] : +# 1084| r1084_11(iterator) = Call[begin] : func:r1084_10, this:r0_9 +# 1084| mu1084_12(unknown) = ^CallSideEffect : ~m? +#-----| v0_10(void) = ^BufferReadSideEffect[-1] : &:r0_9, ~m? +# 1084| mu1084_13(iterator) = Store[(__begin)] : &:r1084_7, r1084_11 +# 1084| r1084_14(glval) = VariableAddress[(__end)] : +# 1084| r1084_15(glval &>) = VariableAddress[(__range)] : +# 1084| r1084_16(vector &) = Load[(__range)] : &:r1084_15, ~m? +#-----| r0_11(glval>) = CopyValue : r1084_16 +# 1084| r1084_17(glval) = FunctionAddress[end] : +# 1084| r1084_18(iterator) = Call[end] : func:r1084_17, this:r0_11 +# 1084| mu1084_19(unknown) = ^CallSideEffect : ~m? +#-----| v0_12(void) = ^BufferReadSideEffect[-1] : &:r0_11, ~m? +# 1084| mu1084_20(iterator) = Store[(__end)] : &:r1084_14, r1084_18 #-----| Goto -> Block 6 # 1084| Block 6 -# 1084| r1084_21(glval) = VariableAddress[(__begin)] : -#-----| r0_19(glval) = Convert : r1084_21 -# 1084| r1084_22(glval) = FunctionAddress[operator!=] : -# 1084| r1084_23(glval) = VariableAddress[(__end)] : -# 1084| r1084_24(iterator) = Load[(__end)] : &:r1084_23, ~m? -# 1084| r1084_25(bool) = Call[operator!=] : func:r1084_22, this:r0_19, 0:r1084_24 -# 1084| mu1084_26(unknown) = ^CallSideEffect : ~m? -#-----| v0_20(void) = ^BufferReadSideEffect[-1] : &:r0_19, ~m? -#-----| mu0_21(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r0_19 -# 1084| v1084_27(void) = ConditionalBranch : r1084_25 +# 1084| r1084_21(glval) = VariableAddress[(__begin)] : +#-----| r0_13(glval) = Convert : r1084_21 +# 1084| r1084_22(glval) = FunctionAddress[operator!=] : +# 1084| r1084_23(glval) = VariableAddress[(__end)] : +# 1084| r1084_24(iterator) = Load[(__end)] : &:r1084_23, ~m? +# 1084| r1084_25(bool) = Call[operator!=] : func:r1084_22, this:r0_13, 0:r1084_24 +# 1084| mu1084_26(unknown) = ^CallSideEffect : ~m? +#-----| v0_14(void) = ^BufferReadSideEffect[-1] : &:r0_13, ~m? +# 1084| v1084_27(void) = ConditionalBranch : r1084_25 #-----| False -> Block 10 #-----| True -> Block 8 @@ -6273,24 +6225,23 @@ ir.cpp: #-----| Goto (back edge) -> Block 6 # 1084| Block 8 -# 1084| r1084_35(glval) = VariableAddress[e] : -# 1084| r1084_36(glval) = VariableAddress[(__begin)] : -#-----| r0_22(glval) = Convert : r1084_36 -# 1084| r1084_37(glval) = FunctionAddress[operator*] : -# 1084| r1084_38(int &) = Call[operator*] : func:r1084_37, this:r0_22 -# 1084| mu1084_39(unknown) = ^CallSideEffect : ~m? -#-----| v0_23(void) = ^BufferReadSideEffect[-1] : &:r0_22, ~m? -#-----| mu0_24(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r0_22 -# 1084| r1084_40(glval) = CopyValue : r1084_38 -# 1084| r1084_41(glval) = Convert : r1084_40 -# 1084| r1084_42(int &) = CopyValue : r1084_41 -# 1084| mu1084_43(int &) = Store[e] : &:r1084_35, r1084_42 -# 1085| r1085_1(glval) = VariableAddress[e] : -# 1085| r1085_2(int &) = Load[e] : &:r1085_1, ~m? -# 1085| r1085_3(int) = Load[?] : &:r1085_2, ~m? -# 1085| r1085_4(int) = Constant[5] : -# 1085| r1085_5(bool) = CompareLT : r1085_3, r1085_4 -# 1085| v1085_6(void) = ConditionalBranch : r1085_5 +# 1084| r1084_35(glval) = VariableAddress[e] : +# 1084| r1084_36(glval) = VariableAddress[(__begin)] : +#-----| r0_15(glval) = Convert : r1084_36 +# 1084| r1084_37(glval) = FunctionAddress[operator*] : +# 1084| r1084_38(int &) = Call[operator*] : func:r1084_37, this:r0_15 +# 1084| mu1084_39(unknown) = ^CallSideEffect : ~m? +#-----| v0_16(void) = ^BufferReadSideEffect[-1] : &:r0_15, ~m? +# 1084| r1084_40(glval) = CopyValue : r1084_38 +# 1084| r1084_41(glval) = Convert : r1084_40 +# 1084| r1084_42(int &) = CopyValue : r1084_41 +# 1084| mu1084_43(int &) = Store[e] : &:r1084_35, r1084_42 +# 1085| r1085_1(glval) = VariableAddress[e] : +# 1085| r1085_2(int &) = Load[e] : &:r1085_1, ~m? +# 1085| r1085_3(int) = Load[?] : &:r1085_2, ~m? +# 1085| r1085_4(int) = Constant[5] : +# 1085| r1085_5(bool) = CompareLT : r1085_3, r1085_4 +# 1085| v1085_6(void) = ConditionalBranch : r1085_5 #-----| False -> Block 7 #-----| True -> Block 9 @@ -6475,8 +6426,7 @@ ir.cpp: # 1149| mu1149_17(unknown) = ^CallSideEffect : ~m? # 1149| mu1149_18(String) = ^IndirectMayWriteSideEffect[-1] : &:r1149_11 # 1149| v1149_19(void) = ^BufferReadSideEffect[0] : &:r1149_15, ~m? -# 1149| mu1149_20(unknown) = ^BufferMayWriteSideEffect[0] : &:r1149_15 -# 1149| v1149_21(void) = ThrowValue : &:r1149_11, ~m? +# 1149| v1149_20(void) = ThrowValue : &:r1149_11, ~m? #-----| Exception -> Block 9 # 1151| Block 8 @@ -6504,8 +6454,7 @@ ir.cpp: # 1154| mu1154_7(unknown) = ^CallSideEffect : ~m? # 1154| mu1154_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r1154_1 # 1154| v1154_9(void) = ^BufferReadSideEffect[0] : &:r1154_5, ~m? -# 1154| mu1154_10(unknown) = ^BufferMayWriteSideEffect[0] : &:r1154_5 -# 1154| v1154_11(void) = ThrowValue : &:r1154_1, ~m? +# 1154| v1154_10(void) = ThrowValue : &:r1154_1, ~m? #-----| Exception -> Block 2 # 1156| Block 11 @@ -6631,7 +6580,6 @@ ir.cpp: # 1179| mu1179_7(unknown) = ^CallSideEffect : ~m? # 1179| mu1179_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r1179_1 # 1179| v1179_9(void) = ^BufferReadSideEffect[0] : &:r1179_5, ~m? -# 1179| mu1179_10(unknown) = ^BufferMayWriteSideEffect[0] : &:r1179_5 # 1178| r1178_4(glval) = VariableAddress[#return] : # 1178| v1178_5(void) = ReturnValue : &:r1178_4, ~m? # 1178| v1178_6(void) = AliasedUse : ~m? @@ -6887,9 +6835,8 @@ ir.cpp: # 1242| mu1242_9(unknown) = ^CallSideEffect : ~m? # 1242| mu1242_10(String) = ^IndirectMayWriteSideEffect[-1] : &:r1242_4 # 1242| v1242_11(void) = ^BufferReadSideEffect[0] : &:r1242_7, ~m? -# 1242| mu1242_12(unknown) = ^BufferMayWriteSideEffect[0] : &:r1242_7 -# 1242| r1242_13(bool) = Constant[1] : -# 1242| mu1242_14(bool) = Store[b#init] : &:r1242_1, r1242_13 +# 1242| r1242_12(bool) = Constant[1] : +# 1242| mu1242_13(bool) = Store[b#init] : &:r1242_1, r1242_12 #-----| Goto -> Block 4 # 1243| Block 4 @@ -6908,9 +6855,8 @@ ir.cpp: # 1243| mu1243_9(unknown) = ^CallSideEffect : ~m? # 1243| mu1243_10(String) = ^IndirectMayWriteSideEffect[-1] : &:r1243_4 # 1243| v1243_11(void) = ^BufferReadSideEffect[0] : &:r1243_7, ~m? -# 1243| mu1243_12(unknown) = ^BufferMayWriteSideEffect[0] : &:r1243_7 -# 1243| r1243_13(bool) = Constant[1] : -# 1243| mu1243_14(bool) = Store[c#init] : &:r1243_1, r1243_13 +# 1243| r1243_12(bool) = Constant[1] : +# 1243| mu1243_13(bool) = Store[c#init] : &:r1243_1, r1243_12 #-----| Goto -> Block 6 # 1244| Block 6 @@ -7528,7 +7474,6 @@ ir.cpp: # 1369| v1369_5(void) = Call[acceptRef] : func:r1369_1, 0:r1369_4 # 1369| mu1369_6(unknown) = ^CallSideEffect : ~m? # 1369| v1369_7(void) = ^BufferReadSideEffect[0] : &:r1369_4, ~m? -# 1369| mu1369_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r1369_4 # 1370| r1370_1(glval) = FunctionAddress[acceptRef] : # 1370| r1370_2(glval) = VariableAddress[#temp1370:23] : # 1370| mu1370_3(String) = Uninitialized[#temp1370:23] : &:r1370_2 @@ -7539,12 +7484,10 @@ ir.cpp: # 1370| mu1370_8(unknown) = ^CallSideEffect : ~m? # 1370| mu1370_9(String) = ^IndirectMayWriteSideEffect[-1] : &:r1370_2 # 1370| v1370_10(void) = ^BufferReadSideEffect[0] : &:r1370_6, ~m? -# 1370| mu1370_11(unknown) = ^BufferMayWriteSideEffect[0] : &:r1370_6 -# 1370| r1370_12(String &) = CopyValue : r1370_2 -# 1370| v1370_13(void) = Call[acceptRef] : func:r1370_1, 0:r1370_12 -# 1370| mu1370_14(unknown) = ^CallSideEffect : ~m? -# 1370| v1370_15(void) = ^BufferReadSideEffect[0] : &:r1370_12, ~m? -# 1370| mu1370_16(unknown) = ^BufferMayWriteSideEffect[0] : &:r1370_12 +# 1370| r1370_11(String &) = CopyValue : r1370_2 +# 1370| v1370_12(void) = Call[acceptRef] : func:r1370_1, 0:r1370_11 +# 1370| mu1370_13(unknown) = ^CallSideEffect : ~m? +# 1370| v1370_14(void) = ^BufferReadSideEffect[0] : &:r1370_11, ~m? # 1371| r1371_1(glval) = FunctionAddress[acceptValue] : # 1371| r1371_2(glval) = VariableAddress[#temp1371:17] : # 1371| mu1371_3(String) = Uninitialized[#temp1371:17] : &:r1371_2 @@ -7556,10 +7499,9 @@ ir.cpp: # 1371| mu1371_9(unknown) = ^CallSideEffect : ~m? # 1371| mu1371_10(String) = ^IndirectMayWriteSideEffect[-1] : &:r1371_2 # 1371| v1371_11(void) = ^BufferReadSideEffect[0] : &:r1371_7, ~m? -# 1371| mu1371_12(unknown) = ^BufferMayWriteSideEffect[0] : &:r1371_7 -# 1371| r1371_13(String) = Load[#temp1371:17] : &:r1371_2, ~m? -# 1371| v1371_14(void) = Call[acceptValue] : func:r1371_1, 0:r1371_13 -# 1371| mu1371_15(unknown) = ^CallSideEffect : ~m? +# 1371| r1371_12(String) = Load[#temp1371:17] : &:r1371_2, ~m? +# 1371| v1371_13(void) = Call[acceptValue] : func:r1371_1, 0:r1371_12 +# 1371| mu1371_14(unknown) = ^CallSideEffect : ~m? # 1372| r1372_1(glval) = FunctionAddress[acceptValue] : # 1372| r1372_2(glval) = VariableAddress[#temp1372:25] : # 1372| mu1372_3(String) = Uninitialized[#temp1372:25] : &:r1372_2 @@ -7570,10 +7512,9 @@ ir.cpp: # 1372| mu1372_8(unknown) = ^CallSideEffect : ~m? # 1372| mu1372_9(String) = ^IndirectMayWriteSideEffect[-1] : &:r1372_2 # 1372| v1372_10(void) = ^BufferReadSideEffect[0] : &:r1372_6, ~m? -# 1372| mu1372_11(unknown) = ^BufferMayWriteSideEffect[0] : &:r1372_6 -# 1372| r1372_12(String) = Load[#temp1372:25] : &:r1372_2, ~m? -# 1372| v1372_13(void) = Call[acceptValue] : func:r1372_1, 0:r1372_12 -# 1372| mu1372_14(unknown) = ^CallSideEffect : ~m? +# 1372| r1372_11(String) = Load[#temp1372:25] : &:r1372_2, ~m? +# 1372| v1372_12(void) = Call[acceptValue] : func:r1372_1, 0:r1372_11 +# 1372| mu1372_13(unknown) = ^CallSideEffect : ~m? # 1373| r1373_1(glval) = VariableAddress[#temp1373:5] : # 1373| mu1373_2(String) = Uninitialized[#temp1373:5] : &:r1373_1 # 1373| r1373_3(glval) = FunctionAddress[String] : @@ -7585,7 +7526,6 @@ ir.cpp: # 1373| r1373_9(char *) = Call[c_str] : func:r1373_8, this:r1373_7 # 1373| mu1373_10(unknown) = ^CallSideEffect : ~m? # 1373| v1373_11(void) = ^BufferReadSideEffect[-1] : &:r1373_7, ~m? -# 1373| mu1373_12(String) = ^IndirectMayWriteSideEffect[-1] : &:r1373_7 # 1374| r1374_1(glval) = VariableAddress[#temp1374:5] : # 1374| r1374_2(glval) = FunctionAddress[returnValue] : # 1374| r1374_3(String) = Call[returnValue] : func:r1374_2 @@ -7596,7 +7536,6 @@ ir.cpp: # 1374| r1374_8(char *) = Call[c_str] : func:r1374_7, this:r1374_6 # 1374| mu1374_9(unknown) = ^CallSideEffect : ~m? # 1374| v1374_10(void) = ^BufferReadSideEffect[-1] : &:r1374_6, ~m? -# 1374| mu1374_11(String) = ^IndirectMayWriteSideEffect[-1] : &:r1374_6 # 1376| r1376_1(glval) = VariableAddress[#temp1376:5] : # 1376| r1376_2(glval) = FunctionAddress[defaultConstruct] : # 1376| r1376_3(String) = Call[defaultConstruct] : func:r1376_2 @@ -7636,7 +7575,6 @@ ir.cpp: # 1383| v1383_5(void) = Call[acceptRef] : func:r1383_1, 0:r1383_4 # 1383| mu1383_6(unknown) = ^CallSideEffect : ~m? # 1383| v1383_7(void) = ^BufferReadSideEffect[0] : &:r1383_4, ~m? -# 1383| mu1383_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r1383_4 # 1384| r1384_1(glval) = FunctionAddress[acceptValue] : # 1384| r1384_2(glval) = VariableAddress[#temp1384:17] : # 1384| r1384_3(glval) = VariableAddress[d] : @@ -7706,7 +7644,6 @@ ir.cpp: # 1395| v1395_5(void) = Call[acceptRef] : func:r1395_1, 0:r1395_4 # 1395| mu1395_6(unknown) = ^CallSideEffect : ~m? # 1395| v1395_7(void) = ^BufferReadSideEffect[0] : &:r1395_4, ~m? -# 1395| mu1395_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r1395_4 # 1396| r1396_1(glval) = FunctionAddress[acceptValue] : # 1396| r1396_2(glval) = VariableAddress[#temp1396:17] : # 1396| mu1396_3(copy_constructor) = Uninitialized[#temp1396:17] : &:r1396_2 @@ -7718,10 +7655,9 @@ ir.cpp: # 1396| mu1396_9(unknown) = ^CallSideEffect : ~m? # 1396| mu1396_10(copy_constructor) = ^IndirectMayWriteSideEffect[-1] : &:r1396_2 # 1396| v1396_11(void) = ^BufferReadSideEffect[0] : &:r1396_7, ~m? -# 1396| mu1396_12(unknown) = ^BufferMayWriteSideEffect[0] : &:r1396_7 -# 1396| r1396_13(copy_constructor) = Load[#temp1396:17] : &:r1396_2, ~m? -# 1396| v1396_14(void) = Call[acceptValue] : func:r1396_1, 0:r1396_13 -# 1396| mu1396_15(unknown) = ^CallSideEffect : ~m? +# 1396| r1396_12(copy_constructor) = Load[#temp1396:17] : &:r1396_2, ~m? +# 1396| v1396_13(void) = Call[acceptValue] : func:r1396_1, 0:r1396_12 +# 1396| mu1396_14(unknown) = ^CallSideEffect : ~m? # 1397| r1397_1(glval) = VariableAddress[#temp1397:5] : # 1397| mu1397_2(copy_constructor) = Uninitialized[#temp1397:5] : &:r1397_1 # 1397| r1397_3(glval) = FunctionAddress[copy_constructor] : @@ -7789,7 +7725,6 @@ ir.cpp: # 1408| v1408_5(void) = Call[acceptRef] : func:r1408_1, 0:r1408_4 # 1408| mu1408_6(unknown) = ^CallSideEffect : ~m? # 1408| v1408_7(void) = ^BufferReadSideEffect[0] : &:r1408_4, ~m? -# 1408| mu1408_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r1408_4 # 1409| r1409_1(glval) = FunctionAddress[acceptValue] : # 1409| r1409_2(glval) = VariableAddress[p] : # 1409| r1409_3(Point) = Load[p] : &:r1409_2, ~m? @@ -7917,8 +7852,7 @@ ir.cpp: # 1447| r1447_10(float) = Call[f] : func:r1447_9, this:r1447_8 # 1447| mu1447_11(unknown) = ^CallSideEffect : ~m? # 1447| v1447_12(void) = ^BufferReadSideEffect[-1] : &:r1447_8, ~m? -# 1447| mu1447_13(POD_Base) = ^IndirectMayWriteSideEffect[-1] : &:r1447_8 -# 1447| mu1447_14(float) = Store[f] : &:r1447_1, r1447_10 +# 1447| mu1447_13(float) = Store[f] : &:r1447_1, r1447_10 # 1448| v1448_1(void) = NoOp : # 1443| v1443_4(void) = ReturnVoid : # 1443| v1443_5(void) = AliasedUse : ~m? diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected index 6aeadb2f174..511edc7e45a 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected @@ -1492,12 +1492,8 @@ postWithInFlow | cpp11.cpp:65:19:65:45 | Store | PostUpdateNode should not be the target of local flow. | | cpp11.cpp:82:17:82:55 | Chi | PostUpdateNode should not be the target of local flow. | | cpp11.cpp:82:17:82:55 | Chi | PostUpdateNode should not be the target of local flow. | -| cpp11.cpp:82:45:82:48 | Chi | PostUpdateNode should not be the target of local flow. | | defdestructordeleteexpr.cpp:4:9:4:15 | Chi | PostUpdateNode should not be the target of local flow. | | deleteexpr.cpp:7:9:7:15 | Chi | PostUpdateNode should not be the target of local flow. | -| file://:0:0:0:0 | Chi | PostUpdateNode should not be the target of local flow. | -| file://:0:0:0:0 | Chi | PostUpdateNode should not be the target of local flow. | -| file://:0:0:0:0 | Chi | PostUpdateNode should not be the target of local flow. | | ir.cpp:177:5:177:12 | Chi | PostUpdateNode should not be the target of local flow. | | ir.cpp:178:5:178:12 | Chi | PostUpdateNode should not be the target of local flow. | | ir.cpp:183:5:183:12 | Chi | PostUpdateNode should not be the target of local flow. | diff --git a/cpp/ql/test/library-tests/syntax-zoo/modeled-functions.cpp b/cpp/ql/test/library-tests/syntax-zoo/modeled-functions.cpp new file mode 100644 index 00000000000..c6867a9abe3 --- /dev/null +++ b/cpp/ql/test/library-tests/syntax-zoo/modeled-functions.cpp @@ -0,0 +1,7 @@ +void accept(int arg, char *buf, unsigned long* bufSize); + +void testAccept(int socket1, int socket2) +{ + char buffer[1024]; + accept(socket2, 0, 0); +} diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-014/MemsetMayBeDeleted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-014/MemsetMayBeDeleted.expected new file mode 100644 index 00000000000..7b469bea894 --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-014/MemsetMayBeDeleted.expected @@ -0,0 +1,3 @@ +| test.cpp:48:5:48:10 | call to memset | Call to memset may be deleted by the compiler. | +| test.cpp:79:5:79:10 | call to memset | Call to memset may be deleted by the compiler. | +| test.cpp:208:2:208:7 | call to memset | Call to memset may be deleted by the compiler. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-014/MemsetMayBeDeleted.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-014/MemsetMayBeDeleted.qlref new file mode 100644 index 00000000000..e81526fe6d9 --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-014/MemsetMayBeDeleted.qlref @@ -0,0 +1 @@ +Security/CWE/CWE-014/MemsetMayBeDeleted.ql diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-014/test.c b/cpp/ql/test/query-tests/Security/CWE/CWE-014/test.c new file mode 100644 index 00000000000..1ca22b5782e --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-014/test.c @@ -0,0 +1,140 @@ +typedef unsigned long long size_t; +void *memset(void *s, int c, unsigned long n); +void *__builtin_memset(void *s, int c, unsigned long n); +typedef int errno_t; +typedef unsigned int rsize_t; +errno_t memset_s(void *dest, rsize_t destsz, int ch, rsize_t count); +char *strcpy(char *dest, const char *src); + +extern void use_pw(char *pw); + +#define PW_SIZE 32 + +// x86-64 gcc 9.2: deleted +// x86-64 clang 9.0.0: deleted +// x64 msvc v19.14 (WINE): deleted +int func1(void) { + char pw1[PW_SIZE]; + use_pw(pw1); + memset(pw1, 0, PW_SIZE); // BAD [NOT DETECTED] + return 0; +} + +// x86-64 gcc 9.2: deleted +// x86-64 clang 9.0.0: deleted +// x64 msvc v19.14 (WINE): not deleted +int func1a(void) { + char pw1a[PW_SIZE]; + use_pw(pw1a); + __builtin_memset(pw1a, 0, PW_SIZE); // BAD [NOT DETECTED] + return 0; +} + +// x86-64 gcc 9.2: deleted +// x86-64 clang 9.0.0: deleted +// x64 msvc v19.14 (WINE): deleted +char *func1b(void) { + char pw1b[PW_SIZE]; + use_pw(pw1b); + memset(pw1b, 0, PW_SIZE); // BAD [NOT DETECTED] + pw1b[0] = pw1b[3] = 'a'; + return 0; +} + +// x86-64 gcc 9.2: not deleted +// x86-64 clang 9.0.0: not deleted +// x64 msvc v19.14 (WINE): not deleted +int func1c(char pw1c[PW_SIZE]) { + use_pw(pw1c); + memset(pw1c, 0, PW_SIZE); // GOOD + return 0; +} + +// x86-64 gcc 9.2: not deleted +// x86-64 clang 9.0.0: not deleted +// x64 msvc v19.14 (WINE): not deleted +char pw1d[PW_SIZE]; +int func1d() { + use_pw(pw1d); + memset(pw1d, 0, PW_SIZE); // GOOD + return 0; +} +// x86-64 gcc 9.2: deleted +// x86-64 clang 9.0.0: deleted +// x64 msvc v19.14 (WINE): deleted +char *func2(void) { + char pw2[PW_SIZE]; + use_pw(pw2); + memset(pw2, 1, PW_SIZE); // BAD [NOT DETECTED] + return pw2; +} + +// x86-64 gcc 9.2: deleted +// x86-64 clang 9.0.0: deleted +// x64 msvc v19.14 (WINE): partially deleted +int func3(void) { + char pw3[PW_SIZE]; + use_pw(pw3); + memset(pw3, 4, PW_SIZE); // BAD [NOT DETECTED] + return pw3[2]; +} + +// x86-64 gcc 9.2: deleted +// x86-64 clang 9.0.0: deleted +// x64 msvc v19.14 (WINE): not deleted +int func4(void) { + char pw1a[PW_SIZE]; + use_pw(pw1a); + __builtin_memset(pw1a + 3, 0, PW_SIZE - 3); // BAD [NOT DETECTED] + return 0; +} + +// x86-64 gcc 9.2: deleted +// x86-64 clang 9.0.0: deleted +// x64 msvc v19.14 (WINE): not deleted +int func6(void) { + char pw1a[PW_SIZE]; + use_pw(pw1a); + __builtin_memset(&pw1a[3], 0, PW_SIZE - 3); // BAD [NOT DETECTED] + return pw1a[2]; +} + +// x86-64 gcc 9.2: deleted +// x86-64 clang 9.0.0: deleted +// x64 msvc v19.14 (WINE): not deleted +int func5(void) { + char pw1a[PW_SIZE]; + use_pw(pw1a); + __builtin_memset(pw1a + 3, 0, PW_SIZE - 4); // GOOD + return pw1a[4]; +} + +// x86-64 gcc 9.2: deleted +// x86-64 clang 9.0.0: deleted +// x64 msvc v19.14 (WINE): not deleted +int func7(void) { + char pw1a[PW_SIZE]; + use_pw(pw1a); + __builtin_memset(&pw1a[3], 0, PW_SIZE - 5); // BAD [NOT DETECTED] + return 0; +} + +// x86-64 gcc 9.2: not deleted +// x86-64 clang 9.0.0: not deleted +// x64 msvc v19.14 (WINE): not deleted +int func8(void) { + char pw1a[PW_SIZE]; + use_pw(pw1a); + __builtin_memset(pw1a + pw1a[3], 0, PW_SIZE - 4); // GOOD + return pw1a[4]; +} + +// x86-64 gcc 9.2: deleted +// x86-64 clang 9.0.0: deleted +// x64 msvc v19.14 (WINE): deleted +char *func9(void) { + char pw1[PW_SIZE]; + use_pw(pw1); + memset(pw1, 0, PW_SIZE); // BAD [NOT DETECTED] + return 0; +} diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-014/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-014/test.cpp new file mode 100644 index 00000000000..1e0ed7d70f0 --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-014/test.cpp @@ -0,0 +1,401 @@ +extern "C" { + typedef unsigned long long size_t; + void *memset(void *s, int c, unsigned long n); + void *__builtin_memset(void *s, int c, unsigned long n); + typedef int errno_t; + typedef unsigned int rsize_t; + errno_t memset_s(void *dest, rsize_t destsz, int ch, rsize_t count); + char *strcpy(char *dest, const char *src); + void *memcpy(void *dest, const void *src, unsigned long n); + void *malloc(unsigned long size); + void free(void *ptr); + extern void use_pw(char *pw); + int printf(const char* format, ...); + char* gets(char * str); +} + +#define PW_SIZE 32 + +struct mem { + int a; + char b[PW_SIZE]; + int c; +}; + +// x86-64 gcc 9.2: not deleted +// x86-64 clang 9.0.0: not deleted +// x64 msvc v19.22: not deleted +void func(char buff[128], unsigned long long sz) { + gets(buff); + memset(buff, 0, PW_SIZE); // GOOD +} + +// x86-64 gcc 9.2: not deleted +// x86-64 clang 9.0.0: not deleted +// x64 msvc v19.22: not deleted +char *func2(char buff[128], unsigned long long sz) { + gets(buff); + memset(buff, 0, PW_SIZE); // GOOD + return buff; +} + +// x86-64 gcc 9.2: deleted +// x86-64 clang 9.0.0: deleted +// x64 msvc v19.22: deleted +void func3(unsigned long long sz) { + char buff[128]; + gets(buff); + memset(buff, 0, PW_SIZE); // BAD +} + +// x86-64 gcc 9.2: deleted +// x86-64 clang 9.0.0: deleted +// x64 msvc v19.22: deleted +void func4(unsigned long long sz) { + char buff[128]; + gets(buff); + memset(buff, 0, PW_SIZE); // BAD [NOT DETECTED] + strcpy(buff, "Hello"); +} + +// x86-64 gcc 9.2: deleted +// x86-64 clang 9.0.0: deleted +// x64 msvc v19.22: deleted +void func5(unsigned long long sz) { + char buff[128]; + gets(buff); + memset(buff, 0, PW_SIZE); // BAD [NOT DETECTED] + if (sz > 5) { + strcpy(buff, "Hello"); + } +} + +// x86-64 gcc 9.2: deleted +// x86-64 clang 9.0.0: deleted +// x64 msvc v19.22: deleted +void func6(unsigned long long sz) { + struct mem m; + gets(m.b); + memset(&m, 0, PW_SIZE); // BAD +} + +// x86-64 gcc 9.2: deleted +// x86-64 clang 9.0.0: deleted +// x64 msvc v19.22: deleted +void func7(unsigned long long sz) { + struct mem m; + gets(m.b); + memset(&m, 0, PW_SIZE); // BAD [NOT DETECTED] + m.a = 15; +} + +// x86-64 gcc 9.2: deleted +// x86-64 clang 9.0.0: deleted +// x64 msvc v19.22: not deleted +void func8(unsigned long long sz) { + struct mem *m = (struct mem *)malloc(sizeof(struct mem)); + gets(m->b); + memset(m, 0, PW_SIZE); // BAD [NOT DETECTED] +} + +// x86-64 gcc 9.2: deleted +// x86-64 clang 9.0.0: deleted +// x64 msvc v19.22: not deleted +void func9(unsigned long long sz) { + struct mem *m = (struct mem *)malloc(sizeof(struct mem)); + gets(m->b); + memset(m, 0, PW_SIZE); // BAD [NOT DETECTED] + free(m); +} + +// x86-64 gcc 9.2: deleted +// x86-64 clang 9.0.0: deleted +// x64 msvc v19.22: not deleted +void func10(unsigned long long sz) { + struct mem *m = (struct mem *)malloc(sizeof(struct mem)); + gets(m->b); + memset(m, 0, PW_SIZE); // BAD [NOT DETECTED] + m->a = sz; + m->c = m->a + 1; +} + +// x86-64 gcc 9.2: deleted +// x86-64 clang 9.0.0: deleted +// x64 msvc v19.22: not deleted +void func11(unsigned long long sz) { + struct mem *m = (struct mem *)malloc(sizeof(struct mem)); + gets(m->b); + ::memset(m, 0, PW_SIZE); // BAD [NOT DETECTED] + if (sz > 5) { + strcpy(m->b, "Hello"); + } +} + +// x86-64 gcc 9.2: not deleted +// x86-64 clang 9.0.0: not deleted +// x64 msvc v19.22: not deleted +int func12(unsigned long long sz) { + struct mem *m = (struct mem *)malloc(sizeof(struct mem)); + gets(m->b); + memset(m, 0, sz); // GOOD + return m->c; +} + +int funcN1() { + char pw[PW_SIZE]; + gets(pw); + char *pw_ptr = pw; + memset(pw, 0, PW_SIZE); // GOOD + use_pw(pw_ptr); + return 0; +} + +char pw_global[PW_SIZE]; +int funcN2() { + gets(pw_global); + use_pw(pw_global); + memset(pw_global, 0, PW_SIZE); // GOOD + return 0; +} + +int funcN3(unsigned long long sz) { + struct mem m; + gets(m.b); + memset(&m, 0, sizeof(m)); // GOOD + return m.a; +} + +void funcN(int num) { + char pw[PW_SIZE]; + int i; + for (i = 0; i < num; i++) + { + gets(pw); + use_pw(pw); + memset(pw, 0, PW_SIZE); // GOOD + } +} + +class MyClass +{ +public: + void set(int _x) { + x = _x; + } + + int get() + { + return x; + } + + void clear1() { + memset(&x, 0, sizeof(x)); // GOOD + } + + void clear2() { + memset(&(this->x), 0, sizeof(this->x)); // GOOD + } + +private: + int x; +}; + +void badFunc0_0(){ + unsigned char buff1[PW_SIZE]; + for(int i = 0; i < PW_SIZE; i++) { + buff1[i] = 13; + } + memset(buff1, 0, PW_SIZE); // BAD +} + +void nobadFunc1_0() { + char* buff1 = (char *) malloc(PW_SIZE); + gets(buff1); + memset(buff1, 0, PW_SIZE); // BAD [NOT DETECTED] +} +void badFunc1_0(){ + char * buff1 = (char *) malloc(PW_SIZE); + gets(buff1); + memset(buff1, 0, PW_SIZE); // BAD [NOT DETECTED] + free(buff1); +} +void badFunc1_1(){ + unsigned char buff1[PW_SIZE]; + for(int i = 0; i < PW_SIZE; i++) { + buff1[i] = 'a' + i; + } + memset(buff1, 0, PW_SIZE); // BAD [NOT DETECTED] + free(buff1); +} +void nobadFunc2_0_0(){ + unsigned char buff1[PW_SIZE]; + buff1[sizeof(buff1) - 1] = '\0'; + memset(buff1, 0, PW_SIZE); // GOOD + printf("%s", buff1); +} + +void nobadFunc2_0_1(){ + char buff1[PW_SIZE]; + gets(buff1); + memset(buff1, '\0', sizeof(buff1)); + memset(buff1, 0, PW_SIZE); // GOOD + printf("%s", buff1 + 3); +} + +void nobadFunc2_0_2(){ + char buff1[PW_SIZE]; + gets(buff1); + memset(buff1, 0, PW_SIZE); // GOOD + printf("%c", *buff1); +} + +void nobadFunc2_0_3(char ch){ + unsigned char buff1[PW_SIZE]; + for(int i = 0; i < PW_SIZE; i++) { + buff1[i] = ch; + } + memset(buff1, 0, PW_SIZE); // GOOD + printf("%c", *(buff1 + 3)); +} + +char * nobadFunc2_0_4(){ + char buff1[PW_SIZE]; + gets(buff1); + memset(buff1, 0, PW_SIZE); // GOOD + return buff1; +} + +char * nobadFunc2_0_5(){ + char buff1[PW_SIZE]; + gets(buff1); + memset(buff1, 0, PW_SIZE); // GOOD + + return buff1+3; +} + +unsigned char nobadFunc2_0_6(){ + unsigned char buff1[PW_SIZE]; + buff1[0] = 'z'; + int i; + memset(buff1, 0, PW_SIZE); // GOOD + + return *buff1; +} + +unsigned char nobadFunc2_0_7(){ + char buff1[PW_SIZE]; + gets(buff1); + memset(buff1, 0, PW_SIZE); // GOOD + + return *(buff1 + 3); +} + +bool nobadFunc2_1_0(unsigned char ch){ + char buff1[PW_SIZE]; + gets(buff1); + memset(buff1, 0, PW_SIZE); // GOOD + if(*buff1 == ch) { return true; } + return false; +} + +void nobadFunc2_1_2(){ + char buff1[PW_SIZE]; + gets(buff1); + memset(buff1, 0, PW_SIZE); // BAD [NOT DETECTED] + buff1[2] = 5; +} + +void nobadFunc3_0(char * buffAll){ + char * buff1 = buffAll; + gets(buff1); + memset(buff1, 0, PW_SIZE); // GOOD +} + +void nobadFunc3_1(unsigned char * buffAll){ + unsigned char * buff1 = buffAll + 3; + memset(buff1, 0, PW_SIZE); // GOOD +} + +struct buffers +{ + char buff1[50]; + unsigned char *buff2; +}; + +void nobadFunc3_2(struct buffers buffAll) { + char * buff1 = buffAll.buff1; + gets(buff1); + memset(buff1, 0, PW_SIZE); // GOOD +} + +void nobadFunc3_3(struct buffers buffAll) { + unsigned char * buff1 = buffAll.buff2; + memset(buff1, 0, PW_SIZE); // GOOD +} + +void nobadFunc3_4(struct buffers buffAll) { + unsigned char * buff1 = buffAll.buff2 + 3; + memset(buff1, 0, PW_SIZE); // GOOD +} + +void nobadFunc3_5(struct buffers * buffAll) { + char * buff1 = buffAll->buff1; + memset(buff1, 0, PW_SIZE); // GOOD +} + +void nobadFunc3_6(struct buffers *buffAll){ + unsigned char * buff1 = buffAll->buff2; + memset(buff1, 0, PW_SIZE); // GOOD +} + +char * globalBuff; + +void nobadFunc4(){ + char * buff1 = globalBuff; + memset(buff1, 0, PW_SIZE); // GOOD +} + +void nobadFunc4_0(){ + char * buff1 = globalBuff; + gets(buff1); + memset(buff1, 0, PW_SIZE); // GOOD +} +void nobadFunc4_1(){ + char * buff1 = globalBuff + 3; + memset(buff1, 0, PW_SIZE); // GOOD +} + +buffers globalBuff1, *globalBuff2; + +void nobadFunc4_2(){ + char * buff1 = globalBuff1.buff1; + memset(buff1, 0, PW_SIZE); // GOOD +} + +void nobadFunc4_3(){ + unsigned char * buff1 = globalBuff1.buff2; + memset(buff1, 0, PW_SIZE); // GOOD +} + +void nobadFunc4_4(){ + unsigned char * buff1 = globalBuff1.buff2+3; + memset(buff1, 0, PW_SIZE); // GOOD +} + +void nobadFunc4_5(){ + char * buff1 = globalBuff2->buff1; + memset(buff1, 0, PW_SIZE); // GOOD +} + +void nobadFunc4_6(){ + unsigned char * buff1 = globalBuff2->buff2; + memset(buff1, 0, PW_SIZE); // GOOD +} + +extern void use_byte(unsigned char); + +void test_static_func() { + static unsigned char buffer[PW_SIZE] = {0}; + use_byte(buffer[0]); + memset(buffer, 42, sizeof(buffer)); // GOOD +} diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-079/semmle/CgiXss/CgiXss.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-079/semmle/CgiXss/CgiXss.expected index de3326e74a5..6fc3eab83e9 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-079/semmle/CgiXss/CgiXss.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-079/semmle/CgiXss/CgiXss.expected @@ -8,14 +8,18 @@ edges | search.c:22:24:22:28 | *query | search.c:23:39:23:43 | query | | search.c:22:24:22:28 | query | search.c:23:39:23:43 | query | | search.c:22:24:22:28 | query | search.c:23:39:23:43 | query | -| search.c:51:21:51:26 | call to getenv | search.c:14:24:14:28 | *query | -| search.c:51:21:51:26 | call to getenv | search.c:14:24:14:28 | *query | -| search.c:51:21:51:26 | call to getenv | search.c:14:24:14:28 | query | -| search.c:51:21:51:26 | call to getenv | search.c:14:24:14:28 | query | -| search.c:51:21:51:26 | call to getenv | search.c:22:24:22:28 | *query | -| search.c:51:21:51:26 | call to getenv | search.c:22:24:22:28 | *query | -| search.c:51:21:51:26 | call to getenv | search.c:22:24:22:28 | query | -| search.c:51:21:51:26 | call to getenv | search.c:22:24:22:28 | query | +| search.c:51:21:51:26 | call to getenv | search.c:55:5:55:15 | raw_query | +| search.c:51:21:51:26 | call to getenv | search.c:55:5:55:15 | raw_query | +| search.c:51:21:51:26 | call to getenv | search.c:55:17:55:25 | raw_query indirection | +| search.c:51:21:51:26 | call to getenv | search.c:55:17:55:25 | raw_query indirection | +| search.c:51:21:51:26 | call to getenv | search.c:57:5:57:15 | raw_query | +| search.c:51:21:51:26 | call to getenv | search.c:57:5:57:15 | raw_query | +| search.c:51:21:51:26 | call to getenv | search.c:57:17:57:25 | raw_query indirection | +| search.c:51:21:51:26 | call to getenv | search.c:57:17:57:25 | raw_query indirection | +| search.c:55:5:55:15 | raw_query | search.c:14:24:14:28 | query | +| search.c:55:17:55:25 | raw_query indirection | search.c:14:24:14:28 | *query | +| search.c:57:5:57:15 | raw_query | search.c:22:24:22:28 | query | +| search.c:57:17:57:25 | raw_query indirection | search.c:22:24:22:28 | *query | nodes | search.c:14:24:14:28 | *query | semmle.label | *query | | search.c:14:24:14:28 | query | semmle.label | query | @@ -31,10 +35,10 @@ nodes | search.c:23:39:23:43 | query | semmle.label | query | | search.c:51:21:51:26 | call to getenv | semmle.label | call to getenv | | search.c:51:21:51:26 | call to getenv | semmle.label | call to getenv | -| search.c:55:5:55:15 | Argument 0 | semmle.label | Argument 0 | -| search.c:55:17:55:25 | Argument 0 indirection | semmle.label | Argument 0 indirection | -| search.c:57:5:57:15 | Argument 0 | semmle.label | Argument 0 | -| search.c:57:17:57:25 | Argument 0 indirection | semmle.label | Argument 0 indirection | +| search.c:55:5:55:15 | raw_query | semmle.label | raw_query | +| search.c:55:17:55:25 | raw_query indirection | semmle.label | raw_query indirection | +| search.c:57:5:57:15 | raw_query | semmle.label | raw_query | +| search.c:57:17:57:25 | raw_query indirection | semmle.label | raw_query indirection | #select | search.c:17:8:17:12 | query | search.c:51:21:51:26 | call to getenv | search.c:17:8:17:12 | query | Cross-site scripting vulnerability due to $@. | search.c:51:21:51:26 | call to getenv | this query data | | search.c:23:39:23:43 | query | search.c:51:21:51:26 | call to getenv | search.c:23:39:23:43 | query | Cross-site scripting vulnerability due to $@. | search.c:51:21:51:26 | call to getenv | this query data | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected index 0c2778abaf4..798a2eff20f 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected @@ -7,14 +7,18 @@ edges | test.cpp:29:30:29:36 | *command | test.cpp:31:10:31:16 | command | | test.cpp:29:30:29:36 | command | test.cpp:31:10:31:16 | command | | test.cpp:29:30:29:36 | command | test.cpp:31:10:31:16 | command | -| test.cpp:42:18:42:23 | call to getenv | test.cpp:24:30:24:36 | *command | -| test.cpp:42:18:42:23 | call to getenv | test.cpp:24:30:24:36 | command | -| test.cpp:42:18:42:34 | (const char *)... | test.cpp:24:30:24:36 | *command | -| test.cpp:42:18:42:34 | (const char *)... | test.cpp:24:30:24:36 | command | -| test.cpp:43:18:43:23 | call to getenv | test.cpp:29:30:29:36 | *command | -| test.cpp:43:18:43:23 | call to getenv | test.cpp:29:30:29:36 | command | -| test.cpp:43:18:43:34 | (const char *)... | test.cpp:29:30:29:36 | *command | -| test.cpp:43:18:43:34 | (const char *)... | test.cpp:29:30:29:36 | command | +| test.cpp:42:7:42:16 | call to getenv | test.cpp:24:30:24:36 | command | +| test.cpp:42:18:42:23 | call to getenv | test.cpp:42:7:42:16 | call to getenv | +| test.cpp:42:18:42:23 | call to getenv | test.cpp:42:18:42:34 | call to getenv indirection | +| test.cpp:42:18:42:34 | (const char *)... | test.cpp:42:7:42:16 | call to getenv | +| test.cpp:42:18:42:34 | (const char *)... | test.cpp:42:18:42:34 | call to getenv indirection | +| test.cpp:42:18:42:34 | call to getenv indirection | test.cpp:24:30:24:36 | *command | +| test.cpp:43:7:43:16 | call to getenv | test.cpp:29:30:29:36 | command | +| test.cpp:43:18:43:23 | call to getenv | test.cpp:43:7:43:16 | call to getenv | +| test.cpp:43:18:43:23 | call to getenv | test.cpp:43:18:43:34 | call to getenv indirection | +| test.cpp:43:18:43:34 | (const char *)... | test.cpp:43:7:43:16 | call to getenv | +| test.cpp:43:18:43:34 | (const char *)... | test.cpp:43:18:43:34 | call to getenv indirection | +| test.cpp:43:18:43:34 | call to getenv indirection | test.cpp:29:30:29:36 | *command | | test.cpp:56:12:56:17 | buffer | test.cpp:62:10:62:15 | (const char *)... | | test.cpp:56:12:56:17 | buffer | test.cpp:62:10:62:15 | buffer | | test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | (const char *)... | @@ -31,6 +35,14 @@ edges | test.cpp:76:12:76:17 | fgets output argument | test.cpp:78:10:78:15 | buffer | | test.cpp:76:12:76:17 | fgets output argument | test.cpp:79:10:79:13 | (const char *)... | | test.cpp:76:12:76:17 | fgets output argument | test.cpp:79:10:79:13 | data | +| test.cpp:98:17:98:22 | buffer | test.cpp:99:15:99:20 | (const char *)... | +| test.cpp:98:17:98:22 | buffer | test.cpp:99:15:99:20 | buffer | +| test.cpp:98:17:98:22 | recv output argument | test.cpp:99:15:99:20 | (const char *)... | +| test.cpp:98:17:98:22 | recv output argument | test.cpp:99:15:99:20 | buffer | +| test.cpp:106:17:106:22 | buffer | test.cpp:107:15:107:20 | (const char *)... | +| test.cpp:106:17:106:22 | buffer | test.cpp:107:15:107:20 | buffer | +| test.cpp:106:17:106:22 | recv output argument | test.cpp:107:15:107:20 | (const char *)... | +| test.cpp:106:17:106:22 | recv output argument | test.cpp:107:15:107:20 | buffer | nodes | test.cpp:24:30:24:36 | *command | semmle.label | *command | | test.cpp:24:30:24:36 | command | semmle.label | command | @@ -42,14 +54,14 @@ nodes | test.cpp:31:10:31:16 | command | semmle.label | command | | test.cpp:31:10:31:16 | command | semmle.label | command | | test.cpp:31:10:31:16 | command | semmle.label | command | -| test.cpp:42:7:42:16 | Argument 0 | semmle.label | Argument 0 | +| test.cpp:42:7:42:16 | call to getenv | semmle.label | call to getenv | | test.cpp:42:18:42:23 | call to getenv | semmle.label | call to getenv | | test.cpp:42:18:42:34 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:42:18:42:34 | Argument 0 indirection | semmle.label | Argument 0 indirection | -| test.cpp:43:7:43:16 | Argument 0 | semmle.label | Argument 0 | +| test.cpp:42:18:42:34 | call to getenv indirection | semmle.label | call to getenv indirection | +| test.cpp:43:7:43:16 | call to getenv | semmle.label | call to getenv | | test.cpp:43:18:43:23 | call to getenv | semmle.label | call to getenv | | test.cpp:43:18:43:34 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:43:18:43:34 | Argument 0 indirection | semmle.label | Argument 0 indirection | +| test.cpp:43:18:43:34 | call to getenv indirection | semmle.label | call to getenv indirection | | test.cpp:56:12:56:17 | buffer | semmle.label | buffer | | test.cpp:56:12:56:17 | fgets output argument | semmle.label | fgets output argument | | test.cpp:62:10:62:15 | (const char *)... | semmle.label | (const char *)... | @@ -66,6 +78,16 @@ nodes | test.cpp:79:10:79:13 | (const char *)... | semmle.label | (const char *)... | | test.cpp:79:10:79:13 | (const char *)... | semmle.label | (const char *)... | | test.cpp:79:10:79:13 | data | semmle.label | data | +| test.cpp:98:17:98:22 | buffer | semmle.label | buffer | +| test.cpp:98:17:98:22 | recv output argument | semmle.label | recv output argument | +| test.cpp:99:15:99:20 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:99:15:99:20 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:99:15:99:20 | buffer | semmle.label | buffer | +| test.cpp:106:17:106:22 | buffer | semmle.label | buffer | +| test.cpp:106:17:106:22 | recv output argument | semmle.label | recv output argument | +| test.cpp:107:15:107:20 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:107:15:107:20 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:107:15:107:20 | buffer | semmle.label | buffer | #select | test.cpp:26:10:26:16 | command | test.cpp:42:18:42:23 | call to getenv | test.cpp:26:10:26:16 | command | The value of this argument may come from $@ and is being passed to system | test.cpp:42:18:42:23 | call to getenv | call to getenv | | test.cpp:31:10:31:16 | command | test.cpp:43:18:43:23 | call to getenv | test.cpp:31:10:31:16 | command | The value of this argument may come from $@ and is being passed to system | test.cpp:43:18:43:23 | call to getenv | call to getenv | @@ -73,3 +95,5 @@ nodes | test.cpp:63:10:63:13 | data | test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | data | The value of this argument may come from $@ and is being passed to system | test.cpp:56:12:56:17 | buffer | buffer | | test.cpp:78:10:78:15 | buffer | test.cpp:76:12:76:17 | buffer | test.cpp:78:10:78:15 | buffer | The value of this argument may come from $@ and is being passed to system | test.cpp:76:12:76:17 | buffer | buffer | | test.cpp:79:10:79:13 | data | test.cpp:76:12:76:17 | buffer | test.cpp:79:10:79:13 | data | The value of this argument may come from $@ and is being passed to system | test.cpp:76:12:76:17 | buffer | buffer | +| test.cpp:99:15:99:20 | buffer | test.cpp:98:17:98:22 | buffer | test.cpp:99:15:99:20 | buffer | The value of this argument may come from $@ and is being passed to LoadLibrary | test.cpp:98:17:98:22 | buffer | buffer | +| test.cpp:107:15:107:20 | buffer | test.cpp:106:17:106:22 | buffer | test.cpp:107:15:107:20 | buffer | The value of this argument may come from $@ and is being passed to LoadLibrary | test.cpp:106:17:106:22 | buffer | buffer | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/test.cpp index eb9436bcadb..26aff44f3d8 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/test.cpp @@ -81,3 +81,29 @@ void testReferencePointer2() system(data2); // BAD [NOT DETECTED] } } + +// --- + +typedef unsigned long size_t; + +void accept(int arg, char *buf, size_t *bufSize); +void recv(int arg, char *buf, size_t bufSize); +void LoadLibrary(const char *arg); + +void testAcceptRecv(int socket1, int socket2) +{ + { + char buffer[1024]; + + recv(socket1, buffer, 1024); + LoadLibrary(buffer); // BAD: using data from recv + } + + { + char buffer[1024]; + + accept(socket2, 0, 0); + recv(socket2, buffer, 1024); + LoadLibrary(buffer); // BAD: using data from recv + } +} diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected index 9e73ca54e98..4c02b16cfe7 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected @@ -53,22 +53,26 @@ edges | argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | (const char *)... | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | i3 | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | i3 | +| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:2:117:13 | i3 | +| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:2:117:13 | i3 | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | array to pointer conversion | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | array to pointer conversion | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | i3 | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | i3 | -| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | printWrapper output argument | -| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | printWrapper output argument | +| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | i3 indirection | +| argvLocal.c:115:13:115:16 | argv | argvLocal.c:117:15:117:16 | i3 indirection | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | (const char *)... | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | (const char *)... | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | i4 | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:121:9:121:10 | i4 | +| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:2:122:13 | i4 | +| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:2:122:13 | i4 | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 | -| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | printWrapper output argument | -| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | printWrapper output argument | +| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 indirection | +| argvLocal.c:115:13:115:16 | argv | argvLocal.c:122:15:122:16 | i4 indirection | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | (const char *)... | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | (const char *)... | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:135:9:135:12 | ... ++ | @@ -77,15 +81,20 @@ edges | argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... | | argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... | +| argvLocal.c:117:2:117:13 | i3 | argvLocal.c:117:15:117:16 | printWrapper output argument | +| argvLocal.c:117:15:117:16 | i3 indirection | argvLocal.c:117:15:117:16 | printWrapper output argument | | argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:121:9:121:10 | (const char *)... | | argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:121:9:121:10 | i4 | +| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:122:2:122:13 | i4 | | argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:122:15:122:16 | i4 | | argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:122:15:122:16 | i4 | -| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:122:15:122:16 | printWrapper output argument | +| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:122:15:122:16 | i4 indirection | | argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | (const char *)... | | argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | ... ++ | | argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | -- ... | | argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | -- ... | +| argvLocal.c:122:2:122:13 | i4 | argvLocal.c:122:15:122:16 | printWrapper output argument | +| argvLocal.c:122:15:122:16 | i4 indirection | argvLocal.c:122:15:122:16 | printWrapper output argument | | argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | (const char *)... | | argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | ... ++ | | argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | -- ... | @@ -94,12 +103,14 @@ edges | argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | (const char *)... | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | i5 | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:127:9:127:10 | i5 | +| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:2:128:13 | i5 | +| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:2:128:13 | i5 | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | array to pointer conversion | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | array to pointer conversion | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | i5 | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | i5 | -| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | printWrapper output argument | -| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | printWrapper output argument | +| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | i5 indirection | +| argvLocal.c:126:10:126:13 | argv | argvLocal.c:128:15:128:16 | i5 indirection | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:131:9:131:14 | (const char *)... | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:131:9:131:14 | (const char *)... | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:131:9:131:14 | ... + ... | @@ -108,6 +119,8 @@ edges | argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | ... + ... | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | ... + ... | | argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | ... + ... | +| argvLocal.c:128:2:128:13 | i5 | argvLocal.c:128:15:128:16 | printWrapper output argument | +| argvLocal.c:128:15:128:16 | i5 indirection | argvLocal.c:128:15:128:16 | printWrapper output argument | | argvLocal.c:128:15:128:16 | printWrapper output argument | argvLocal.c:131:9:131:14 | (const char *)... | | argvLocal.c:128:15:128:16 | printWrapper output argument | argvLocal.c:131:9:131:14 | ... + ... | | argvLocal.c:128:15:128:16 | printWrapper output argument | argvLocal.c:132:15:132:20 | ... + ... | @@ -154,9 +167,8 @@ edges | argvLocal.c:168:18:168:21 | argv | argvLocal.c:170:24:170:26 | i10 | nodes | argvLocal.c:9:25:9:31 | *correct | semmle.label | *correct | +| argvLocal.c:9:25:9:31 | *correct | semmle.label | *correct | | argvLocal.c:9:25:9:31 | correct | semmle.label | correct | -| argvLocal.c:10:9:10:15 | Chi | semmle.label | Chi | -| argvLocal.c:10:9:10:15 | Chi | semmle.label | Chi | | argvLocal.c:95:9:95:12 | argv | semmle.label | argv | | argvLocal.c:95:9:95:12 | argv | semmle.label | argv | | argvLocal.c:95:9:95:15 | (const char *)... | semmle.label | (const char *)... | @@ -202,31 +214,31 @@ nodes | argvLocal.c:116:9:116:10 | (const char *)... | semmle.label | (const char *)... | | argvLocal.c:116:9:116:10 | (const char *)... | semmle.label | (const char *)... | | argvLocal.c:116:9:116:10 | i3 | semmle.label | i3 | -| argvLocal.c:117:2:117:13 | Argument 0 | semmle.label | Argument 0 | -| argvLocal.c:117:15:117:16 | Argument 0 indirection | semmle.label | Argument 0 indirection | +| argvLocal.c:117:2:117:13 | i3 | semmle.label | i3 | | argvLocal.c:117:15:117:16 | array to pointer conversion | semmle.label | array to pointer conversion | | argvLocal.c:117:15:117:16 | array to pointer conversion | semmle.label | array to pointer conversion | | argvLocal.c:117:15:117:16 | i3 | semmle.label | i3 | +| argvLocal.c:117:15:117:16 | i3 indirection | semmle.label | i3 indirection | | argvLocal.c:117:15:117:16 | printWrapper output argument | semmle.label | printWrapper output argument | | argvLocal.c:121:9:121:10 | (const char *)... | semmle.label | (const char *)... | | argvLocal.c:121:9:121:10 | (const char *)... | semmle.label | (const char *)... | | argvLocal.c:121:9:121:10 | i4 | semmle.label | i4 | -| argvLocal.c:122:2:122:13 | Argument 0 | semmle.label | Argument 0 | -| argvLocal.c:122:15:122:16 | Argument 0 indirection | semmle.label | Argument 0 indirection | +| argvLocal.c:122:2:122:13 | i4 | semmle.label | i4 | | argvLocal.c:122:15:122:16 | i4 | semmle.label | i4 | | argvLocal.c:122:15:122:16 | i4 | semmle.label | i4 | | argvLocal.c:122:15:122:16 | i4 | semmle.label | i4 | +| argvLocal.c:122:15:122:16 | i4 indirection | semmle.label | i4 indirection | | argvLocal.c:122:15:122:16 | printWrapper output argument | semmle.label | printWrapper output argument | | argvLocal.c:126:10:126:13 | argv | semmle.label | argv | | argvLocal.c:126:10:126:13 | argv | semmle.label | argv | | argvLocal.c:127:9:127:10 | (const char *)... | semmle.label | (const char *)... | | argvLocal.c:127:9:127:10 | (const char *)... | semmle.label | (const char *)... | | argvLocal.c:127:9:127:10 | i5 | semmle.label | i5 | -| argvLocal.c:128:2:128:13 | Argument 0 | semmle.label | Argument 0 | -| argvLocal.c:128:15:128:16 | Argument 0 indirection | semmle.label | Argument 0 indirection | +| argvLocal.c:128:2:128:13 | i5 | semmle.label | i5 | | argvLocal.c:128:15:128:16 | array to pointer conversion | semmle.label | array to pointer conversion | | argvLocal.c:128:15:128:16 | array to pointer conversion | semmle.label | array to pointer conversion | | argvLocal.c:128:15:128:16 | i5 | semmle.label | i5 | +| argvLocal.c:128:15:128:16 | i5 indirection | semmle.label | i5 indirection | | argvLocal.c:128:15:128:16 | printWrapper output argument | semmle.label | printWrapper output argument | | argvLocal.c:131:9:131:14 | (const char *)... | semmle.label | (const char *)... | | argvLocal.c:131:9:131:14 | (const char *)... | semmle.label | (const char *)... | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected index 6e90a21c7e1..1e66a438a5a 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected @@ -21,16 +21,19 @@ edges | globalVars.c:12:2:12:15 | Store | globalVars.c:8:7:8:10 | copy | | globalVars.c:15:21:15:23 | val | globalVars.c:16:2:16:12 | Store | | globalVars.c:16:2:16:12 | Store | globalVars.c:9:7:9:11 | copy2 | -| globalVars.c:24:11:24:14 | argv | globalVars.c:11:22:11:25 | *argv | -| globalVars.c:24:11:24:14 | argv | globalVars.c:11:22:11:25 | *argv | -| globalVars.c:24:11:24:14 | argv | globalVars.c:11:22:11:25 | argv | -| globalVars.c:24:11:24:14 | argv | globalVars.c:11:22:11:25 | argv | +| globalVars.c:24:2:24:9 | argv | globalVars.c:11:22:11:25 | argv | +| globalVars.c:24:11:24:14 | argv | globalVars.c:24:2:24:9 | argv | +| globalVars.c:24:11:24:14 | argv | globalVars.c:24:2:24:9 | argv | +| globalVars.c:24:11:24:14 | argv | globalVars.c:24:11:24:14 | argv indirection | +| globalVars.c:24:11:24:14 | argv | globalVars.c:24:11:24:14 | argv indirection | +| globalVars.c:24:11:24:14 | argv indirection | globalVars.c:11:22:11:25 | *argv | | globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | (const char *)... | | globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | copy | | globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy | | globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy | | globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy | -| globalVars.c:35:11:35:14 | copy | globalVars.c:15:21:15:23 | val | +| globalVars.c:35:2:35:9 | copy | globalVars.c:15:21:15:23 | val | +| globalVars.c:35:11:35:14 | copy | globalVars.c:35:2:35:9 | copy | | globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | (const char *)... | | globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | copy2 | | globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 | @@ -46,10 +49,10 @@ nodes | globalVars.c:12:2:12:15 | Store | semmle.label | Store | | globalVars.c:15:21:15:23 | val | semmle.label | val | | globalVars.c:16:2:16:12 | Store | semmle.label | Store | -| globalVars.c:24:2:24:9 | Argument 0 | semmle.label | Argument 0 | -| globalVars.c:24:11:24:14 | Argument 0 indirection | semmle.label | Argument 0 indirection | +| globalVars.c:24:2:24:9 | argv | semmle.label | argv | | globalVars.c:24:11:24:14 | argv | semmle.label | argv | | globalVars.c:24:11:24:14 | argv | semmle.label | argv | +| globalVars.c:24:11:24:14 | argv indirection | semmle.label | argv indirection | | globalVars.c:27:9:27:12 | (const char *)... | semmle.label | (const char *)... | | globalVars.c:27:9:27:12 | (const char *)... | semmle.label | (const char *)... | | globalVars.c:27:9:27:12 | copy | semmle.label | copy | @@ -58,7 +61,7 @@ nodes | globalVars.c:30:15:30:18 | copy | semmle.label | copy | | globalVars.c:30:15:30:18 | copy | semmle.label | copy | | globalVars.c:30:15:30:18 | copy | semmle.label | copy | -| globalVars.c:35:2:35:9 | Argument 0 | semmle.label | Argument 0 | +| globalVars.c:35:2:35:9 | copy | semmle.label | copy | | globalVars.c:35:11:35:14 | copy | semmle.label | copy | | globalVars.c:38:9:38:13 | (const char *)... | semmle.label | (const char *)... | | globalVars.c:38:9:38:13 | (const char *)... | semmle.label | (const char *)... | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected index 9876b9695ad..8b5e24af336 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected @@ -47,16 +47,18 @@ edges | test.cpp:214:23:214:23 | s | test.cpp:215:21:215:21 | s | | test.cpp:220:21:220:21 | s | test.cpp:221:21:221:21 | s | | test.cpp:220:21:220:21 | s | test.cpp:221:21:221:21 | s | -| test.cpp:227:24:227:29 | call to getenv | test.cpp:214:23:214:23 | s | -| test.cpp:227:24:227:29 | call to getenv | test.cpp:220:21:220:21 | s | | test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | (size_t)... | | test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | local_size | | test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | local_size | -| test.cpp:227:24:227:37 | (const char *)... | test.cpp:214:23:214:23 | s | -| test.cpp:227:24:227:37 | (const char *)... | test.cpp:220:21:220:21 | s | +| test.cpp:227:24:227:29 | call to getenv | test.cpp:235:2:235:9 | local_size | +| test.cpp:227:24:227:29 | call to getenv | test.cpp:237:2:237:8 | local_size | | test.cpp:227:24:227:37 | (const char *)... | test.cpp:229:9:229:18 | (size_t)... | | test.cpp:227:24:227:37 | (const char *)... | test.cpp:229:9:229:18 | local_size | | test.cpp:227:24:227:37 | (const char *)... | test.cpp:229:9:229:18 | local_size | +| test.cpp:227:24:227:37 | (const char *)... | test.cpp:235:2:235:9 | local_size | +| test.cpp:227:24:227:37 | (const char *)... | test.cpp:237:2:237:8 | local_size | +| test.cpp:235:2:235:9 | local_size | test.cpp:214:23:214:23 | s | +| test.cpp:237:2:237:8 | local_size | test.cpp:220:21:220:21 | s | | test.cpp:241:2:241:32 | Chi [array content] | test.cpp:279:17:279:20 | get_size output argument [array content] | | test.cpp:241:2:241:32 | Chi [array content] | test.cpp:295:18:295:21 | get_size output argument [array content] | | test.cpp:241:18:241:23 | call to getenv | test.cpp:241:2:241:32 | Chi [array content] | @@ -140,8 +142,8 @@ nodes | test.cpp:231:9:231:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | | test.cpp:231:9:231:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | | test.cpp:231:9:231:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | -| test.cpp:235:2:235:9 | Argument 0 | semmle.label | Argument 0 | -| test.cpp:237:2:237:8 | Argument 0 | semmle.label | Argument 0 | +| test.cpp:235:2:235:9 | local_size | semmle.label | local_size | +| test.cpp:237:2:237:8 | local_size | semmle.label | local_size | | test.cpp:241:2:241:32 | Chi [array content] | semmle.label | Chi [array content] | | test.cpp:241:2:241:32 | ChiPartial | semmle.label | ChiPartial | | test.cpp:241:18:241:23 | call to getenv | semmle.label | call to getenv | diff --git a/cpp/ql/test/query-tests/Summary/LinesOfCode.expected b/cpp/ql/test/query-tests/Summary/LinesOfCode.expected new file mode 100644 index 00000000000..a75c288c151 --- /dev/null +++ b/cpp/ql/test/query-tests/Summary/LinesOfCode.expected @@ -0,0 +1 @@ +| 93 | diff --git a/cpp/ql/test/query-tests/Summary/LinesOfCode.qlref b/cpp/ql/test/query-tests/Summary/LinesOfCode.qlref new file mode 100644 index 00000000000..b60eb791722 --- /dev/null +++ b/cpp/ql/test/query-tests/Summary/LinesOfCode.qlref @@ -0,0 +1 @@ +Summary/LinesOfCode.ql diff --git a/cpp/ql/test/query-tests/Summary/empty-file.cpp b/cpp/ql/test/query-tests/Summary/empty-file.cpp new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/cpp/ql/test/query-tests/Summary/empty-file.cpp @@ -0,0 +1 @@ + diff --git a/cpp/ql/test/query-tests/Summary/large-file.cpp b/cpp/ql/test/query-tests/Summary/large-file.cpp new file mode 100644 index 00000000000..b4af2da0cde --- /dev/null +++ b/cpp/ql/test/query-tests/Summary/large-file.cpp @@ -0,0 +1,123 @@ +int a00(float x) { + return (int)x; +} + +int a01(float x) { + return (int)x; +} + +int a02(float x) { + return (int)x; +} + +int a03(float x) { + return (int)x; +} + +int a04(float x) { + return (int)x; +} + +int a05(float x) { + return (int)x; +} + +int a06(float x) { + return (int)x; +} + +/** + * This is a multi-line comment + */ +int a07(float x) { + return (int)x; +} + +// this is a single-line comment +int a08(float x) { + return (int)x; +} + +int a09(float x) { + return (int)x; +} + +int a10(float x) { + return (int)x; +} + +int a11(float x) { + return (int)x; +} + +int a12(float x) { + return (int)x; +} + +int a13(float x) { + return (int)x; +} + +int a14(float x) { + return (int)x; +} + +int a15(float x) { + return (int)x; +} + +int a16(float x) { + return (int)x; +} + +int a17(float x) { + return (int)x; +} + +int a18(float x) { + return (int)x; +} + +int a19(float x) { + return (int)x; +} + +int a20(float x) { + return (int)x; +} + +int a21(float x) { + return (int)x; +} + +int a22(float x) { + return (int)x; +} + +int a23(float x) { + return (int)x; +} + +int a24(float x) { + return (int)x; +} + +int a25(float x) { + return (int)x; +} + +int a26(float x) { + return (int)x; +} + +int a27(float x) { + return (int)x; +} + +int a28(float x) { + return (int)x; +} + +int a29(float x) { + return (int)x; +} diff --git a/cpp/ql/test/query-tests/Summary/short-file.cpp b/cpp/ql/test/query-tests/Summary/short-file.cpp new file mode 100644 index 00000000000..e4055360d37 --- /dev/null +++ b/cpp/ql/test/query-tests/Summary/short-file.cpp @@ -0,0 +1,3 @@ +int g(float x) { + return (int)x; +} diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/Semmle.Autobuild.CSharp.Tests.csproj b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/Semmle.Autobuild.CSharp.Tests.csproj index ee3324eb639..64e25a8eb64 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/Semmle.Autobuild.CSharp.Tests.csproj +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/Semmle.Autobuild.CSharp.Tests.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net5.0 false win-x64;linux-x64;osx-x64 enable diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs index 5f29771c11b..ff6d2c804cc 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs @@ -97,21 +97,21 @@ namespace Semmle.Autobuild.CSharp /// private CSharpBuildStrategy GetCSharpBuildStrategy() { - if (Options.BuildCommand != null) + if (Options.BuildCommand is not null) return CSharpBuildStrategy.CustomBuildCommand; if (Options.Buildless) return CSharpBuildStrategy.Buildless; - if (Options.MsBuildArguments != null - || Options.MsBuildConfiguration != null - || Options.MsBuildPlatform != null - || Options.MsBuildTarget != null) + if (Options.MsBuildArguments is not null + || Options.MsBuildConfiguration is not null + || Options.MsBuildPlatform is not null + || Options.MsBuildTarget is not null) { return CSharpBuildStrategy.MSBuild; } - if (Options.DotNetArguments != null || Options.DotNetVersion != null) + if (Options.DotNetArguments is not null || Options.DotNetVersion is not null) return CSharpBuildStrategy.DotNet; return CSharpBuildStrategy.Auto; diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs index 0af1026e35a..8787406c104 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs @@ -27,7 +27,7 @@ namespace Semmle.Autobuild.CSharp .SelectMany(p => Enumerators.Singleton(p).Concat(p.IncludedProjects)) .OfType() .FirstOrDefault(p => !p.DotNetProject); - if (notDotNetProject != null) + if (notDotNetProject is not null) { builder.Log(Severity.Info, "Not using .NET Core because of incompatible project {0}", notDotNetProject); return BuildScript.Failure; @@ -103,7 +103,7 @@ namespace Semmle.Autobuild.CSharp if (!compatibleClr) { - if (env == null) + if (env is null) env = new Dictionary(); env.Add("UseSharedCompilation", "false"); } @@ -145,7 +145,7 @@ namespace Semmle.Autobuild.CSharp try { var o = JObject.Parse(File.ReadAllText(path)); - version = (string)o["sdk"]["version"]; + version = (string)(o?["sdk"]?["version"]!); } catch // lgtm[cs/catch-of-all-exceptions] { @@ -266,7 +266,7 @@ Invoke-Command -ScriptBlock $ScriptBlock"; } private static string DotNetCommand(IBuildActions actions, string? dotNetPath) => - dotNetPath != null ? actions.PathCombine(dotNetPath, "dotnet") : "dotnet"; + dotNetPath is not null ? actions.PathCombine(dotNetPath, "dotnet") : "dotnet"; private static BuildScript GetInfoCommand(IBuildActions actions, string? dotNetPath, IDictionary? environment) { diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/Semmle.Autobuild.CSharp.csproj b/csharp/autobuilder/Semmle.Autobuild.CSharp/Semmle.Autobuild.CSharp.csproj index 82ca05504bc..60291bfc308 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp/Semmle.Autobuild.CSharp.csproj +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/Semmle.Autobuild.CSharp.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net5.0 Semmle.Autobuild.CSharp Semmle.Autobuild.CSharp @@ -17,8 +17,8 @@ - - + + diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/StandaloneBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/StandaloneBuildRule.cs index 997d1db5522..e98f94f3b44 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp/StandaloneBuildRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/StandaloneBuildRule.cs @@ -13,11 +13,11 @@ namespace Semmle.Autobuild.CSharp BuildScript GetCommand(string? solution) { string standalone; - if (builder.CodeQLExtractorLangRoot is object && builder.CodeQlPlatform is object) + if (builder.CodeQLExtractorLangRoot is not null && builder.CodeQlPlatform is not null) { standalone = builder.Actions.PathCombine(builder.CodeQLExtractorLangRoot, "tools", builder.CodeQlPlatform, "Semmle.Extraction.CSharp.Standalone"); } - else if (builder.SemmlePlatformTools is object) + else if (builder.SemmlePlatformTools is not null) { standalone = builder.Actions.PathCombine(builder.SemmlePlatformTools, "csharp", "Semmle.Extraction.CSharp.Standalone"); } @@ -29,7 +29,7 @@ namespace Semmle.Autobuild.CSharp var cmd = new CommandBuilder(builder.Actions); cmd.RunCommand(standalone); - if (solution != null) + if (solution is not null) cmd.QuoteArgument(solution); cmd.Argument("--references:."); diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs index 0bcffa68496..3e8720ce676 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs @@ -59,7 +59,7 @@ namespace Semmle.Autobuild.Shared { public static bool AsBool(this string? value, string param, bool defaultValue) { - if (value == null) + if (value is null) return defaultValue; switch (value.ToLower()) @@ -81,7 +81,7 @@ namespace Semmle.Autobuild.Shared public static string[] AsListWithExpandedEnvVars(this string? value, IBuildActions actions, string[] defaultValue) { - if (value == null) + if (value is null) return defaultValue; return value. diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs index 29465bde90a..408672f7b97 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs @@ -176,12 +176,12 @@ namespace Semmle.Autobuild.Shared // First look for `.proj` files ret = FindFiles(".proj", f => new Project(this, f))?.ToList(); - if (ret != null) + if (ret is not null) return ret; // Then look for `.sln` files ret = FindFiles(".sln", f => new Solution(this, f, false))?.ToList(); - if (ret != null) + if (ret is not null) return ret; // Finally look for language specific project files, e.g. `.csproj` files diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/BuildActions.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildActions.cs index b9a5c9c17c3..9fd505d5457 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/BuildActions.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildActions.cs @@ -157,11 +157,11 @@ namespace Semmle.Autobuild.Shared UseShellExecute = false, RedirectStandardOutput = redirectStandardOutput }; - if (workingDirectory != null) + if (workingDirectory is not null) pi.WorkingDirectory = workingDirectory; // Environment variables can only be used when not redirecting stdout - if (!redirectStandardOutput && environment != null) + if (!redirectStandardOutput && environment is not null) environment.ForEach(kvp => pi.Environment[kvp.Key] = kvp.Value); return pi; } @@ -170,6 +170,10 @@ namespace Semmle.Autobuild.Shared { var pi = GetProcessStartInfo(cmd, args, workingDirectory, environment, false); using var p = Process.Start(pi); + if (p is null) + { + return -1; + } p.WaitForExit(); return p.ExitCode; } diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/BuildCommandAutoRule.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildCommandAutoRule.cs index dfbed17b2d7..fbb109f0b4c 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/BuildCommandAutoRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildCommandAutoRule.cs @@ -39,7 +39,7 @@ namespace Semmle.Autobuild.Shared var scripts = buildScripts.SelectMany(s => extensions.Select(e => s + e)); var scriptPath = builder.Paths.Where(p => scripts.Any(p.Item1.ToLower().EndsWith)).OrderBy(p => p.Item2).Select(p => p.Item1).FirstOrDefault(); - if (scriptPath == null) + if (scriptPath is null) return BuildScript.Failure; var chmod = new CommandBuilder(builder.Actions); @@ -55,7 +55,7 @@ namespace Semmle.Autobuild.Shared // A specific Visual Studio version may be required var vsTools = MsBuildRule.GetVcVarsBatFile(builder); - if (vsTools != null) + if (vsTools is not null) command.CallBatFile(vsTools.Path); builder.MaybeIndex(command, scriptPath); diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/BuildCommandRule.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildCommandRule.cs index daccb41d96c..453d3c4f9ad 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/BuildCommandRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildCommandRule.cs @@ -14,7 +14,7 @@ public BuildScript Analyse(Autobuilder builder, bool auto) { - if (builder.Options.BuildCommand == null) + if (builder.Options.BuildCommand is null) return BuildScript.Failure; // Custom build commands may require a specific .NET Core version @@ -24,7 +24,7 @@ // Custom build commands may require a specific Visual Studio version var vsTools = MsBuildRule.GetVcVarsBatFile(builder); - if (vsTools != null) + if (vsTools is not null) command.CallBatFile(vsTools.Path); builder.MaybeIndex(command, builder.Options.BuildCommand); diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/BuildScript.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildScript.cs index 954befd2c05..679db860c49 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/BuildScript.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildScript.cs @@ -150,13 +150,13 @@ namespace Semmle.Autobuild.Shared public override int Run(IBuildActions actions, Action startCallback, Action exitCallBack) { int ret1; - if (s2a != null) + if (s2a is not null) { ret1 = s1.Run(actions, startCallback, exitCallBack, out var stdout1); return s2a(stdout1, ret1).Run(actions, startCallback, exitCallBack); } - if (s2b != null) + if (s2b is not null) { ret1 = s1.Run(actions, startCallback, exitCallBack); return s2b(ret1).Run(actions, startCallback, exitCallBack); @@ -168,7 +168,7 @@ namespace Semmle.Autobuild.Shared public override int Run(IBuildActions actions, Action startCallback, Action exitCallBack, out IList stdout) { var ret1 = s1.Run(actions, startCallback, exitCallBack, out var stdout1); - var ret2 = (s2a != null ? s2a(stdout1, ret1) : s2b!(ret1)).Run(actions, startCallback, exitCallBack, out var stdout2); + var ret2 = (s2a is not null ? s2a(stdout1, ret1) : s2b!(ret1)).Run(actions, startCallback, exitCallBack, out var stdout2); var @out = new List(); @out.AddRange(stdout1); @out.AddRange(stdout2); diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/BuildTools.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildTools.cs index 79367c90d8e..a0079ca8bb9 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/BuildTools.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildTools.cs @@ -27,7 +27,7 @@ namespace Semmle.Autobuild.Shared public static IEnumerable GetCandidateVcVarsFiles(IBuildActions actions) { var programFilesx86 = actions.GetEnvironmentVariable("ProgramFiles(x86)"); - if (programFilesx86 == null) + if (programFilesx86 is null) yield break; // Attempt to use vswhere to find installations of Visual Studio @@ -84,7 +84,7 @@ namespace Semmle.Autobuild.Shared /// /// The solution file. /// A compatible file, or throws an exception. - public static VcVarsBatFile FindCompatibleVcVars(IBuildActions actions, ISolution sln) => + public static VcVarsBatFile? FindCompatibleVcVars(IBuildActions actions, ISolution sln) => FindCompatibleVcVars(actions, sln.ToolsVersion.Major); ///

    @@ -92,9 +92,9 @@ namespace Semmle.Autobuild.Shared /// /// The tools version. /// A compatible file, or null. - public static VcVarsBatFile FindCompatibleVcVars(IBuildActions actions, int targetVersion) => - targetVersion < 10 ? - VcVarsAllBatFiles(actions).OrderByDescending(b => b.ToolsVersion).FirstOrDefault() : - VcVarsAllBatFiles(actions).Where(b => b.ToolsVersion >= targetVersion).OrderBy(b => b.ToolsVersion).FirstOrDefault(); + public static VcVarsBatFile? FindCompatibleVcVars(IBuildActions actions, int targetVersion) => + targetVersion < 10 + ? VcVarsAllBatFiles(actions).OrderByDescending(b => b.ToolsVersion).FirstOrDefault() + : VcVarsAllBatFiles(actions).Where(b => b.ToolsVersion >= targetVersion).OrderBy(b => b.ToolsVersion).FirstOrDefault(); } } diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/CommandBuilder.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/CommandBuilder.cs index a5f49d4f435..e3b7bdbab34 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/CommandBuilder.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/CommandBuilder.cs @@ -145,7 +145,7 @@ namespace Semmle.Autobuild.Shared public CommandBuilder QuoteArgument(string argumentsOpt) { - if (argumentsOpt != null) + if (argumentsOpt is not null) { NextArgument(); ArgvQuote(argumentsOpt, false); @@ -161,7 +161,7 @@ namespace Semmle.Autobuild.Shared public CommandBuilder Argument(string? argumentsOpt) { - if (argumentsOpt != null) + if (argumentsOpt is not null) { NextArgument(); arguments.Append(argumentsOpt); @@ -185,7 +185,7 @@ namespace Semmle.Autobuild.Shared : (exe, null); NextCommand(); - if (executable == null) + if (executable is null) { executable = exe0; } diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/MsBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/MsBuildRule.cs index 30b7e047613..1a938f81a32 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/MsBuildRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/MsBuildRule.cs @@ -23,15 +23,15 @@ namespace Semmle.Autobuild.Shared var vsTools = GetVcVarsBatFile(builder); - if (vsTools == null && builder.ProjectsOrSolutionsToBuild.Any()) + if (vsTools is null && builder.ProjectsOrSolutionsToBuild.Any()) { var firstSolution = builder.ProjectsOrSolutionsToBuild.OfType().FirstOrDefault(); - vsTools = firstSolution != null + vsTools = firstSolution is not null ? BuildTools.FindCompatibleVcVars(builder.Actions, firstSolution) : BuildTools.VcVarsAllBatFiles(builder.Actions).OrderByDescending(b => b.ToolsVersion).FirstOrDefault(); } - if (vsTools == null && builder.Actions.IsWindows()) + if (vsTools is null && builder.Actions.IsWindows()) { builder.Log(Severity.Warning, "Could not find a suitable version of VsDevCmd.bat/vcvarsall.bat"); } @@ -85,7 +85,7 @@ namespace Semmle.Autobuild.Shared var command = new CommandBuilder(builder.Actions); - if (vsTools != null) + if (vsTools is not null) { command.CallBatFile(vsTools.Path); // `vcvarsall.bat` sets a default Platform environment variable, @@ -105,9 +105,9 @@ namespace Semmle.Autobuild.Shared var configuration = builder.Options.MsBuildConfiguration ?? (projectOrSolution is ISolution s2 ? s2.DefaultConfigurationName : null); command.Argument("/t:" + target); - if (platform != null) + if (platform is not null) command.Argument(string.Format("/p:Platform=\"{0}\"", platform)); - if (configuration != null) + if (configuration is not null) command.Argument(string.Format("/p:Configuration=\"{0}\"", configuration)); command.Argument("/p:MvcBuildViews=true"); @@ -130,7 +130,7 @@ namespace Semmle.Autobuild.Shared { VcVarsBatFile? vsTools = null; - if (builder.Options.VsToolsVersion != null) + if (builder.Options.VsToolsVersion is not null) { if (int.TryParse(builder.Options.VsToolsVersion, out var msToolsVersion)) { @@ -140,7 +140,7 @@ namespace Semmle.Autobuild.Shared } vsTools = BuildTools.FindCompatibleVcVars(builder.Actions, msToolsVersion); - if (vsTools == null) + if (vsTools is null) builder.Log(Severity.Warning, "Could not find build tools matching version {0}", msToolsVersion); else builder.Log(Severity.Info, "Setting Visual Studio tools to {0}", vsTools.Path); diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/Project.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/Project.cs index aaaa7cdac56..bdc712d6623 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/Project.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Project.cs @@ -47,7 +47,7 @@ namespace Semmle.Autobuild.Shared var root = projFile.DocumentElement; - if (root.Name == "Project") + if (root?.Name == "Project") { if (root.HasAttribute("Sdk")) { @@ -77,10 +77,17 @@ namespace Semmle.Autobuild.Shared // `` and `` is valid var mgr = new XmlNamespaceManager(projFile.NameTable); mgr.AddNamespace("msbuild", "http://schemas.microsoft.com/developer/msbuild/2003"); - var projectFileIncludes = root.SelectNodes("//msbuild:Project/msbuild:ItemGroup/msbuild:ProjectFile/@Include", mgr).OfType(); - var projectFilesIncludes = root.SelectNodes("//msbuild:Project/msbuild:ItemGroup/msbuild:ProjectFiles/@Include", mgr).OfType(); + var projectFileIncludes = root.SelectNodes("//msbuild:Project/msbuild:ItemGroup/msbuild:ProjectFile/@Include", mgr) + ?.OfType() ?? Array.Empty(); + var projectFilesIncludes = root.SelectNodes("//msbuild:Project/msbuild:ItemGroup/msbuild:ProjectFiles/@Include", mgr) + ?.OfType() ?? Array.Empty(); foreach (var include in projectFileIncludes.Concat(projectFilesIncludes)) { + if (include?.Value is null) + { + continue; + } + var includePath = builder.Actions.PathCombine(include.Value.Split('\\', StringSplitOptions.RemoveEmptyEntries)); ret.Add(new Project(builder, builder.Actions.PathCombine(DirectoryName, includePath))); } diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/Semmle.Autobuild.Shared.csproj b/csharp/autobuilder/Semmle.Autobuild.Shared/Semmle.Autobuild.Shared.csproj index 6663c428b03..ae1c61bc992 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/Semmle.Autobuild.Shared.csproj +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Semmle.Autobuild.Shared.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net5.0 Semmle.Autobuild.Shared Semmle.Autobuild.Shared false @@ -14,7 +14,7 @@
    - + diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/Solution.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/Solution.cs index c5d65794fb5..0ec54c1e02f 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Shared/Solution.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Solution.cs @@ -49,13 +49,13 @@ namespace Semmle.Autobuild.Shared public override IEnumerable IncludedProjects => includedProjects; public IEnumerable Configurations => - solution == null ? Enumerable.Empty() : solution.SolutionConfigurations; + solution is null ? Enumerable.Empty() : solution.SolutionConfigurations; public string DefaultConfigurationName => - solution == null ? "" : solution.GetDefaultConfigurationName(); + solution is null ? "" : solution.GetDefaultConfigurationName(); public string DefaultPlatformName => - solution == null ? "" : solution.GetDefaultPlatformName(); + solution is null ? "" : solution.GetDefaultPlatformName(); public Solution(Autobuilder builder, string path, bool allowProject) : base(builder, path) { @@ -90,7 +90,7 @@ namespace Semmle.Autobuild.Shared .Select(p => p.ToolsVersion); public Version ToolsVersion => ToolsVersions.Any() - ? ToolsVersions.Max() + ? ToolsVersions.Max()! : new Version(); } } diff --git a/csharp/change-notes/2021-02-26-tuple-dataflow.md b/csharp/change-notes/2021-02-26-tuple-dataflow.md new file mode 100644 index 00000000000..4526bb5db87 --- /dev/null +++ b/csharp/change-notes/2021-02-26-tuple-dataflow.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* Data flow for tuples has been improved to track data flowing into and out of +tuple fields. Tuple extraction was changed to extract non-named tuple elements. diff --git a/csharp/change-notes/2021-03-01-fluent-interface-data-flow.md b/csharp/change-notes/2021-03-01-fluent-interface-data-flow.md new file mode 100644 index 00000000000..f1492afcf31 --- /dev/null +++ b/csharp/change-notes/2021-03-01-fluent-interface-data-flow.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The data-flow library now recognises more side-effects of method chaining (e.g. `someObject.setX(clean).setY(tainted).setZ...` having a side-effect on `someObject`), as well as other related circumstances where a function input is directly passed to its output. All queries that use data-flow analysis, including most security queries, may return more results accordingly. diff --git a/csharp/change-notes/2021-03-02-dotnet5.md b/csharp/change-notes/2021-03-02-dotnet5.md new file mode 100644 index 00000000000..57ec8bc485d --- /dev/null +++ b/csharp/change-notes/2021-03-02-dotnet5.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The extractor has been updated to use dotnet 5 dependencies. diff --git a/csharp/extractor/Semmle.Extraction.CIL.Driver/Program.cs b/csharp/extractor/Semmle.Extraction.CIL.Driver/Program.cs index 53542c970e9..940cd647ed8 100644 --- a/csharp/extractor/Semmle.Extraction.CIL.Driver/Program.cs +++ b/csharp/extractor/Semmle.Extraction.CIL.Driver/Program.cs @@ -24,7 +24,7 @@ namespace Semmle.Extraction.CIL.Driver { var sw = new Stopwatch(); sw.Start(); - Entities.Assembly.ExtractCIL(layout, assemblyPath, logger, nocache, extractPdbs, trapCompression, out _, out _); + Analyser.ExtractCIL(layout, assemblyPath, logger, nocache, extractPdbs, trapCompression, out _, out _); sw.Stop(); logger.Log(Severity.Info, " {0} ({1})", assemblyPath, sw.Elapsed); } diff --git a/csharp/extractor/Semmle.Extraction.CIL.Driver/Semmle.Extraction.CIL.Driver.csproj b/csharp/extractor/Semmle.Extraction.CIL.Driver/Semmle.Extraction.CIL.Driver.csproj index 67e40dae2d8..dea12d5d012 100644 --- a/csharp/extractor/Semmle.Extraction.CIL.Driver/Semmle.Extraction.CIL.Driver.csproj +++ b/csharp/extractor/Semmle.Extraction.CIL.Driver/Semmle.Extraction.CIL.Driver.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net5.0 Semmle.Extraction.CIL.Driver Semmle.Extraction.CIL.Driver false diff --git a/csharp/extractor/Semmle.Extraction.CIL/Analyser.cs b/csharp/extractor/Semmle.Extraction.CIL/Analyser.cs new file mode 100644 index 00000000000..d93d7eb7dc7 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CIL/Analyser.cs @@ -0,0 +1,53 @@ +using Semmle.Util.Logging; +using System; +using Semmle.Util; +using Semmle.Extraction.CIL.Entities; + +namespace Semmle.Extraction.CIL +{ + public static class Analyser + { + private static void ExtractCIL(TracingExtractor extractor, TrapWriter trapWriter, bool extractPdbs) + { + using var cilContext = new Context(extractor, trapWriter, extractor.OutputPath, extractPdbs); + cilContext.Populate(new Assembly(cilContext)); + cilContext.PopulateAll(); + } + + /// + /// Main entry point to the CIL extractor. + /// Call this to extract a given assembly. + /// + /// The trap layout. + /// The full path of the assembly to extract. + /// The logger. + /// True to overwrite existing trap file. + /// Whether to extract PDBs. + /// The path of the trap file. + /// Whether the file was extracted (false=cached). + public static void ExtractCIL(Layout layout, string assemblyPath, ILogger logger, bool nocache, bool extractPdbs, TrapWriter.CompressionMode trapCompression, out string trapFile, out bool extracted) + { + trapFile = ""; + extracted = false; + try + { + var canonicalPathCache = CanonicalPathCache.Create(logger, 1000); + var pathTransformer = new PathTransformer(canonicalPathCache); + var extractor = new TracingExtractor(assemblyPath, logger, pathTransformer); + var transformedAssemblyPath = pathTransformer.Transform(assemblyPath); + var project = layout.LookupProjectOrDefault(transformedAssemblyPath); + using var trapWriter = project.CreateTrapWriter(logger, transformedAssemblyPath.WithSuffix(".cil"), trapCompression, discardDuplicates: true); + trapFile = trapWriter.TrapFile; + if (nocache || !System.IO.File.Exists(trapFile)) + { + ExtractCIL(extractor, trapWriter, extractPdbs); + extracted = true; + } + } + catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] + { + logger.Log(Severity.Error, string.Format("Exception extracting {0}: {1}", assemblyPath, ex)); + } + } + } +} diff --git a/csharp/extractor/Semmle.Extraction.CIL/Context.Factories.cs b/csharp/extractor/Semmle.Extraction.CIL/Context.Factories.cs index 36e420e3628..0cdc5bf8dac 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Context.Factories.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Context.Factories.cs @@ -9,11 +9,11 @@ namespace Semmle.Extraction.CIL /// /// Provides methods for creating and caching various entities. /// - public sealed partial class Context + internal sealed partial class Context { private readonly Dictionary ids = new Dictionary(); - public T Populate(T e) where T : IExtractedEntity + internal T Populate(T e) where T : IExtractedEntity { if (e.Label.Valid) { @@ -27,10 +27,10 @@ namespace Semmle.Extraction.CIL } else { - e.Label = Cx.GetNewLabel(); - Cx.DefineLabel(e, Cx.TrapWriter.Writer, Cx.Extractor); + e.Label = GetNewLabel(); + DefineLabel(e); ids.Add(e, e.Label); - Cx.PopulateLater(() => + PopulateLater(() => { foreach (var c in e.Contents) c.Extract(this); @@ -42,7 +42,7 @@ namespace Semmle.Extraction.CIL if (debugLabels.TryGetValue(id, out var previousEntity)) { - Cx.Extractor.Message(new Message("Duplicate trap ID", id, null, severity: Util.Logging.Severity.Warning)); + Extractor.Message(new Message("Duplicate trap ID", id, null, severity: Util.Logging.Severity.Warning)); } else { @@ -74,9 +74,9 @@ namespace Semmle.Extraction.CIL { e = new PrimitiveType(this, code) { - Label = Cx.GetNewLabel() + Label = GetNewLabel() }; - Cx.DefineLabel(e, Cx.TrapWriter.Writer, Cx.Extractor); + DefineLabel(e); primitiveTypes[(int)code] = e; } @@ -114,7 +114,7 @@ namespace Semmle.Extraction.CIL entity = new MethodSpecificationMethod(gc, (MethodSpecificationHandle)handle); break; case HandleKind.FieldDefinition: - entity = new DefinitionField(gc.Cx, (FieldDefinitionHandle)handle); + entity = new DefinitionField(gc.Context, (FieldDefinitionHandle)handle); break; case HandleKind.TypeReference: var tr = new TypeReferenceType(this, (TypeReferenceHandle)handle); @@ -128,6 +128,11 @@ namespace Semmle.Extraction.CIL case HandleKind.TypeDefinition: entity = new TypeDefinitionType(this, (TypeDefinitionHandle)handle); break; + case HandleKind.StandaloneSignature: + var signature = MdReader.GetStandaloneSignature((StandaloneSignatureHandle)handle); + var method = signature.DecodeMethodSignature(gc.Context.TypeSignatureDecoder, gc); + entity = new FunctionPointerType(this, method); + break; default: throw new InternalError("Unhandled handle kind " + handle.Kind); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Context.cs b/csharp/extractor/Semmle.Extraction.CIL/Context.cs index 99113714b7c..9aa1917dd3e 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Context.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Context.cs @@ -10,12 +10,10 @@ namespace Semmle.Extraction.CIL /// Adds additional context that is specific for CIL extraction. /// One context = one DLL/EXE. /// - public sealed partial class Context : IDisposable + internal sealed partial class Context : Extraction.Context, IDisposable { private readonly FileStream stream; private Entities.Assembly? assemblyNull; - - public Extraction.Context Cx { get; } public MetadataReader MdReader { get; } public PEReader PeReader { get; } public string AssemblyPath { get; } @@ -26,9 +24,9 @@ namespace Semmle.Extraction.CIL } public PDB.IPdb? Pdb { get; } - public Context(Extraction.Context cx, string assemblyPath, bool extractPdbs) + public Context(Extractor extractor, TrapWriter trapWriter, string assemblyPath, bool extractPdbs) + : base(extractor, trapWriter) { - this.Cx = cx; this.AssemblyPath = assemblyPath; stream = File.OpenRead(assemblyPath); PeReader = new PEReader(stream, PEStreamOptions.PrefetchEntireImage); @@ -49,16 +47,16 @@ namespace Semmle.Extraction.CIL if (extractPdbs) { Pdb = PDB.PdbReader.Create(assemblyPath, PeReader); - if (Pdb != null) + if (Pdb is not null) { - cx.Extractor.Logger.Log(Util.Logging.Severity.Info, string.Format("Found PDB information for {0}", assemblyPath)); + Extractor.Logger.Log(Util.Logging.Severity.Info, string.Format("Found PDB information for {0}", assemblyPath)); } } } public void Dispose() { - if (Pdb != null) + if (Pdb is not null) Pdb.Dispose(); PeReader.Dispose(); stream.Dispose(); diff --git a/csharp/extractor/Semmle.Extraction.CIL/EmptyContext.cs b/csharp/extractor/Semmle.Extraction.CIL/EmptyContext.cs index 1105bc39cba..8404bc26545 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/EmptyContext.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/EmptyContext.cs @@ -5,14 +5,14 @@ namespace Semmle.Extraction.CIL /// /// A generic context which does not contain any type parameters. /// - public class EmptyContext : IGenericContext + internal class EmptyContext : IGenericContext { public EmptyContext(Context cx) { - Cx = cx; + Context = cx; } - public Context Cx { get; } + public Context Context { get; } public IEnumerable TypeParameters { get { yield break; } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/ArrayType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/ArrayType.cs index ae04add2b12..b838a9070ff 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/ArrayType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/ArrayType.cs @@ -42,7 +42,7 @@ namespace Semmle.Extraction.CIL.Entities public override string Name => elementType.Name + "[]"; - public override Namespace ContainingNamespace => Cx.SystemNamespace; + public override Namespace ContainingNamespace => Context.SystemNamespace; public override Type? ContainingType => null; @@ -50,9 +50,9 @@ namespace Semmle.Extraction.CIL.Entities public override CilTypeKind Kind => CilTypeKind.Array; - public override Type Construct(IEnumerable typeArguments) => Cx.Populate(new ArrayType(Cx, elementType.Construct(typeArguments))); + public override Type Construct(IEnumerable typeArguments) => Context.Populate(new ArrayType(Context, elementType.Construct(typeArguments))); - public override Type SourceDeclaration => Cx.Populate(new ArrayType(Cx, elementType.SourceDeclaration)); + public override Type SourceDeclaration => Context.Populate(new ArrayType(Context, elementType.SourceDeclaration)); public override IEnumerable Contents { diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs index ba332b2db97..d28fa0035c3 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs @@ -1,18 +1,15 @@ using System.Reflection; using System.Globalization; using System.Collections.Generic; -using Semmle.Util.Logging; -using System; using Semmle.Extraction.Entities; using System.IO; -using Semmle.Util; namespace Semmle.Extraction.CIL.Entities { /// /// An assembly to extract. /// - public class Assembly : LabelledEntity, ILocation + internal class Assembly : LabelledEntity, ILocation { private readonly File file; private readonly AssemblyName assemblyName; @@ -39,7 +36,7 @@ namespace Semmle.Extraction.CIL.Entities { trapFile.Write(FullName); trapFile.Write("#file:///"); - trapFile.Write(Cx.AssemblyPath.Replace("\\", "/")); + trapFile.Write(Context.AssemblyPath.Replace("\\", "/")); trapFile.Write(";assembly"); } @@ -59,91 +56,47 @@ namespace Semmle.Extraction.CIL.Entities yield return file; yield return Tuples.assemblies(this, file, FullName, assemblyName.Name ?? string.Empty, assemblyName.Version?.ToString() ?? string.Empty); - if (Cx.Pdb != null) + if (Context.Pdb is not null) { - foreach (var f in Cx.Pdb.SourceFiles) + foreach (var f in Context.Pdb.SourceFiles) { - yield return Cx.CreateSourceFile(f); + yield return Context.CreateSourceFile(f); } } - foreach (var handle in Cx.MdReader.TypeDefinitions) + foreach (var handle in Context.MdReader.TypeDefinitions) { IExtractionProduct? product = null; try { - product = Cx.Create(handle); + product = Context.Create(handle); } catch (InternalError e) { - Cx.Cx.ExtractionError("Error processing type definition", e.Message, GeneratedLocation.Create(Cx.Cx), e.StackTrace); + Context.ExtractionError("Error processing type definition", e.Message, GeneratedLocation.Create(Context), e.StackTrace); } // Limitation of C#: Cannot yield return inside a try-catch. - if (product != null) + if (product is not null) yield return product; } - foreach (var handle in Cx.MdReader.MethodDefinitions) + foreach (var handle in Context.MdReader.MethodDefinitions) { IExtractionProduct? product = null; try { - product = Cx.Create(handle); + product = Context.Create(handle); } catch (InternalError e) { - Cx.Cx.ExtractionError("Error processing bytecode", e.Message, GeneratedLocation.Create(Cx.Cx), e.StackTrace); + Context.ExtractionError("Error processing bytecode", e.Message, GeneratedLocation.Create(Context), e.StackTrace); } - if (product != null) + if (product is not null) yield return product; } } } - - private static void ExtractCIL(Extraction.Context cx, string assemblyPath, bool extractPdbs) - { - using var cilContext = new Context(cx, assemblyPath, extractPdbs); - cilContext.Populate(new Assembly(cilContext)); - cilContext.Cx.PopulateAll(); - } - - /// - /// Main entry point to the CIL extractor. - /// Call this to extract a given assembly. - /// - /// The trap layout. - /// The full path of the assembly to extract. - /// The logger. - /// True to overwrite existing trap file. - /// Whether to extract PDBs. - /// The path of the trap file. - /// Whether the file was extracted (false=cached). - public static void ExtractCIL(Layout layout, string assemblyPath, ILogger logger, bool nocache, bool extractPdbs, TrapWriter.CompressionMode trapCompression, out string trapFile, out bool extracted) - { - trapFile = ""; - extracted = false; - try - { - var canonicalPathCache = CanonicalPathCache.Create(logger, 1000); - var pathTransformer = new PathTransformer(canonicalPathCache); - var extractor = new Extractor(false, assemblyPath, logger, pathTransformer); - var transformedAssemblyPath = pathTransformer.Transform(assemblyPath); - var project = layout.LookupProjectOrDefault(transformedAssemblyPath); - using var trapWriter = project.CreateTrapWriter(logger, transformedAssemblyPath.WithSuffix(".cil"), trapCompression, discardDuplicates: true); - trapFile = trapWriter.TrapFile; - if (nocache || !System.IO.File.Exists(trapFile)) - { - var cx = new Extraction.Context(extractor, trapWriter); - ExtractCIL(cx, assemblyPath, extractPdbs); - extracted = true; - } - } - catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] - { - logger.Log(Severity.Error, string.Format("Exception extracting {0}: {1}", assemblyPath, ex)); - } - } } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Attribute.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Attribute.cs index d79215f0adf..03c916e58ea 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Attribute.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Attribute.cs @@ -32,7 +32,7 @@ namespace Semmle.Extraction.CIL.Entities { get { - var constructor = (Method)Cx.Create(attrib.Constructor); + var constructor = (Method)Context.Create(attrib.Constructor); yield return constructor; yield return Tuples.cil_attribute(this, @object, constructor); @@ -41,11 +41,11 @@ namespace Semmle.Extraction.CIL.Entities try { - decoded = attrib.DecodeValue(new CustomAttributeDecoder(Cx)); + decoded = attrib.DecodeValue(new CustomAttributeDecoder(Context)); } catch { - Cx.Cx.Extractor.Logger.Log(Util.Logging.Severity.Info, + Context.Extractor.Logger.Log(Util.Logging.Severity.Info, $"Attribute decoding is partial. Decoding attribute {constructor.DeclaringType.GetQualifiedName()} failed on {@object}."); yield break; } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/IExtractedEntity.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/IExtractedEntity.cs index 080227a63b9..d94b94df8a8 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/IExtractedEntity.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/IExtractedEntity.cs @@ -5,7 +5,7 @@ namespace Semmle.Extraction.CIL /// /// A CIL entity which has been extracted. /// - public interface IExtractedEntity : IExtractionProduct, IEntity + internal interface IExtractedEntity : IExtractionProduct, IEntity { /// /// The contents of the entity. diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/IExtractionProduct.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/IExtractionProduct.cs index 6383d7e1b4c..231a311a78c 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/IExtractionProduct.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/IExtractionProduct.cs @@ -12,7 +12,7 @@ namespace Semmle.Extraction.CIL /// - Enumerate Contents to produce more extraction products /// - Extract these until there is nothing left to extract /// - public interface IExtractionProduct + internal interface IExtractionProduct { /// /// Perform further extraction/population of this item as necessary. diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/IGenericContext.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/IGenericContext.cs index 21c69f7aeb4..babe0bdfd65 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/IGenericContext.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/IGenericContext.cs @@ -6,9 +6,9 @@ namespace Semmle.Extraction.CIL /// When we decode a type/method signature, we need access to /// generic parameters. /// - public interface IGenericContext + internal interface IGenericContext { - Context Cx { get; } + Context Context { get; } /// /// The list of generic type parameters/arguments, including type parameters/arguments of diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/LabelledEntity.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/LabelledEntity.cs index a66ecbb1fab..150074f22ee 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/LabelledEntity.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/LabelledEntity.cs @@ -8,14 +8,12 @@ namespace Semmle.Extraction.CIL /// An entity that needs to be populated during extraction. /// This assigns a key and optionally extracts its contents. /// - public abstract class LabelledEntity : Extraction.LabelledEntity, IExtractedEntity + internal abstract class LabelledEntity : Extraction.LabelledEntity, IExtractedEntity { - // todo: with .NET 5 this can override the base context, and change the return type. - public Context Cx { get; } + public override Context Context => (Context)base.Context; - protected LabelledEntity(Context cx) : base(cx.Cx) + protected LabelledEntity(Context cx) : base(cx) { - this.Cx = cx; } public override Microsoft.CodeAnalysis.Location ReportingLocation => throw new NotImplementedException(); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/Tuple.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/Tuple.cs index 5657f072c9c..58c15dd21f6 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/Tuple.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/Tuple.cs @@ -14,7 +14,7 @@ namespace Semmle.Extraction.CIL public void Extract(Context cx) { - cx.Cx.Emit(tuple); + cx.TrapWriter.Emit(tuple); } public override string ToString() => tuple.ToString(); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/UnlabelledEntity.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/UnlabelledEntity.cs index d8c18d291eb..8f3d340d104 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/UnlabelledEntity.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/UnlabelledEntity.cs @@ -7,14 +7,12 @@ namespace Semmle.Extraction.CIL /// An entity that has contents to extract. There is no need to populate /// a key as it's done in the contructor. /// - public abstract class UnlabelledEntity : Extraction.UnlabelledEntity, IExtractedEntity + internal abstract class UnlabelledEntity : Extraction.UnlabelledEntity, IExtractedEntity { - // todo: with .NET 5 this can override the base context, and change the return type. - public Context Cx { get; } + public override Context Context => (Context)base.Context; - protected UnlabelledEntity(Context cx) : base(cx.Cx) + protected UnlabelledEntity(Context cx) : base(cx) { - Cx = cx; } public override Microsoft.CodeAnalysis.Location ReportingLocation => throw new NotImplementedException(); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/ConstructedType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/ConstructedType.cs index be2ab5da4d8..6e2fb90beae 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/ConstructedType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/ConstructedType.cs @@ -9,7 +9,7 @@ namespace Semmle.Extraction.CIL.Entities /// /// A constructed type. /// - public sealed class ConstructedType : Type + internal sealed class ConstructedType : Type { private readonly Type unboundGenericType; diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/CustomAttributeDecoder.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/CustomAttributeDecoder.cs index 94d6cc0ab6d..184e37a1adf 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/CustomAttributeDecoder.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/CustomAttributeDecoder.cs @@ -40,11 +40,11 @@ namespace Semmle.Extraction.CIL.Entities if (wellKnownEnums.TryGetValue(name, out var code)) { - cx.Cx.Extractor.Logger.Log(Util.Logging.Severity.Debug, $"Using hard coded underlying enum type for {name}"); + cx.Extractor.Logger.Log(Util.Logging.Severity.Debug, $"Using hard coded underlying enum type for {name}"); return code; } - cx.Cx.Extractor.Logger.Log(Util.Logging.Severity.Info, $"Couldn't get underlying enum type for {name}"); + cx.Extractor.Logger.Log(Util.Logging.Severity.Info, $"Couldn't get underlying enum type for {name}"); // We can't fall back to Int32, because the type returned here defines how many bytes are read from the // stream and how those bytes are interpreted. @@ -56,7 +56,8 @@ namespace Semmle.Extraction.CIL.Entities private static readonly Dictionary wellKnownEnums = new Dictionary { { "System.AttributeTargets", PrimitiveTypeCode.Int32 }, - { "System.ComponentModel.EditorBrowsableState", PrimitiveTypeCode.Int32 } + { "System.ComponentModel.EditorBrowsableState", PrimitiveTypeCode.Int32 }, + { "System.Diagnostics.DebuggerBrowsableState", PrimitiveTypeCode.Int32 } }; } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/DefinitionField.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/DefinitionField.cs index 253346b47be..69fd9cbd5d4 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/DefinitionField.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/DefinitionField.cs @@ -14,7 +14,7 @@ namespace Semmle.Extraction.CIL.Entities public DefinitionField(Context cx, FieldDefinitionHandle handle) : base(cx) { this.handle = handle; - fd = Cx.MdReader.GetFieldDefinition(handle); + fd = Context.MdReader.GetFieldDefinition(handle); } public override bool Equals(object? obj) @@ -28,7 +28,7 @@ namespace Semmle.Extraction.CIL.Entities { get { - yield return Tuples.metadata_handle(this, Cx.Assembly, MetadataTokens.GetToken(handle)); + yield return Tuples.metadata_handle(this, Context.Assembly, MetadataTokens.GetToken(handle)); foreach (var c in base.Contents) yield return c; @@ -48,16 +48,16 @@ namespace Semmle.Extraction.CIL.Entities if (fd.Attributes.HasFlag(FieldAttributes.Assembly)) yield return Tuples.cil_internal(this); - foreach (var c in Attribute.Populate(Cx, this, fd.GetCustomAttributes())) + foreach (var c in Attribute.Populate(Context, this, fd.GetCustomAttributes())) yield return c; } } - public override string Name => Cx.GetString(fd.Name); + public override string Name => Context.GetString(fd.Name); - public override Type DeclaringType => (Type)Cx.Create(fd.GetDeclaringType()); + public override Type DeclaringType => (Type)Context.Create(fd.GetDeclaringType()); - public override Type Type => fd.DecodeSignature(Cx.TypeSignatureDecoder, DeclaringType); + public override Type Type => fd.DecodeSignature(Context.TypeSignatureDecoder, DeclaringType); public override IEnumerable TypeParameters => throw new NotImplementedException(); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/DefinitionMethod.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/DefinitionMethod.cs index 8ff7fcf24bf..5c48e5e52e1 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/DefinitionMethod.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/DefinitionMethod.cs @@ -25,16 +25,16 @@ namespace Semmle.Extraction.CIL.Entities public DefinitionMethod(IGenericContext gc, MethodDefinitionHandle handle) : base(gc) { - md = Cx.MdReader.GetMethodDefinition(handle); + md = Context.MdReader.GetMethodDefinition(handle); this.gc = gc; this.handle = handle; - name = Cx.GetString(md.Name); + name = Context.GetString(md.Name); - declaringType = (Type)Cx.CreateGeneric(this, md.GetDeclaringType()); + declaringType = (Type)Context.CreateGeneric(this, md.GetDeclaringType()); signature = md.DecodeSignature(new SignatureDecoder(), this); - methodDebugInformation = Cx.GetMethodDebugInformation(handle); + methodDebugInformation = Context.GetMethodDebugInformation(handle); } public override bool Equals(object? obj) @@ -48,7 +48,7 @@ namespace Semmle.Extraction.CIL.Entities public override Type DeclaringType => declaringType; - public override string Name => Cx.ShortName(md.Name); + public override string Name => Context.ShortName(md.Name); public override string NameLabel => name; @@ -67,14 +67,14 @@ namespace Semmle.Extraction.CIL.Entities // depend on other type parameters (as a constraint). genericParams = new MethodTypeParameter[md.GetGenericParameters().Count]; for (var i = 0; i < genericParams.Length; ++i) - genericParams[i] = Cx.Populate(new MethodTypeParameter(this, this, i)); + genericParams[i] = Context.Populate(new MethodTypeParameter(this, this, i)); for (var i = 0; i < genericParams.Length; ++i) genericParams[i].PopulateHandle(md.GetGenericParameters()[i]); foreach (var p in genericParams) yield return p; } - var typeSignature = md.DecodeSignature(Cx.TypeSignatureDecoder, this); + var typeSignature = md.DecodeSignature(Context.TypeSignatureDecoder, this); var parameters = GetParameterExtractionProducts(typeSignature.ParameterTypes).ToArray(); Parameters = parameters.OfType().ToArray(); @@ -85,17 +85,17 @@ namespace Semmle.Extraction.CIL.Entities foreach (var c in PopulateFlags) yield return c; - foreach (var p in md.GetParameters().Select(h => Cx.MdReader.GetParameter(h)).Where(p => p.SequenceNumber > 0)) + foreach (var p in md.GetParameters().Select(h => Context.MdReader.GetParameter(h)).Where(p => p.SequenceNumber > 0)) { var pe = Parameters[IsStatic ? p.SequenceNumber - 1 : p.SequenceNumber]; if (p.Attributes.HasFlag(ParameterAttributes.Out)) yield return Tuples.cil_parameter_out(pe); if (p.Attributes.HasFlag(ParameterAttributes.In)) yield return Tuples.cil_parameter_in(pe); - Attribute.Populate(Cx, pe, p.GetCustomAttributes()); + Attribute.Populate(Context, pe, p.GetCustomAttributes()); } - yield return Tuples.metadata_handle(this, Cx.Assembly, MetadataTokens.GetToken(handle)); + yield return Tuples.metadata_handle(this, Context.Assembly, MetadataTokens.GetToken(handle)); foreach (var m in GetMethodExtractionProducts(Name, declaringType, typeSignature.ReturnType)) { @@ -103,19 +103,19 @@ namespace Semmle.Extraction.CIL.Entities } yield return Tuples.cil_method_source_declaration(this, this); - yield return Tuples.cil_method_location(this, Cx.Assembly); + yield return Tuples.cil_method_location(this, Context.Assembly); if (HasBytecode) { Implementation = new MethodImplementation(this); yield return Implementation; - var body = Cx.PeReader.GetMethodBody(md.RelativeVirtualAddress); + var body = Context.PeReader.GetMethodBody(md.RelativeVirtualAddress); if (!body.LocalSignature.IsNil) { - var locals = Cx.MdReader.GetStandaloneSignature(body.LocalSignature); - var localVariableTypes = locals.DecodeLocalSignature(Cx.TypeSignatureDecoder, this); + var locals = Context.MdReader.GetStandaloneSignature(body.LocalSignature); + var localVariableTypes = locals.DecodeLocalSignature(Context.TypeSignatureDecoder, this); this.locals = new LocalVariable[localVariableTypes.Length]; @@ -125,13 +125,13 @@ namespace Semmle.Extraction.CIL.Entities if (t is ByRefType brt) { t = brt.ElementType; - this.locals[l] = Cx.Populate(new LocalVariable(Cx, Implementation, l, t)); + this.locals[l] = Context.Populate(new LocalVariable(Context, Implementation, l, t)); yield return this.locals[l]; yield return Tuples.cil_type_annotation(this.locals[l], TypeAnnotation.Ref); } else { - this.locals[l] = Cx.Populate(new LocalVariable(Cx, Implementation, l, t)); + this.locals[l] = Context.Populate(new LocalVariable(Context, Implementation, l, t)); yield return this.locals[l]; } } @@ -150,9 +150,9 @@ namespace Semmle.Extraction.CIL.Entities yield return Tuples.cil_method_stack_size(Implementation, body.MaxStack); - if (methodDebugInformation != null) + if (methodDebugInformation is not null) { - var sourceLocation = Cx.CreateSourceLocation(methodDebugInformation.Location); + var sourceLocation = Context.CreateSourceLocation(methodDebugInformation.Location); yield return sourceLocation; yield return Tuples.cil_method_location(this, sourceLocation); } @@ -191,7 +191,7 @@ namespace Semmle.Extraction.CIL.Entities yield return Tuples.cil_newslot(this); // Populate attributes - Attribute.Populate(Cx, this, md.GetCustomAttributes()); + Attribute.Populate(Context, this, md.GetCustomAttributes()); } } @@ -205,12 +205,12 @@ namespace Semmle.Extraction.CIL.Entities IEnumerator? nextSequencePoint = null; PdbSourceLocation? instructionLocation = null; - if (methodDebugInformation != null) + if (methodDebugInformation is not null) { nextSequencePoint = methodDebugInformation.SequencePoints.GetEnumerator(); if (nextSequencePoint.MoveNext()) { - instructionLocation = Cx.CreateSourceLocation(nextSequencePoint.Current.Location); + instructionLocation = Context.CreateSourceLocation(nextSequencePoint.Current.Location); yield return instructionLocation; } else @@ -222,18 +222,18 @@ namespace Semmle.Extraction.CIL.Entities var child = 0; for (var offset = 0; offset < (ilbytes?.Length ?? 0);) { - var instruction = new Instruction(Cx, this, ilbytes!, offset, child++); + var instruction = new Instruction(Context, this, ilbytes!, offset, child++); yield return instruction; - if (nextSequencePoint != null && offset >= nextSequencePoint.Current.Offset) + if (nextSequencePoint is not null && offset >= nextSequencePoint.Current.Offset) { - instructionLocation = Cx.CreateSourceLocation(nextSequencePoint.Current.Location); + instructionLocation = Context.CreateSourceLocation(nextSequencePoint.Current.Location); yield return instructionLocation; if (!nextSequencePoint.MoveNext()) nextSequencePoint = null; } - if (instructionLocation != null) + if (instructionLocation is not null) yield return Tuples.cil_instruction_location(instruction, instructionLocation); jump_table.Add(instruction.Offset, instruction); @@ -257,7 +257,7 @@ namespace Semmle.Extraction.CIL.Entities { if (md.ImplAttributes == MethodImplAttributes.IL && md.RelativeVirtualAddress != 0) { - var body = Cx.PeReader.GetMethodBody(md.RelativeVirtualAddress); + var body = Context.PeReader.GetMethodBody(md.RelativeVirtualAddress); var ilbytes = body.GetILBytes(); @@ -267,7 +267,7 @@ namespace Semmle.Extraction.CIL.Entities Instruction decoded; try { - decoded = new Instruction(Cx, this, ilbytes!, offset, child++); + decoded = new Instruction(Context, this, ilbytes!, offset, child++); offset += decoded.Width; } catch // lgtm[cs/catch-of-all-exceptions] diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/ErrorType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/ErrorType.cs index e99fd8b4bc3..e08cea2854c 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/ErrorType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/ErrorType.cs @@ -16,7 +16,7 @@ namespace Semmle.Extraction.CIL.Entities public override string Name => "!error"; - public override Namespace ContainingNamespace => Cx.GlobalNamespace; + public override Namespace ContainingNamespace => Context.GlobalNamespace; public override Type? ContainingType => null; diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Event.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Event.cs index a6ca9364e0f..a54d0a8065d 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Event.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Event.cs @@ -24,7 +24,7 @@ namespace Semmle.Extraction.CIL.Entities { parent.WriteId(trapFile); trapFile.Write('.'); - trapFile.Write(Cx.ShortName(ed.Name)); + trapFile.Write(Context.ShortName(ed.Name)); trapFile.Write(";cil-event"); } @@ -39,34 +39,34 @@ namespace Semmle.Extraction.CIL.Entities { get { - var signature = (Type)Cx.CreateGeneric(parent, ed.Type); + var signature = (Type)Context.CreateGeneric(parent, ed.Type); yield return signature; - yield return Tuples.cil_event(this, parent, Cx.ShortName(ed.Name), signature); + yield return Tuples.cil_event(this, parent, Context.ShortName(ed.Name), signature); var accessors = ed.GetAccessors(); if (!accessors.Adder.IsNil) { - var adder = (Method)Cx.CreateGeneric(parent, accessors.Adder); + var adder = (Method)Context.CreateGeneric(parent, accessors.Adder); yield return adder; yield return Tuples.cil_adder(this, adder); } if (!accessors.Remover.IsNil) { - var remover = (Method)Cx.CreateGeneric(parent, accessors.Remover); + var remover = (Method)Context.CreateGeneric(parent, accessors.Remover); yield return remover; yield return Tuples.cil_remover(this, remover); } if (!accessors.Raiser.IsNil) { - var raiser = (Method)Cx.CreateGeneric(parent, accessors.Raiser); + var raiser = (Method)Context.CreateGeneric(parent, accessors.Raiser); yield return raiser; yield return Tuples.cil_raiser(this, raiser); } - foreach (var c in Attribute.Populate(Cx, this, ed.GetCustomAttributes())) + foreach (var c in Attribute.Populate(Context, this, ed.GetCustomAttributes())) yield return c; } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/ExceptionRegion.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/ExceptionRegion.cs index 3026ff03030..5e49c10f533 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/ExceptionRegion.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/ExceptionRegion.cs @@ -13,7 +13,7 @@ namespace Semmle.Extraction.CIL.Entities private readonly System.Reflection.Metadata.ExceptionRegion r; private readonly Dictionary jump_table; - public ExceptionRegion(IGenericContext gc, MethodImplementation method, int index, System.Reflection.Metadata.ExceptionRegion r, Dictionary jump_table) : base(gc.Cx) + public ExceptionRegion(IGenericContext gc, MethodImplementation method, int index, System.Reflection.Metadata.ExceptionRegion r, Dictionary jump_table) : base(gc.Context) { this.gc = gc; this.method = method; @@ -46,7 +46,7 @@ namespace Semmle.Extraction.CIL.Entities if (!r.CatchType.IsNil) { - var catchType = (Type)Cx.CreateGeneric(gc, r.CatchType); + var catchType = (Type)Context.CreateGeneric(gc, r.CatchType); yield return catchType; yield return Tuples.cil_handler_type(this, catchType); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs index 4b612ae86af..a07ffc61de8 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs @@ -3,7 +3,7 @@ using System.IO; namespace Semmle.Extraction.CIL.Entities { - public class File : LabelledEntity, IFileOrFolder + internal class File : LabelledEntity, IFileOrFolder { protected string OriginalPath { get; } protected PathTransformer.ITransformedPath TransformedPath { get; } @@ -11,7 +11,7 @@ namespace Semmle.Extraction.CIL.Entities public File(Context cx, string path) : base(cx) { this.OriginalPath = path; - TransformedPath = cx.Cx.Extractor.PathTransformer.Transform(OriginalPath); + TransformedPath = Context.Extractor.PathTransformer.Transform(OriginalPath); } public override void WriteId(TextWriter trapFile) @@ -33,7 +33,7 @@ namespace Semmle.Extraction.CIL.Entities { if (TransformedPath.ParentDirectory is PathTransformer.ITransformedPath dir) { - var parent = Cx.CreateFolder(dir); + var parent = Context.CreateFolder(dir); yield return parent; yield return Tuples.containerparent(parent, this); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs index 2294a525e4b..b49abce64c9 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs @@ -3,7 +3,7 @@ using System.IO; namespace Semmle.Extraction.CIL.Entities { - public sealed class Folder : LabelledEntity, IFileOrFolder + internal sealed class Folder : LabelledEntity, IFileOrFolder { private readonly PathTransformer.ITransformedPath transformedPath; @@ -24,7 +24,7 @@ namespace Semmle.Extraction.CIL.Entities { if (transformedPath.ParentDirectory is PathTransformer.ITransformedPath parent) { - var parentFolder = Cx.CreateFolder(parent); + var parentFolder = Context.CreateFolder(parent); yield return parentFolder; yield return Tuples.containerparent(parentFolder, this); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/FunctionPointerType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/FunctionPointerType.cs index e92dd957cb5..3b6bbba00cc 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/FunctionPointerType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/FunctionPointerType.cs @@ -30,7 +30,7 @@ namespace Semmle.Extraction.CIL.Entities } } - public override Namespace? ContainingNamespace => Cx.GlobalNamespace; + public override Namespace? ContainingNamespace => Context.GlobalNamespace; public override Type? ContainingType => null; @@ -104,7 +104,7 @@ namespace Semmle.Extraction.CIL.Entities yield return Tuples.cil_function_pointer_calling_conventions(this, signature.Header.CallingConvention); - foreach (var p in Method.GetParameterExtractionProducts(signature.ParameterTypes, this, this, Cx, 0)) + foreach (var p in Method.GetParameterExtractionProducts(signature.ParameterTypes, this, this, Context, 0)) { yield return p; } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/GenericsHelper.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/GenericsHelper.cs index 311d4e48134..dd3657c0e0d 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/GenericsHelper.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/GenericsHelper.cs @@ -45,7 +45,7 @@ namespace Semmle.Extraction.CIL.Entities public static IEnumerable GetAllTypeParameters(Type? container, IEnumerable thisTypeParameters) { - if (container != null) + if (container is not null) { foreach (var t in container.TypeParameters) yield return t; diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Instruction.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Instruction.cs index 033010e18a3..cf7e8fee0b5 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Instruction.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Instruction.cs @@ -356,7 +356,7 @@ namespace Semmle.Extraction.CIL.Entities switch (PayloadType) { case Payload.String: - yield return Tuples.cil_value(this, Cx.MdReader.GetUserString(MetadataTokens.UserStringHandle(payloadValue))); + yield return Tuples.cil_value(this, Context.MdReader.GetUserString(MetadataTokens.UserStringHandle(payloadValue))); break; case Payload.Float32: yield return Tuples.cil_value(this, BitConverter.ToSingle(data, offset).ToString()); @@ -385,29 +385,31 @@ namespace Semmle.Extraction.CIL.Entities case Payload.Type: case Payload.Field: case Payload.ValueType: - // A generic EntityHandle. - var handle = MetadataTokens.EntityHandle(payloadValue); - var target = Cx.CreateGeneric(Method, handle); - yield return target; - if (target != null) { - yield return Tuples.cil_access(this, target); + // A generic EntityHandle. + var handle = MetadataTokens.EntityHandle(payloadValue); + var target = Context.CreateGeneric(Method, handle); + yield return target; + if (target is not null) + { + yield return Tuples.cil_access(this, target); + } + else + { + throw new InternalError($"Unable to create payload type {PayloadType} for opcode {OpCode}"); + } + break; } - else - { - throw new InternalError($"Unable to create payload type {PayloadType} for opcode {OpCode}"); - } - break; case Payload.Arg8: case Payload.Arg16: - if (Method.Parameters is object) + if (Method.Parameters is not null) { yield return Tuples.cil_access(this, Method.Parameters[(int)unsignedPayloadValue]); } break; case Payload.Local8: case Payload.Local16: - if (Method.LocalVariables is object) + if (Method.LocalVariables is not null) { yield return Tuples.cil_access(this, Method.LocalVariables[(int)unsignedPayloadValue]); } @@ -417,10 +419,33 @@ namespace Semmle.Extraction.CIL.Entities case Payload.Target32: case Payload.Switch: case Payload.Ignore8: - case Payload.CallSiteDesc: // These are not handled here. // Some of these are handled by JumpContents(). break; + case Payload.CallSiteDesc: + { + var handle = MetadataTokens.EntityHandle(payloadValue); + IExtractedEntity? target = null; + try + { + target = Context.CreateGeneric(Method, handle); + } + catch (Exception exc) + { + Context.Extractor.Logger.Log(Util.Logging.Severity.Warning, $"Couldn't interpret payload of payload type {PayloadType} as a function pointer type. {exc.Message} {exc.StackTrace}"); + } + + if (target is not null) + { + yield return target; + yield return Tuples.cil_access(this, target); + } + else + { + throw new InternalError($"Unable to create payload type {PayloadType} for opcode {OpCode}"); + } + break; + } default: throw new InternalError($"Unhandled payload type {PayloadType}"); } @@ -476,7 +501,7 @@ namespace Semmle.Extraction.CIL.Entities // TODO: Find a solution to this. // For now, just log the error - Cx.Cx.ExtractionError("A CIL instruction jumps outside the current method", null, Extraction.Entities.GeneratedLocation.Create(Cx.Cx), "", Util.Logging.Severity.Warning); + Context.ExtractionError("A CIL instruction jumps outside the current method", null, Extraction.Entities.GeneratedLocation.Create(Context), "", Util.Logging.Severity.Warning); } } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/MemberReferenceField.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/MemberReferenceField.cs index 136ece10a8c..6a198052018 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/MemberReferenceField.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/MemberReferenceField.cs @@ -11,12 +11,12 @@ namespace Semmle.Extraction.CIL.Entities private readonly IGenericContext gc; private readonly Type declType; - public MemberReferenceField(IGenericContext gc, MemberReferenceHandle handle) : base(gc.Cx) + public MemberReferenceField(IGenericContext gc, MemberReferenceHandle handle) : base(gc.Context) { this.handle = handle; this.gc = gc; - mr = Cx.MdReader.GetMemberReference(handle); - declType = (Type)Cx.CreateGeneric(gc, mr.Parent); + mr = Context.MdReader.GetMemberReference(handle); + declType = (Type)Context.CreateGeneric(gc, mr.Parent); } public override bool Equals(object? obj) @@ -29,11 +29,11 @@ namespace Semmle.Extraction.CIL.Entities return handle.GetHashCode(); } - public override string Name => Cx.GetString(mr.Name); + public override string Name => Context.GetString(mr.Name); public override Type DeclaringType => declType; - public override Type Type => mr.DecodeFieldSignature(Cx.TypeSignatureDecoder, this); + public override Type Type => mr.DecodeFieldSignature(Context.TypeSignatureDecoder, this); public override IEnumerable TypeParameters => gc.TypeParameters.Concat(declType.TypeParameters); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/MemberReferenceMethod.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/MemberReferenceMethod.cs index 703bb717cee..daceabd23eb 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/MemberReferenceMethod.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/MemberReferenceMethod.cs @@ -19,11 +19,11 @@ namespace Semmle.Extraction.CIL.Entities { this.handle = handle; this.gc = gc; - mr = Cx.MdReader.GetMemberReference(handle); + mr = Context.MdReader.GetMemberReference(handle); signature = mr.DecodeMethodSignature(new SignatureDecoder(), gc); - parent = (IGenericContext)Cx.CreateGeneric(gc, mr.Parent); + parent = (IGenericContext)Context.CreateGeneric(gc, mr.Parent); var declType = parent is Method parentMethod ? parentMethod.DeclaringType @@ -33,7 +33,7 @@ namespace Semmle.Extraction.CIL.Entities throw new InternalError("Parent context of method is not a type"); declaringType = declType; - nameLabel = Cx.GetString(mr.Name); + nameLabel = Context.GetString(mr.Name); var typeSourceDeclaration = declaringType.SourceDeclaration; sourceDeclaration = typeSourceDeclaration == declaringType ? (Method)this : typeSourceDeclaration.LookupMethod(mr.Name, mr.Signature); @@ -59,7 +59,7 @@ namespace Semmle.Extraction.CIL.Entities public override Type DeclaringType => declaringType; - public override string Name => Cx.ShortName(mr.Name); + public override string Name => Context.ShortName(mr.Name); public override IEnumerable TypeParameters => parent.TypeParameters.Concat(gc.TypeParameters); @@ -69,12 +69,12 @@ namespace Semmle.Extraction.CIL.Entities { genericParams = new MethodTypeParameter[signature.GenericParameterCount]; for (var p = 0; p < genericParams.Length; ++p) - genericParams[p] = Cx.Populate(new MethodTypeParameter(this, this, p)); + genericParams[p] = Context.Populate(new MethodTypeParameter(this, this, p)); foreach (var p in genericParams) yield return p; - var typeSignature = mr.DecodeMethodSignature(Cx.TypeSignatureDecoder, this); + var typeSignature = mr.DecodeMethodSignature(Context.TypeSignatureDecoder, this); var parameters = GetParameterExtractionProducts(typeSignature.ParameterTypes).ToArray(); Parameters = parameters.OfType().ToArray(); @@ -87,7 +87,7 @@ namespace Semmle.Extraction.CIL.Entities yield return m; } - if (SourceDeclaration != null) + if (SourceDeclaration is not null) yield return Tuples.cil_method_source_declaration(this, SourceDeclaration); } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs index d1302837c16..9df82edaa68 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs @@ -15,7 +15,7 @@ namespace Semmle.Extraction.CIL.Entities protected IGenericContext gc; protected MethodSignature signature; - protected Method(IGenericContext gc) : base(gc.Cx) + protected Method(IGenericContext gc) : base(gc.Context) { this.gc = gc; } @@ -25,7 +25,7 @@ namespace Semmle.Extraction.CIL.Entities public override IEnumerable TypeParameters => gc.TypeParameters.Concat(DeclaringType.TypeParameters); public override IEnumerable MethodParameters => - genericParams == null ? gc.MethodParameters : gc.MethodParameters.Concat(genericParams); + genericParams is null ? gc.MethodParameters : gc.MethodParameters.Concat(genericParams); public int GenericParameterCount => signature.GenericParameterCount; @@ -83,10 +83,10 @@ namespace Semmle.Extraction.CIL.Entities if (!IsStatic) { - yield return Cx.Populate(new Parameter(Cx, this, i++, DeclaringType)); + yield return Context.Populate(new Parameter(Context, this, i++, DeclaringType)); } - foreach (var p in GetParameterExtractionProducts(parameterTypes, this, this, Cx, i)) + foreach (var p in GetParameterExtractionProducts(parameterTypes, this, this, Context, i)) { yield return p; } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodImplementation.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodImplementation.cs index bbc69ed9b3e..eb31e2813ee 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodImplementation.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodImplementation.cs @@ -10,7 +10,7 @@ namespace Semmle.Extraction.CIL.Entities { private readonly Method m; - public MethodImplementation(Method m) : base(m.Cx) + public MethodImplementation(Method m) : base(m.Context) { this.m = m; } @@ -19,7 +19,7 @@ namespace Semmle.Extraction.CIL.Entities { get { - yield return Tuples.cil_method_implementation(this, m, Cx.Assembly); + yield return Tuples.cil_method_implementation(this, m, Context.Assembly); } } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodSpecificationMethod.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodSpecificationMethod.cs index a3f87b14a8e..840d106b536 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodSpecificationMethod.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodSpecificationMethod.cs @@ -21,9 +21,9 @@ namespace Semmle.Extraction.CIL.Entities public MethodSpecificationMethod(IGenericContext gc, MethodSpecificationHandle handle) : base(gc) { this.handle = handle; - ms = Cx.MdReader.GetMethodSpecification(handle); - typeParams = ms.DecodeSignature(Cx.TypeSignatureDecoder, gc); - unboundMethod = (Method)Cx.CreateGeneric(gc, ms.Method); + ms = Context.MdReader.GetMethodSpecification(handle); + typeParams = ms.DecodeSignature(Context.TypeSignatureDecoder, gc); + unboundMethod = (Method)Context.CreateGeneric(gc, ms.Method); } public override void WriteId(TextWriter trapFile) @@ -66,12 +66,12 @@ namespace Semmle.Extraction.CIL.Entities switch (ms.Method.Kind) { case HandleKind.MemberReference: - var mr = Cx.MdReader.GetMemberReference((MemberReferenceHandle)ms.Method); - constructedTypeSignature = mr.DecodeMethodSignature(Cx.TypeSignatureDecoder, this); + var mr = Context.MdReader.GetMemberReference((MemberReferenceHandle)ms.Method); + constructedTypeSignature = mr.DecodeMethodSignature(Context.TypeSignatureDecoder, this); break; case HandleKind.MethodDefinition: - var md = Cx.MdReader.GetMethodDefinition((MethodDefinitionHandle)ms.Method); - constructedTypeSignature = md.DecodeSignature(Cx.TypeSignatureDecoder, this); + var md = Context.MdReader.GetMethodDefinition((MethodDefinitionHandle)ms.Method); + constructedTypeSignature = md.DecodeSignature(Context.TypeSignatureDecoder, this); break; default: throw new InternalError($"Unexpected constructed method handle kind {ms.Method.Kind}"); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/NamedTypeIdWriter.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/NamedTypeIdWriter.cs index 98f9e94ec82..59c7d172d01 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/NamedTypeIdWriter.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/NamedTypeIdWriter.cs @@ -21,7 +21,7 @@ namespace Semmle.Extraction.CIL.Entities } var ct = type.ContainingType; - if (ct != null) + if (ct is not null) { ct.WriteId(trapFile, inContext); trapFile.Write('.'); @@ -41,7 +41,7 @@ namespace Semmle.Extraction.CIL.Entities trapFile.Write(type.Name); var thisTypeArguments = type.ThisTypeArguments; - if (thisTypeArguments != null && thisTypeArguments.Any()) + if (thisTypeArguments is not null && thisTypeArguments.Any()) { trapFile.Write('<'); var index = 0; diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Namespace.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Namespace.cs index 5d498c5ac54..1d0b373952b 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Namespace.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Namespace.cs @@ -7,7 +7,7 @@ namespace Semmle.Extraction.CIL.Entities /// /// A namespace. /// - public sealed class Namespace : TypeContainer + internal sealed class Namespace : TypeContainer { public Namespace? ParentNamespace { get; } public string Name { get; } @@ -16,7 +16,7 @@ namespace Semmle.Extraction.CIL.Entities public override void WriteId(TextWriter trapFile) { - if (ParentNamespace != null && !ParentNamespace.IsGlobalNamespace) + if (ParentNamespace is not null && !ParentNamespace.IsGlobalNamespace) { ParentNamespace.WriteId(trapFile); trapFile.Write('.'); @@ -77,7 +77,7 @@ namespace Semmle.Extraction.CIL.Entities get { yield return Tuples.namespaces(this, Name); - if (ParentNamespace is object) + if (ParentNamespace is not null) yield return Tuples.parent_namespace(this, ParentNamespace); } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/NoMetadataHandleType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/NoMetadataHandleType.cs index c1259534f79..c2a340c4de1 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/NoMetadataHandleType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/NoMetadataHandleType.cs @@ -36,13 +36,13 @@ namespace Semmle.Extraction.CIL.Entities isContainerNamespace = nameParser.IsContainerNamespace; containerName = nameParser.ContainerName; - unboundGenericType = nameParser.UnboundGenericTypeName == null + unboundGenericType = nameParser.UnboundGenericTypeName is null ? this - : new NoMetadataHandleType(Cx, nameParser.UnboundGenericTypeName); + : new NoMetadataHandleType(Context, nameParser.UnboundGenericTypeName); - if (nameParser.TypeArguments != null) + if (nameParser.TypeArguments is not null) { - thisTypeArguments = nameParser.TypeArguments.Select(t => new NoMetadataHandleType(Cx, t)).ToArray(); + thisTypeArguments = nameParser.TypeArguments.Select(t => new NoMetadataHandleType(Context, t)).ToArray(); } else { @@ -51,14 +51,14 @@ namespace Semmle.Extraction.CIL.Entities containingType = isContainerNamespace ? null - : new NoMetadataHandleType(Cx, containerName); + : new NoMetadataHandleType(Context, containerName); containingNamespace = isContainerNamespace - ? containerName == Cx.GlobalNamespace.Name - ? Cx.GlobalNamespace - : containerName == Cx.SystemNamespace.Name - ? Cx.SystemNamespace - : new Namespace(Cx, containerName) + ? containerName == Context.GlobalNamespace.Name + ? Context.GlobalNamespace + : containerName == Context.SystemNamespace.Name + ? Context.SystemNamespace + : new Namespace(Context, containerName) : null; Populate(); @@ -66,12 +66,12 @@ namespace Semmle.Extraction.CIL.Entities private void Populate() { - if (ContainingNamespace is object) + if (ContainingNamespace is not null) { - Cx.Populate(ContainingNamespace); + Context.Populate(ContainingNamespace); } - Cx.Populate(this); + Context.Populate(this); } public override bool Equals(object? obj) @@ -118,7 +118,7 @@ namespace Semmle.Extraction.CIL.Entities if (TotalTypeParametersCount != typeArguments.Count()) throw new InternalError("Mismatched type arguments"); - return Cx.Populate(new ConstructedType(Cx, this, typeArguments)); + return Context.Populate(new ConstructedType(Context, this, typeArguments)); } public override void WriteAssemblyPrefix(TextWriter trapFile) @@ -133,7 +133,7 @@ namespace Semmle.Extraction.CIL.Entities } else { - Cx.WriteAssemblyPrefix(trapFile); + Context.WriteAssemblyPrefix(trapFile); } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/PdbSourceFile.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/PdbSourceFile.cs index 07cfdab6543..45623b3ceeb 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/PdbSourceFile.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/PdbSourceFile.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; namespace Semmle.Extraction.CIL.Entities { - public class PdbSourceFile : File + internal class PdbSourceFile : File { private readonly PDB.ISourceFile file; @@ -20,10 +20,10 @@ namespace Semmle.Extraction.CIL.Entities var text = file.Contents; - if (text == null) - Cx.Cx.Extractor.Logger.Log(Util.Logging.Severity.Warning, string.Format("PDB source file {0} could not be found", OriginalPath)); + if (text is null) + Context.Extractor.Logger.Log(Util.Logging.Severity.Warning, string.Format("PDB source file {0} could not be found", OriginalPath)); else - Cx.Cx.TrapWriter.Archive(TransformedPath, text); + Context.TrapWriter.Archive(TransformedPath, text); yield return Tuples.file_extraction_mode(this, 2); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/PrimitiveType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/PrimitiveType.cs index 661ae0f535b..1e331397237 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/PrimitiveType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/PrimitiveType.cs @@ -5,7 +5,7 @@ using System.IO; namespace Semmle.Extraction.CIL.Entities { - public sealed class PrimitiveType : Type + internal sealed class PrimitiveType : Type { private readonly PrimitiveTypeCode typeCode; public PrimitiveType(Context cx, PrimitiveTypeCode tc) : base(cx) @@ -27,7 +27,7 @@ namespace Semmle.Extraction.CIL.Entities public override string Name => typeCode.Id(); - public override Namespace ContainingNamespace => Cx.SystemNamespace; + public override Namespace ContainingNamespace => Context.SystemNamespace; public override Type? ContainingType => null; diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs index e605468827a..9e56c22a099 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs @@ -15,11 +15,11 @@ namespace Semmle.Extraction.CIL.Entities private readonly PropertyDefinition pd; private readonly IGenericContext gc; - public Property(IGenericContext gc, Type type, PropertyDefinitionHandle handle) : base(gc.Cx) + public Property(IGenericContext gc, Type type, PropertyDefinitionHandle handle) : base(gc.Context) { this.gc = gc; this.handle = handle; - pd = Cx.MdReader.GetPropertyDefinition(handle); + pd = Context.MdReader.GetPropertyDefinition(handle); this.type = type; } @@ -27,7 +27,7 @@ namespace Semmle.Extraction.CIL.Entities { trapFile.WriteSubId(type); trapFile.Write('.'); - trapFile.Write(Cx.GetString(pd.Name)); + trapFile.Write(Context.GetString(pd.Name)); trapFile.Write("("); var index = 0; var signature = pd.DecodeSignature(new SignatureDecoder(), gc); @@ -51,10 +51,10 @@ namespace Semmle.Extraction.CIL.Entities { get { - yield return Tuples.metadata_handle(this, Cx.Assembly, MetadataTokens.GetToken(handle)); - var sig = pd.DecodeSignature(Cx.TypeSignatureDecoder, type); + yield return Tuples.metadata_handle(this, Context.Assembly, MetadataTokens.GetToken(handle)); + var sig = pd.DecodeSignature(Context.TypeSignatureDecoder, type); - var name = Cx.ShortName(pd.Name); + var name = Context.ShortName(pd.Name); var t = sig.ReturnType; if (t is ModifiedType mt) @@ -72,19 +72,19 @@ namespace Semmle.Extraction.CIL.Entities var accessors = pd.GetAccessors(); if (!accessors.Getter.IsNil) { - var getter = (Method)Cx.CreateGeneric(type, accessors.Getter); + var getter = (Method)Context.CreateGeneric(type, accessors.Getter); yield return getter; yield return Tuples.cil_getter(this, getter); } if (!accessors.Setter.IsNil) { - var setter = (Method)Cx.CreateGeneric(type, accessors.Setter); + var setter = (Method)Context.CreateGeneric(type, accessors.Setter); yield return setter; yield return Tuples.cil_setter(this, setter); } - foreach (var c in Attribute.Populate(Cx, this, pd.GetCustomAttributes())) + foreach (var c in Attribute.Populate(Context, this, pd.GetCustomAttributes())) yield return c; } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/SignatureDecoder.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/SignatureDecoder.cs index 308846ff30a..ea646449421 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/SignatureDecoder.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/SignatureDecoder.cs @@ -250,7 +250,7 @@ namespace Semmle.Extraction.CIL.Entities public void WriteId(TextWriter trapFile, IGenericContext gc) { - var type = (Type)gc.Cx.Create(handle); + var type = (Type)gc.Context.Create(handle); type.WriteId(trapFile); } } @@ -271,7 +271,7 @@ namespace Semmle.Extraction.CIL.Entities public void WriteId(TextWriter trapFile, IGenericContext gc) { - var type = (Type)gc.Cx.Create(handle); + var type = (Type)gc.Context.Create(handle); type.WriteId(trapFile); } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/SourceLocation.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/SourceLocation.cs index 318cac14930..ff67844121a 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/SourceLocation.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/SourceLocation.cs @@ -4,7 +4,7 @@ using Semmle.Extraction.PDB; namespace Semmle.Extraction.CIL.Entities { - public sealed class PdbSourceLocation : LabelledEntity, ILocation + internal sealed class PdbSourceLocation : LabelledEntity, ILocation { private readonly Location location; private readonly PdbSourceFile file; diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs index ad5a6ababaa..db4876f8a9a 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs @@ -9,7 +9,7 @@ namespace Semmle.Extraction.CIL.Entities /// /// A type. /// - public abstract class Type : TypeContainer, IMember + internal abstract class Type : TypeContainer, IMember { internal const string AssemblyTypeNameSeparator = "::"; internal const string PrimitiveTypePrefix = "builtin" + AssemblyTypeNameSeparator + "System."; @@ -124,7 +124,7 @@ namespace Semmle.Extraction.CIL.Entities { get { - if (ContainingType != null) + if (ContainingType is not null) { foreach (var t in ContainingType.GenericArguments) yield return t; @@ -174,7 +174,7 @@ namespace Semmle.Extraction.CIL.Entities { if (TryGetPrimitiveTypeCode(out var code)) { - t = Cx.Create(code); + t = Context.Create(code); return true; } @@ -184,8 +184,8 @@ namespace Semmle.Extraction.CIL.Entities private bool TryGetPrimitiveTypeCode(out PrimitiveTypeCode code) { - if (ContainingType == null && - ContainingNamespace?.Name == Cx.SystemNamespace.Name && + if (ContainingType is null && + ContainingNamespace?.Name == Context.SystemNamespace.Name && primitiveTypeCodeMapping.TryGetValue(Name, out code)) { return true; @@ -200,6 +200,6 @@ namespace Semmle.Extraction.CIL.Entities public sealed override IEnumerable MethodParameters => Enumerable.Empty(); public static Type DecodeType(IGenericContext gc, TypeSpecificationHandle handle) => - gc.Cx.MdReader.GetTypeSpecification(handle).DecodeSignature(gc.Cx.TypeSignatureDecoder, gc); + gc.Context.MdReader.GetTypeSpecification(handle).DecodeSignature(gc.Context.TypeSignatureDecoder, gc); } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeContainer.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeContainer.cs index 106b6fe83be..e201e065255 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeContainer.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeContainer.cs @@ -6,7 +6,7 @@ namespace Semmle.Extraction.CIL.Entities /// /// Base class for all type containers (namespaces, types, methods). /// - public abstract class TypeContainer : LabelledEntity, IGenericContext + internal abstract class TypeContainer : LabelledEntity, IGenericContext { protected TypeContainer(Context cx) : base(cx) { diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeDefinitionType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeDefinitionType.cs index 461141ae719..51b02f1482e 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeDefinitionType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeDefinitionType.cs @@ -11,7 +11,7 @@ namespace Semmle.Extraction.CIL.Entities /// /// A type defined in the current assembly. /// - public sealed class TypeDefinitionType : Type + internal sealed class TypeDefinitionType : Type { private readonly TypeDefinitionHandle handle; private readonly TypeDefinition td; @@ -45,9 +45,9 @@ namespace Semmle.Extraction.CIL.Entities idWriter.WriteId(trapFile, inContext); } - public override string Name => GenericsHelper.GetNonGenericName(td.Name, Cx.MdReader); + public override string Name => GenericsHelper.GetNonGenericName(td.Name, Context.MdReader); - public override Namespace ContainingNamespace => Cx.Create(td.NamespaceDefinition); + public override Namespace ContainingNamespace => Context.Create(td.NamespaceDefinition); public override Type? ContainingType => declType; @@ -58,14 +58,14 @@ namespace Semmle.Extraction.CIL.Entities if (TotalTypeParametersCount != typeArguments.Count()) throw new InternalError("Mismatched type arguments"); - return Cx.Populate(new ConstructedType(Cx, this, typeArguments)); + return Context.Populate(new ConstructedType(Context, this, typeArguments)); } public override void WriteAssemblyPrefix(TextWriter trapFile) { var ct = ContainingType; if (ct is null) - Cx.WriteAssemblyPrefix(trapFile); + Context.WriteAssemblyPrefix(trapFile); else if (IsPrimitiveType) trapFile.Write(Type.PrimitiveTypePrefix); else @@ -83,7 +83,7 @@ namespace Semmle.Extraction.CIL.Entities // Two-phase population because type parameters can be mutually dependent for (var i = 0; i < newTypeParams.Length; ++i) - newTypeParams[i] = Cx.Populate(new TypeTypeParameter(this, i)); + newTypeParams[i] = Context.Populate(new TypeTypeParameter(this, i)); for (var i = 0; i < newTypeParams.Length; ++i) newTypeParams[i].PopulateHandle(genericParams[i + toSkip]); return newTypeParams; @@ -96,7 +96,7 @@ namespace Semmle.Extraction.CIL.Entities var containingType = td.GetDeclaringType(); var parentTypeParameters = containingType.IsNil ? 0 - : Cx.MdReader.GetTypeDefinition(containingType).GetGenericParameters().Count; + : Context.MdReader.GetTypeDefinition(containingType).GetGenericParameters().Count; return td.GetGenericParameters().Count - parentTypeParameters; } @@ -110,7 +110,7 @@ namespace Semmle.Extraction.CIL.Entities { get { - yield return Tuples.metadata_handle(this, Cx.Assembly, MetadataTokens.GetToken(handle)); + yield return Tuples.metadata_handle(this, Context.Assembly, MetadataTokens.GetToken(handle)); foreach (var c in base.Contents) yield return c; @@ -119,7 +119,7 @@ namespace Semmle.Extraction.CIL.Entities foreach (var f in td.GetFields()) { // Populate field if needed - yield return Cx.CreateGeneric(this, f); + yield return Context.CreateGeneric(this, f); } foreach (var prop in td.GetProperties()) @@ -129,16 +129,16 @@ namespace Semmle.Extraction.CIL.Entities foreach (var @event in td.GetEvents()) { - yield return new Event(Cx, this, @event); + yield return new Event(Context, this, @event); } - foreach (var a in Attribute.Populate(Cx, this, td.GetCustomAttributes())) + foreach (var a in Attribute.Populate(Context, this, td.GetCustomAttributes())) yield return a; - foreach (var impl in td.GetMethodImplementations().Select(i => Cx.MdReader.GetMethodImplementation(i))) + foreach (var impl in td.GetMethodImplementations().Select(i => Context.MdReader.GetMethodImplementation(i))) { - var m = (Method)Cx.CreateGeneric(this, impl.MethodBody); - var decl = (Method)Cx.CreateGeneric(this, impl.MethodDeclaration); + var m = (Method)Context.CreateGeneric(this, impl.MethodBody); + var decl = (Method)Context.CreateGeneric(this, impl.MethodDeclaration); yield return m; yield return decl; @@ -166,7 +166,7 @@ namespace Semmle.Extraction.CIL.Entities if (!td.BaseType.IsNil) { - var @base = (Type)Cx.CreateGeneric(this, td.BaseType); + var @base = (Type)Context.CreateGeneric(this, td.BaseType); yield return @base; yield return Tuples.cil_base_class(this, @base); @@ -174,21 +174,21 @@ namespace Semmle.Extraction.CIL.Entities GetUnderlyingEnumType() is var underlying && underlying.HasValue) { - var underlyingType = Cx.Create(underlying.Value); + var underlyingType = Context.Create(underlying.Value); yield return underlyingType; yield return Tuples.cil_enum_underlying_type(this, underlyingType); } } - foreach (var @interface in td.GetInterfaceImplementations().Select(i => Cx.MdReader.GetInterfaceImplementation(i))) + foreach (var @interface in td.GetInterfaceImplementations().Select(i => Context.MdReader.GetInterfaceImplementation(i))) { - var t = (Type)Cx.CreateGeneric(this, @interface.Interface); + var t = (Type)Context.CreateGeneric(this, @interface.Interface); yield return t; yield return Tuples.cil_base_interface(this, t); } // Only type definitions have locations. - yield return Tuples.cil_type_location(this, Cx.Assembly); + yield return Tuples.cil_type_location(this, Context.Assembly); } } @@ -204,36 +204,36 @@ namespace Semmle.Extraction.CIL.Entities private bool IsSystemEnum(TypeReferenceHandle baseType) { - var baseTypeReference = Cx.MdReader.GetTypeReference(baseType); + var baseTypeReference = Context.MdReader.GetTypeReference(baseType); return IsSystemEnum(baseTypeReference.Name, baseTypeReference.Namespace); } private bool IsSystemEnum(TypeDefinitionHandle baseType) { - var baseTypeDefinition = Cx.MdReader.GetTypeDefinition(baseType); + var baseTypeDefinition = Context.MdReader.GetTypeDefinition(baseType); return IsSystemEnum(baseTypeDefinition.Name, baseTypeDefinition.Namespace); } private bool IsSystemEnum(StringHandle typeName, StringHandle namespaceName) { - return Cx.MdReader.StringComparer.Equals(typeName, "Enum") && + return Context.MdReader.StringComparer.Equals(typeName, "Enum") && !namespaceName.IsNil && - Cx.MdReader.StringComparer.Equals(namespaceName, "System"); + Context.MdReader.StringComparer.Equals(namespaceName, "System"); } internal PrimitiveTypeCode? GetUnderlyingEnumType() { foreach (var handle in td.GetFields()) { - var field = Cx.MdReader.GetFieldDefinition(handle); + var field = Context.MdReader.GetFieldDefinition(handle); if (field.Attributes.HasFlag(FieldAttributes.Static)) { continue; } - var blob = Cx.MdReader.GetBlobReader(field.Signature); + var blob = Context.MdReader.GetBlobReader(field.Signature); if (blob.ReadSignatureHeader().Kind != SignatureKind.Field) { break; @@ -249,11 +249,11 @@ namespace Semmle.Extraction.CIL.Entities { foreach (var h in td.GetMethods()) { - var md = Cx.MdReader.GetMethodDefinition(h); + var md = Context.MdReader.GetMethodDefinition(h); if (md.Name == name && md.Signature == signature) { - return (Method)Cx.Create(h); + return (Method)Context.Create(h); } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeParameter.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeParameter.cs index fd18374ed2d..95b8f026cf8 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeParameter.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeParameter.cs @@ -11,7 +11,7 @@ namespace Semmle.Extraction.CIL.Entities { protected readonly IGenericContext gc; - protected TypeParameter(IGenericContext gc) : base(gc.Cx) + protected TypeParameter(IGenericContext gc) : base(gc.Context) { this.gc = gc; } @@ -32,7 +32,7 @@ namespace Semmle.Extraction.CIL.Entities { if (!parameterHandle.IsNil) { - var tp = Cx.MdReader.GetGenericParameter(parameterHandle); + var tp = Context.MdReader.GetGenericParameter(parameterHandle); if (tp.Attributes.HasFlag(GenericParameterAttributes.Contravariant)) yield return Tuples.cil_typeparam_contravariant(this); @@ -45,9 +45,9 @@ namespace Semmle.Extraction.CIL.Entities if (tp.Attributes.HasFlag(GenericParameterAttributes.NotNullableValueTypeConstraint)) yield return Tuples.cil_typeparam_struct(this); - foreach (var constraint in tp.GetConstraints().Select(h => Cx.MdReader.GetGenericParameterConstraint(h))) + foreach (var constraint in tp.GetConstraints().Select(h => Context.MdReader.GetGenericParameterConstraint(h))) { - var t = (Type)Cx.CreateGeneric(this.gc, constraint.Type); + var t = (Type)Context.CreateGeneric(this.gc, constraint.Type); yield return t; yield return Tuples.cil_typeparam_constraint(this, t); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeReferenceType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeReferenceType.cs index 49a15965dd0..c94a6978f5e 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeReferenceType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeReferenceType.cs @@ -9,7 +9,7 @@ namespace Semmle.Extraction.CIL.Entities /// /// A type reference, to a type in a referenced assembly. /// - public sealed class TypeReferenceType : Type + internal sealed class TypeReferenceType : Type { private readonly TypeReferenceHandle handle; private readonly TypeReference tr; @@ -46,16 +46,16 @@ namespace Semmle.Extraction.CIL.Entities } } - public override string Name => GenericsHelper.GetNonGenericName(tr.Name, Cx.MdReader); + public override string Name => GenericsHelper.GetNonGenericName(tr.Name, Context.MdReader); - public override Namespace ContainingNamespace => Cx.CreateNamespace(tr.Namespace); + public override Namespace ContainingNamespace => Context.CreateNamespace(tr.Namespace); public override Type? ContainingType { get { return tr.ResolutionScope.Kind == HandleKind.TypeReference - ? (Type)Cx.Create((TypeReferenceHandle)tr.ResolutionScope) + ? (Type)Context.Create((TypeReferenceHandle)tr.ResolutionScope) : null; } } @@ -70,19 +70,19 @@ namespace Semmle.Extraction.CIL.Entities ContainingType!.WriteAssemblyPrefix(trapFile); break; case HandleKind.AssemblyReference: - var assemblyDef = Cx.MdReader.GetAssemblyReference((AssemblyReferenceHandle)tr.ResolutionScope); - trapFile.Write(Cx.GetString(assemblyDef.Name)); + var assemblyDef = Context.MdReader.GetAssemblyReference((AssemblyReferenceHandle)tr.ResolutionScope); + trapFile.Write(Context.GetString(assemblyDef.Name)); trapFile.Write('_'); trapFile.Write(assemblyDef.Version.ToString()); trapFile.Write(Entities.Type.AssemblyTypeNameSeparator); break; default: - Cx.WriteAssemblyPrefix(trapFile); + Context.WriteAssemblyPrefix(trapFile); break; } } - public override int ThisTypeParameterCount => GenericsHelper.GetGenericTypeParameterCount(tr.Name, Cx.MdReader); + public override int ThisTypeParameterCount => GenericsHelper.GetGenericTypeParameterCount(tr.Name, Context.MdReader); public override IEnumerable TypeParameters => GenericsHelper.GetAllTypeParameters(ContainingType, typeParams!.Value); @@ -98,7 +98,7 @@ namespace Semmle.Extraction.CIL.Entities if (TotalTypeParametersCount != typeArguments.Count()) throw new InternalError("Mismatched type arguments"); - return Cx.Populate(new ConstructedType(Cx, this, typeArguments)); + return Context.Populate(new ConstructedType(Context, this, typeArguments)); } } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeSignatureDecoder.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeSignatureDecoder.cs index 4417becc58d..d1022d58f0c 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeSignatureDecoder.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeSignatureDecoder.cs @@ -8,7 +8,7 @@ namespace Semmle.Extraction.CIL.Entities /// /// Decodes a type signature and produces a Type, for use by DecodeSignature() and friends. /// - public class TypeSignatureDecoder : ISignatureTypeProvider + internal class TypeSignatureDecoder : ISignatureTypeProvider { private readonly Context cx; diff --git a/csharp/extractor/Semmle.Extraction.CIL/PDB/MetadataPdbReader.cs b/csharp/extractor/Semmle.Extraction.CIL/PDB/MetadataPdbReader.cs index 81c91946657..fa7a1d0e8ca 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/PDB/MetadataPdbReader.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/PDB/MetadataPdbReader.cs @@ -49,7 +49,7 @@ namespace Semmle.Extraction.PDB .Where(p => !p.Document.IsNil && !p.IsHidden) .Select(p => new SequencePoint(p.Offset, new Location( new SourceFile(reader, p.Document), p.StartLine, p.StartColumn, p.EndLine, p.EndColumn))) - .Where(p => p.Location.File.Path != null) + .Where(p => p.Location.File.Path is not null) .ToArray(); return sequencePoints.Any() ? new Method(sequencePoints) : null; @@ -63,7 +63,7 @@ namespace Semmle.Extraction.PDB .Select(dirEntry => peReader.ReadEmbeddedPortablePdbDebugDirectoryData(dirEntry)) .FirstOrDefault(); - if (provider is object) + if (provider is not null) { return new MetadataPdbReader(provider); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/PDB/NativePdbReader.cs b/csharp/extractor/Semmle.Extraction.CIL/PDB/NativePdbReader.cs index 0a8479fb454..412e07844c1 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/PDB/NativePdbReader.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/PDB/NativePdbReader.cs @@ -59,7 +59,7 @@ namespace Semmle.Extraction.PDB { var methodToken = MetadataTokens.GetToken(h.ToDefinitionHandle()); var method = reader.GetMethod(methodToken); - if (method != null) + if (method is not null) { if (method.GetSequencePointCount(out var count) != 0 || count == 0) return null; @@ -100,7 +100,7 @@ namespace Semmle.Extraction.PDB .Select(cv => cv.Path) .FirstOrDefault(File.Exists); - if (path is object) + if (path is not null) { return new NativePdbReader(path); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Semmle.Extraction.CIL.csproj b/csharp/extractor/Semmle.Extraction.CIL/Semmle.Extraction.CIL.csproj index eb90b909b81..7cae08acc48 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Semmle.Extraction.CIL.csproj +++ b/csharp/extractor/Semmle.Extraction.CIL/Semmle.Extraction.CIL.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net5.0 Semmle.Extraction.CIL Semmle.Extraction.CIL false diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Driver/Semmle.Extraction.CSharp.Driver.csproj b/csharp/extractor/Semmle.Extraction.CSharp.Driver/Semmle.Extraction.CSharp.Driver.csproj index 3ec25c498fe..7ae23fdcd2e 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Driver/Semmle.Extraction.CSharp.Driver.csproj +++ b/csharp/extractor/Semmle.Extraction.CSharp.Driver/Semmle.Extraction.CSharp.Driver.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net5.0 Semmle.Extraction.CSharp.Driver Semmle.Extraction.CSharp.Driver false diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyCache.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyCache.cs index 597ff6f2165..7a0d0a48c67 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyCache.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyCache.cs @@ -111,7 +111,7 @@ namespace Semmle.BuildAnalyser { var loadedAssembly = System.Reflection.Assembly.ReflectionOnlyLoad(id); - if (loadedAssembly != null) + if (loadedAssembly is not null) { // The assembly was somewhere we haven't indexed before. // Add this assembly to our index so that subsequent lookups are faster. diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyInfo.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyInfo.cs index 15d29ed7b10..a234d5ca7b3 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyInfo.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyInfo.cs @@ -46,11 +46,11 @@ namespace Semmle.BuildAnalyser get { var result = Name; - if (Version != null) + if (Version is not null) result = string.Format("{0}, Version={1}", result, Version); - if (Culture != null) + if (Culture is not null) result = string.Format("{0}, Culture={1}", result, Culture); - if (PublicKeyToken != null) + if (PublicKeyToken is not null) result = string.Format("{0}, PublicKeyToken={1}", result, PublicKeyToken); return result; } @@ -66,9 +66,9 @@ namespace Semmle.BuildAnalyser get { yield return Id; - if (Version != null) + if (Version is not null) { - if (Culture != null) + if (Culture is not null) yield return string.Format("{0}, Version={1}, Culture={2}", Name, Version, Culture); yield return string.Format("{0}, Version={1}", Name, Version); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/BuildAnalysis.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/BuildAnalysis.cs index bb779a31373..f7752f6d60d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/BuildAnalysis.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/BuildAnalysis.cs @@ -104,7 +104,7 @@ namespace Semmle.BuildAnalyser using (new FileRenamer(sourceDir.GetFiles("global.json", SearchOption.AllDirectories))) using (new FileRenamer(sourceDir.GetFiles("Directory.Build.props", SearchOption.AllDirectories))) { - var solutions = options.SolutionFile != null ? + var solutions = options.SolutionFile is not null ? new[] { options.SolutionFile } : sourceDir.GetFiles("*.sln", SearchOption.AllDirectories).Select(d => d.FullName); diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/CsProjFile.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/CsProjFile.cs index dfbe8df2f06..dc775835e70 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/CsProjFile.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/CsProjFile.cs @@ -88,6 +88,11 @@ namespace Semmle.BuildAnalyser var projDir = fileName.Directory; var root = projFile.DocumentElement; + if (root is null) + { + throw new NotSupportedException("Project file without root is not supported."); + } + // Figure out if it's dotnet core var netCoreProjectFile = root.GetAttribute("Sdk") == "Microsoft.NET.Sdk"; @@ -96,34 +101,52 @@ namespace Semmle.BuildAnalyser { var explicitCsFiles = root .SelectNodes("/Project/ItemGroup/Compile/@Include", mgr) - .NodeList() + ?.NodeList() .Select(node => node.Value) - .Select(cs => Path.DirectorySeparatorChar == '/' ? cs.Replace("\\", "/") : cs) - .Select(f => Path.GetFullPath(Path.Combine(projDir.FullName, f))); + .Select(cs => GetFullPath(cs, projDir)) + .Where(s => s is not null) + ?? Enumerable.Empty(); var additionalCsFiles = System.IO.Directory.GetFiles(directoryName, "*.cs", SearchOption.AllDirectories); +#nullable disable warnings return (explicitCsFiles.Concat(additionalCsFiles).ToArray(), Array.Empty()); +#nullable restore warnings } var references = root .SelectNodes("/msbuild:Project/msbuild:ItemGroup/msbuild:Reference/@Include", mgr) - .NodeList() + ?.NodeList() .Select(node => node.Value) - .ToArray(); + .Where(s => s is not null) + .ToArray() + ?? Array.Empty(); var relativeCsIncludes = root .SelectNodes("/msbuild:Project/msbuild:ItemGroup/msbuild:Compile/@Include", mgr) - .NodeList() + ?.NodeList() .Select(node => node.Value) - .ToArray(); + .ToArray() + ?? Array.Empty(); var csFiles = relativeCsIncludes - .Select(cs => Path.DirectorySeparatorChar == '/' ? cs.Replace("\\", "/") : cs) - .Select(f => Path.GetFullPath(Path.Combine(projDir.FullName, f))) + .Select(cs => GetFullPath(cs, projDir)) + .Where(s => s is not null) .ToArray(); +#nullable disable warnings return (csFiles, references); +#nullable restore warnings + } + + private static string? GetFullPath(string? file, DirectoryInfo? projDir) + { + if (file is null) + { + return null; + } + + return Path.GetFullPath(Path.Combine(projDir?.FullName ?? string.Empty, Path.DirectorySeparatorChar == '/' ? file.Replace("\\", "/") : file)); } private readonly string[] references; diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/NugetPackages.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/NugetPackages.cs index e29b1b4ac1e..94e65d61462 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/NugetPackages.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/NugetPackages.cs @@ -113,6 +113,12 @@ namespace Semmle.BuildAnalyser { using var p = Process.Start(pi); + if (p is null) + { + pm.FailedNugetCommand(pi.FileName, pi.Arguments, "Couldn't start process."); + return; + } + var output = p.StandardOutput.ReadToEnd(); var error = p.StandardError.ReadToEnd(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Program.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Program.cs index dc67a7679e8..197001a22a6 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Program.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Program.cs @@ -30,7 +30,7 @@ namespace Semmle.Extraction.CSharp.Standalone buildAnalysis = new BuildAnalysis(options, progressMonitor); References = buildAnalysis.ReferenceFiles; Extraction = new Extraction(options.SrcDir); - Extraction.Sources.AddRange(options.SolutionFile == null ? buildAnalysis.AllSourceFiles : buildAnalysis.ProjectSourceFiles); + Extraction.Sources.AddRange(options.SolutionFile is null ? buildAnalysis.AllSourceFiles : buildAnalysis.ProjectSourceFiles); } public IEnumerable References { get; } diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Runtime.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Runtime.cs index a922a3bf07a..1fbe016fc76 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Runtime.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Runtime.cs @@ -22,13 +22,13 @@ namespace Semmle.Extraction.CSharp.Standalone get { var dotnetPath = FileUtils.FindProgramOnPath(Win32.IsWindows() ? "dotnet.exe" : "dotnet"); - var dotnetDirs = dotnetPath != null + var dotnetDirs = dotnetPath is not null ? new[] { dotnetPath } : new[] { "/usr/share/dotnet", @"C:\Program Files\dotnet" }; var coreDirs = dotnetDirs.Select(d => Path.Combine(d, "shared", "Microsoft.NETCore.App")); var dir = coreDirs.FirstOrDefault(Directory.Exists); - if (dir is object) + if (dir is not null) { return Directory.EnumerateDirectories(dir).OrderByDescending(Path.GetFileName); } @@ -46,7 +46,7 @@ namespace Semmle.Extraction.CSharp.Standalone get { var monoPath = FileUtils.FindProgramOnPath(Win32.IsWindows() ? "mono.exe" : "mono"); - var monoDirs = monoPath != null + var monoDirs = monoPath is not null ? new[] { monoPath } : new[] { "/usr/lib/mono", @"C:\Program Files\Mono\lib\mono" }; @@ -58,7 +58,7 @@ namespace Semmle.Extraction.CSharp.Standalone var dir = monoDirs.FirstOrDefault(Directory.Exists); - if (dir is object) + if (dir is not null) { return Directory.EnumerateDirectories(dir) .Where(d => Char.IsDigit(Path.GetFileName(d)[0])) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Semmle.Extraction.CSharp.Standalone.csproj b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Semmle.Extraction.CSharp.Standalone.csproj index 277310cc341..aed85473e7c 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Semmle.Extraction.CSharp.Standalone.csproj +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Semmle.Extraction.CSharp.Standalone.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net5.0 Semmle.Extraction.CSharp.Standalone Semmle.Extraction.CSharp.Standalone false @@ -23,7 +23,7 @@ - + diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Analyser.cs b/csharp/extractor/Semmle.Extraction.CSharp/Analyser.cs deleted file mode 100644 index b04b429993d..00000000000 --- a/csharp/extractor/Semmle.Extraction.CSharp/Analyser.cs +++ /dev/null @@ -1,573 +0,0 @@ -using System; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using System.IO; -using System.Linq; -using Semmle.Extraction.CSharp.Populators; -using System.Collections.Generic; -using System.Threading.Tasks; -using System.Diagnostics; -using Semmle.Util.Logging; -using Semmle.Util; - -namespace Semmle.Extraction.CSharp -{ - /// - /// Encapsulates a C# analysis task. - /// - public sealed class Analyser : IDisposable - { - private Extraction.Extractor extractor; - private CSharpCompilation compilation; - private Layout layout; - private bool init; - private readonly object progressMutex = new object(); - private int taskCount = 0; - private CommonOptions options; - private Entities.Compilation compilationEntity; - private IDisposable compilationTrapFile; - - private readonly Stopwatch stopWatch = new Stopwatch(); - - private readonly IProgressMonitor progressMonitor; - - public ILogger Logger { get; } - - public bool AddAssemblyTrapPrefix { get; } - - public PathTransformer PathTransformer { get; } - - public Analyser(IProgressMonitor pm, ILogger logger, bool addAssemblyTrapPrefix, PathTransformer pathTransformer) - { - Logger = logger; - AddAssemblyTrapPrefix = addAssemblyTrapPrefix; - Logger.Log(Severity.Info, "EXTRACTION STARTING at {0}", DateTime.Now); - stopWatch.Start(); - progressMonitor = pm; - PathTransformer = pathTransformer; - } - - /// - /// Start initialization of the analyser. - /// - /// The arguments passed to Roslyn. - /// A Boolean indicating whether to proceed with extraction. - public bool BeginInitialize(IEnumerable roslynArgs) - { - return init = LogRoslynArgs(roslynArgs, Extraction.Extractor.Version); - } - - /// - /// End initialization of the analyser. - /// - /// Arguments passed to csc. - /// Extractor options. - /// The Roslyn compilation. - /// A Boolean indicating whether to proceed with extraction. - public void EndInitialize( - CSharpCommandLineArguments commandLineArguments, - Options options, - CSharpCompilation compilation) - { - if (!init) - throw new InternalError("EndInitialize called without BeginInitialize returning true"); - layout = new Layout(); - this.options = options; - this.compilation = compilation; - extractor = new Extraction.Extractor(false, GetOutputName(compilation, commandLineArguments), Logger, PathTransformer); - LogDiagnostics(); - - SetReferencePaths(); - - CompilationErrors += FilteredDiagnostics.Count(); - } - - /// - /// Constructs the map from assembly string to its filename. - /// - /// Roslyn doesn't record the relationship between a filename and its assembly - /// information, so we need to retrieve this information manually. - /// - private void SetReferencePaths() - { - foreach (var reference in compilation.References.OfType()) - { - try - { - var refPath = reference.FilePath; - - /* This method is significantly faster and more lightweight than using - * System.Reflection.Assembly.ReflectionOnlyLoadFrom. It is also allows - * loading the same assembly from different locations. - */ - using var pereader = new System.Reflection.PortableExecutable.PEReader(new FileStream(refPath, FileMode.Open, FileAccess.Read, FileShare.Read)); - - var metadata = pereader.GetMetadata(); - string assemblyIdentity; - unsafe - { - var reader = new System.Reflection.Metadata.MetadataReader(metadata.Pointer, metadata.Length); - var def = reader.GetAssemblyDefinition(); - assemblyIdentity = reader.GetString(def.Name) + " " + def.Version; - } - extractor.SetAssemblyFile(assemblyIdentity, refPath); - - } - catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] - { - extractor.Message(new Message("Exception reading reference file", reference.FilePath, null, ex.StackTrace)); - } - } - } - - public void InitializeStandalone(CSharpCompilation compilationIn, CommonOptions options) - { - compilation = compilationIn; - layout = new Layout(); - extractor = new Extraction.Extractor(true, null, Logger, PathTransformer); - this.options = options; - LogExtractorInfo(Extraction.Extractor.Version); - SetReferencePaths(); - } - - private readonly HashSet errorsToIgnore = new HashSet - { - "CS7027", // Code signing failure - "CS1589", // XML referencing not supported - "CS1569" // Error writing XML documentation - }; - - private IEnumerable FilteredDiagnostics - { - get - { - return extractor == null || extractor.Standalone || compilation == null ? Enumerable.Empty() : - compilation. - GetDiagnostics(). - Where(e => e.Severity >= DiagnosticSeverity.Error && !errorsToIgnore.Contains(e.Id)); - } - } - - public IEnumerable MissingTypes => extractor.MissingTypes; - - public IEnumerable MissingNamespaces => extractor.MissingNamespaces; - - /// - /// Determine the path of the output dll/exe. - /// - /// Information about the compilation. - /// Cancellation token required. - /// The filename. - private static string GetOutputName(CSharpCompilation compilation, - CSharpCommandLineArguments commandLineArguments) - { - // There's no apparent way to access the output filename from the compilation, - // so we need to re-parse the command line arguments. - - if (commandLineArguments.OutputFileName == null) - { - // No output specified: Use name based on first filename - var entry = compilation.GetEntryPoint(System.Threading.CancellationToken.None); - if (entry == null) - { - if (compilation.SyntaxTrees.Length == 0) - throw new InvalidOperationException("No source files seen"); - - // Probably invalid, but have a go anyway. - var entryPointFile = compilation.SyntaxTrees.First().FilePath; - return Path.ChangeExtension(entryPointFile, ".exe"); - } - - var entryPointFilename = entry.Locations.First().SourceTree.FilePath; - return Path.ChangeExtension(entryPointFilename, ".exe"); - } - - return Path.Combine(commandLineArguments.OutputDirectory, commandLineArguments.OutputFileName); - } - - /// - /// Perform an analysis on a source file/syntax tree. - /// - /// Syntax tree to analyse. - public void AnalyseTree(SyntaxTree tree) - { - extractionTasks.Add(() => DoExtractTree(tree)); - } - - /// - /// Perform an analysis on an assembly. - /// - /// Assembly to analyse. - private void AnalyseReferenceAssembly(PortableExecutableReference assembly) - { - // CIL first - it takes longer. - if (options.CIL) - extractionTasks.Add(() => DoExtractCIL(assembly)); - extractionTasks.Add(() => DoAnalyseReferenceAssembly(assembly)); - } - - private static bool FileIsUpToDate(string src, string dest) - { - return File.Exists(dest) && - File.GetLastWriteTime(dest) >= File.GetLastWriteTime(src); - } - - /// - /// Extracts compilation-wide entities, such as compilations and compiler diagnostics. - /// - public void AnalyseCompilation() - { - extractionTasks.Add(() => DoAnalyseCompilation()); - } - - private void DoAnalyseCompilation() - { - try - { - var assemblyPath = extractor.OutputPath; - var transformedAssemblyPath = PathTransformer.Transform(assemblyPath); - var assembly = compilation.Assembly; - var projectLayout = layout.LookupProjectOrDefault(transformedAssemblyPath); - var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedAssemblyPath, options.TrapCompression, discardDuplicates: false); - compilationTrapFile = trapWriter; // Dispose later - var cx = new Context(extractor, compilation.Clone(), trapWriter, new AssemblyScope(assembly, assemblyPath), AddAssemblyTrapPrefix); - - compilationEntity = Entities.Compilation.Create(cx); - } - catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] - { - Logger.Log(Severity.Error, " Unhandled exception analyzing {0}: {1}", "compilation", ex); - } - } - - public void LogPerformance(Entities.PerformanceMetrics p) => compilationEntity.PopulatePerformance(p); - - /// - /// Extract an assembly to a new trap file. - /// If the trap file exists, skip extraction to avoid duplicating - /// extraction within the snapshot. - /// - /// The assembly to extract. - private void DoAnalyseReferenceAssembly(PortableExecutableReference r) - { - try - { - var stopwatch = new Stopwatch(); - stopwatch.Start(); - - var assemblyPath = r.FilePath; - var transformedAssemblyPath = PathTransformer.Transform(assemblyPath); - var projectLayout = layout.LookupProjectOrDefault(transformedAssemblyPath); - using var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedAssemblyPath, options.TrapCompression, discardDuplicates: true); - - var skipExtraction = options.Cache && File.Exists(trapWriter.TrapFile); - - if (!skipExtraction) - { - /* Note on parallel builds: - * - * The trap writer and source archiver both perform atomic moves - * of the file to the final destination. - * - * If the same source file or trap file are generated concurrently - * (by different parallel invocations of the extractor), then - * last one wins. - * - * Specifically, if two assemblies are analysed concurrently in a build, - * then there is a small amount of duplicated work but the output should - * still be correct. - */ - - // compilation.Clone() reduces memory footprint by allowing the symbols - // in c to be garbage collected. - Compilation c = compilation.Clone(); - - - if (c.GetAssemblyOrModuleSymbol(r) is IAssemblySymbol assembly) - { - var cx = new Context(extractor, c, trapWriter, new AssemblyScope(assembly, assemblyPath), AddAssemblyTrapPrefix); - - foreach (var module in assembly.Modules) - { - AnalyseNamespace(cx, module.GlobalNamespace); - } - - Entities.Attribute.ExtractAttributes(cx, assembly, Extraction.Entities.Assembly.Create(cx, assembly.GetSymbolLocation())); - - cx.PopulateAll(); - } - } - - ReportProgress(assemblyPath, trapWriter.TrapFile, stopwatch.Elapsed, skipExtraction ? AnalysisAction.UpToDate : AnalysisAction.Extracted); - } - catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] - { - Logger.Log(Severity.Error, " Unhandled exception analyzing {0}: {1}", r.FilePath, ex); - } - } - - private void DoExtractCIL(PortableExecutableReference r) - { - var stopwatch = new Stopwatch(); - stopwatch.Start(); - CIL.Entities.Assembly.ExtractCIL(layout, r.FilePath, Logger, !options.Cache, options.PDB, options.TrapCompression, out var trapFile, out var extracted); - stopwatch.Stop(); - ReportProgress(r.FilePath, trapFile, stopwatch.Elapsed, extracted ? AnalysisAction.Extracted : AnalysisAction.UpToDate); - } - - private void AnalyseNamespace(Context cx, INamespaceSymbol ns) - { - foreach (var memberNamespace in ns.GetNamespaceMembers()) - { - AnalyseNamespace(cx, memberNamespace); - } - - foreach (var memberType in ns.GetTypeMembers()) - { - Entities.Type.Create(cx, memberType).ExtractRecursive(); - } - } - - /// - /// Enqueue all reference analysis tasks. - /// - public void AnalyseReferences() - { - foreach (var r in compilation.References.OfType()) - { - AnalyseReferenceAssembly(r); - } - } - - // The bulk of the extraction work, potentially executed in parallel. - private readonly List extractionTasks = new List(); - - private void ReportProgress(string src, string output, TimeSpan time, AnalysisAction action) - { - lock (progressMutex) - progressMonitor.Analysed(++taskCount, extractionTasks.Count, src, output, time, action); - } - - private void DoExtractTree(SyntaxTree tree) - { - try - { - var stopwatch = new Stopwatch(); - stopwatch.Start(); - var sourcePath = tree.FilePath; - var transformedSourcePath = PathTransformer.Transform(sourcePath); - - var projectLayout = layout.LookupProjectOrNull(transformedSourcePath); - var excluded = projectLayout == null; - var trapPath = excluded ? "" : projectLayout.GetTrapPath(Logger, transformedSourcePath, options.TrapCompression); - var upToDate = false; - - if (!excluded) - { - // compilation.Clone() is used to allow symbols to be garbage collected. - using var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedSourcePath, options.TrapCompression, discardDuplicates: false); - - upToDate = options.Fast && FileIsUpToDate(sourcePath, trapWriter.TrapFile); - - if (!upToDate) - { - var cx = new Context(extractor, compilation.Clone(), trapWriter, new SourceScope(tree), AddAssemblyTrapPrefix); - // Ensure that the file itself is populated in case the source file is totally empty - var root = tree.GetRoot(); - Extraction.Entities.File.Create(cx, root.SyntaxTree.FilePath); - - var csNode = (CSharpSyntaxNode)root; - csNode.Accept(new CompilationUnitVisitor(cx)); - csNode.Accept(new DirectiveVisitor(cx)); - cx.PopulateAll(); - CommentPopulator.ExtractCommentBlocks(cx, cx.CommentGenerator); - cx.PopulateAll(); - } - } - - ReportProgress(sourcePath, trapPath, stopwatch.Elapsed, excluded - ? AnalysisAction.Excluded - : upToDate - ? AnalysisAction.UpToDate - : AnalysisAction.Extracted); - } - catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] - { - extractor.Message(new Message($"Unhandled exception processing syntax tree. {ex.Message}", tree.FilePath, null, ex.StackTrace)); - } - } - - /// - /// Run all extraction tasks. - /// - /// The number of threads to use. - public void PerformExtraction(int numberOfThreads) - { - Parallel.Invoke( - new ParallelOptions { MaxDegreeOfParallelism = numberOfThreads }, - extractionTasks.ToArray()); - } - - public void Dispose() - { - compilationTrapFile?.Dispose(); - - stopWatch.Stop(); - Logger.Log(Severity.Info, " Peak working set = {0} MB", Process.GetCurrentProcess().PeakWorkingSet64 / (1024 * 1024)); - - if (TotalErrors > 0) - Logger.Log(Severity.Info, "EXTRACTION FAILED with {0} error{1} in {2}", TotalErrors, TotalErrors == 1 ? "" : "s", stopWatch.Elapsed); - else - Logger.Log(Severity.Info, "EXTRACTION SUCCEEDED in {0}", stopWatch.Elapsed); - - Logger.Dispose(); - } - - /// - /// Number of errors encountered during extraction. - /// - public int ExtractorErrors => extractor == null ? 0 : extractor.Errors; - - /// - /// Number of errors encountered by the compiler. - /// - public int CompilationErrors { get; set; } - - /// - /// Total number of errors reported. - /// - public int TotalErrors => CompilationErrors + ExtractorErrors; - - /// - /// Logs information about the extractor. - /// - public void LogExtractorInfo(string extractorVersion) - { - Logger.Log(Severity.Info, " Extractor: {0}", Environment.GetCommandLineArgs().First()); - Logger.Log(Severity.Info, " Extractor version: {0}", extractorVersion); - Logger.Log(Severity.Info, " Current working directory: {0}", Directory.GetCurrentDirectory()); - } - - /// - /// Logs information about the extractor, as well as the arguments to Roslyn. - /// - /// The arguments passed to Roslyn. - /// A Boolean indicating whether the same arguments have been logged previously. - private bool LogRoslynArgs(IEnumerable roslynArgs, string extractorVersion) - { - LogExtractorInfo(extractorVersion); - Logger.Log(Severity.Info, $" Arguments to Roslyn: {string.Join(' ', roslynArgs)}"); - - var tempFile = Extractor.GetCSharpArgsLogPath(Path.GetRandomFileName()); - - bool argsWritten; - using (var streamWriter = new StreamWriter(new FileStream(tempFile, FileMode.Append, FileAccess.Write))) - { - streamWriter.WriteLine($"# Arguments to Roslyn: {string.Join(' ', roslynArgs.Where(arg => !arg.StartsWith('@')))}"); - argsWritten = roslynArgs.WriteCommandLine(streamWriter); - } - - var hash = FileUtils.ComputeFileHash(tempFile); - var argsFile = Extractor.GetCSharpArgsLogPath(hash); - - if (argsWritten) - Logger.Log(Severity.Info, $" Arguments have been written to {argsFile}"); - - if (File.Exists(argsFile)) - { - try - { - File.Delete(tempFile); - } - catch (IOException e) - { - Logger.Log(Severity.Warning, $" Failed to remove {tempFile}: {e.Message}"); - } - return false; - } - - try - { - File.Move(tempFile, argsFile); - } - catch (IOException e) - { - Logger.Log(Severity.Warning, $" Failed to move {tempFile} to {argsFile}: {e.Message}"); - } - - return true; - } - - - /// - /// Logs detailed information about this invocation, - /// in the event that errors were detected. - /// - /// A Boolean indicating whether to proceed with extraction. - public void LogDiagnostics() - { - foreach (var error in FilteredDiagnostics) - { - Logger.Log(Severity.Error, " Compilation error: {0}", error); - } - - if (FilteredDiagnostics.Any()) - { - foreach (var reference in compilation.References) - { - Logger.Log(Severity.Info, " Resolved reference {0}", reference.Display); - } - } - } - } - - /// - /// What action was performed when extracting a file. - /// - public enum AnalysisAction - { - Extracted, - UpToDate, - Excluded - } - - /// - /// Callback for various extraction events. - /// (Used for display of progress). - /// - public interface IProgressMonitor - { - /// - /// Callback that a particular item has been analysed. - /// - /// The item number being processed. - /// The total number of items to process. - /// The name of the item, e.g. a source file. - /// The name of the item being output, e.g. a trap file. - /// The time to extract the item. - /// What action was taken for the file. - void Analysed(int item, int total, string source, string output, TimeSpan time, AnalysisAction action); - - /// - /// A "using namespace" directive was seen but the given - /// namespace could not be found. - /// Only called once for each @namespace. - /// - /// - void MissingNamespace(string @namespace); - - /// - /// An ErrorType was found. - /// Called once for each type name. - /// - /// The full/partial name of the type. - void MissingType(string type); - - /// - /// Report a summary of missing entities. - /// - /// The number of missing types. - /// The number of missing using namespace declarations. - void MissingSummary(int types, int namespaces); - } -} diff --git a/csharp/extractor/Semmle.Extraction.CSharp/CachedEntityFactory.cs b/csharp/extractor/Semmle.Extraction.CSharp/CachedEntityFactory.cs new file mode 100644 index 00000000000..2673ed0d7ef --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp/CachedEntityFactory.cs @@ -0,0 +1,19 @@ +namespace Semmle.Extraction.CSharp +{ + /// + /// A factory for creating cached entities. + /// + internal abstract class CachedEntityFactory + : Extraction.CachedEntityFactory where TEntity : CachedEntity + { + /// + /// Initializes the entity, but does not generate any trap code. + /// + public sealed override TEntity Create(Extraction.Context cx, TInit init) + { + return Create((Context)cx, init); + } + + public abstract TEntity Create(Context cx, TInit init); + } +} diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Comments/CommentBinding.cs b/csharp/extractor/Semmle.Extraction.CSharp/Comments/CommentBinding.cs new file mode 100644 index 00000000000..22518ba4930 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp/Comments/CommentBinding.cs @@ -0,0 +1,13 @@ +namespace Semmle.Extraction.CSharp +{ + /// + /// Describes the relationship between a comment and a program element. + /// + public enum CommentBinding + { + Parent, // The parent element of a comment + Best, // The most likely element associated with a comment + Before, // The element before the comment + After // The element after the comment + }; +} diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Comments/CommentBlock.cs b/csharp/extractor/Semmle.Extraction.CSharp/Comments/CommentBlock.cs new file mode 100644 index 00000000000..c7bb7c27008 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp/Comments/CommentBlock.cs @@ -0,0 +1,59 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; +using Semmle.Extraction.CSharp.Entities; +using System.Collections.Generic; +using System.Linq; + +namespace Semmle.Extraction.CSharp.Comments +{ + internal class CommentBlock + { + private readonly List lines; + + public IEnumerable CommentLines => lines; + + public Location Location { get; private set; } + + public CommentBlock(CommentLine firstLine) + { + lines = new List { firstLine }; + Location = firstLine.Location; + } + + /// + /// Determine whether commentlines should be merged. + /// + /// A comment line to be appended to this comment block. + /// Whether the new line should be appended to this block. + public bool CombinesWith(CommentLine newLine) + { + if (!CommentLines.Any()) + return true; + + var sameFile = Location.SourceTree == newLine.Location.SourceTree; + var sameRow = Location.EndLine() == newLine.Location.StartLine(); + var sameColumn = Location.EndLine() + 1 == newLine.Location.StartLine(); + var nextRow = Location.StartColumn() == newLine.Location.StartColumn(); + var adjacent = sameFile && (sameRow || (sameColumn && nextRow)); + + return + newLine.Type == CommentLineType.MultilineContinuation || + adjacent; + } + + /// + /// Adds a comment line to the this comment block. + /// + /// The line to add. + public void AddCommentLine(CommentLine line) + { + Location = !lines.Any() + ? line.Location + : Location.Create( + line.Location.SourceTree!, + new TextSpan(Location.SourceSpan.Start, line.Location.SourceSpan.End - Location.SourceSpan.Start)); + + lines.Add(line); + } + } +} diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Comments/CommentLineType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Comments/CommentLineType.cs new file mode 100644 index 00000000000..8506ca701b3 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp/Comments/CommentLineType.cs @@ -0,0 +1,13 @@ +namespace Semmle.Extraction.CSharp +{ + /// + /// The type of a single comment line. + /// + public enum CommentLineType + { + Singleline, // Comment starting // ... + XmlDoc, // Comment starting /// ... + Multiline, // Comment starting /* ..., even if the comment only spans one line. + MultilineContinuation // The second and subsequent lines of comment in a multiline comment. + }; +} diff --git a/csharp/extractor/Semmle.Extraction/CommentProcessing.cs b/csharp/extractor/Semmle.Extraction.CSharp/Comments/CommentProcessor.cs similarity index 77% rename from csharp/extractor/Semmle.Extraction/CommentProcessing.cs rename to csharp/extractor/Semmle.Extraction.CSharp/Comments/CommentProcessor.cs index 201ac705136..e854a04f729 100644 --- a/csharp/extractor/Semmle.Extraction/CommentProcessing.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Comments/CommentProcessor.cs @@ -1,25 +1,25 @@ using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Text; +using Semmle.Extraction.CSharp.Entities; using Semmle.Util; using System.Collections.Generic; using System.Linq; -namespace Semmle.Extraction.CommentProcessing +namespace Semmle.Extraction.CSharp { /// /// Implements the comment processor for associating comments with program elements. /// Registers locations of comments and program elements, /// then generates binding information. /// - internal class CommentProcessor : ICommentGenerator + internal class CommentProcessor { - public void AddComment(ICommentLine comment) + public void AddComment(CommentLine comment) { comments[comment.Location] = comment; } // Comments sorted by location. - private readonly SortedDictionary comments = new SortedDictionary(new LocationComparer()); + private readonly SortedDictionary comments = new SortedDictionary(new LocationComparer()); // Program elements sorted by location. private readonly SortedDictionary elements = new SortedDictionary(new LocationComparer()); @@ -48,9 +48,9 @@ namespace Semmle.Extraction.CommentProcessing { if (object.ReferenceEquals(l1, l2)) return 0; - if (l1 == null) + if (l1 is null) return -1; - if (l2 == null) + if (l2 is null) return 1; var diff = l1.SourceTree == l2.SourceTree ? 0 : l1.SourceTree!.FilePath.CompareTo(l2.SourceTree!.FilePath); @@ -68,19 +68,19 @@ namespace Semmle.Extraction.CommentProcessing /// The label of the element in the trap file. /// The duplication guard key of the element, if any. /// The location of the element. - public void AddElement(Label elementLabel, Key? duplicationGuardKey, Location loc) + public void AddElement(Label elementLabel, Key? duplicationGuardKey, Location? loc) { - if (loc != null && loc.IsInSource) + if (loc is not null && loc.IsInSource) elements[loc] = elementLabel; - if (duplicationGuardKey != null) + if (duplicationGuardKey is not null) duplicationGuardKeys[elementLabel] = duplicationGuardKey; } // Ensure that commentBlock and element refer to the same file // which can happen when processing multiple files. - private static void EnsureSameFile(ICommentBlock commentBlock, ref KeyValuePair? element) + private static void EnsureSameFile(Comments.CommentBlock commentBlock, ref KeyValuePair? element) { - if (element != null && element.Value.Key.SourceTree != commentBlock.Location.SourceTree) + if (element is not null && element.Value.Key.SourceTree != commentBlock.Location.SourceTree) element = null; } @@ -95,7 +95,7 @@ namespace Semmle.Extraction.CommentProcessing /// The parent element of the comment block. /// Output binding information. private void GenerateBindings( - ICommentBlock commentBlock, + Comments.CommentBlock commentBlock, KeyValuePair? previousElement, KeyValuePair? nextElement, KeyValuePair? parentElement, @@ -106,19 +106,19 @@ namespace Semmle.Extraction.CommentProcessing EnsureSameFile(commentBlock, ref nextElement); EnsureSameFile(commentBlock, ref parentElement); - if (previousElement != null) + if (previousElement is not null) { var key = previousElement.Value.Value; callback(key, GetDuplicationGuardKey(key), commentBlock, CommentBinding.Before); } - if (nextElement != null) + if (nextElement is not null) { var key = nextElement.Value.Value; callback(key, GetDuplicationGuardKey(key), commentBlock, CommentBinding.After); } - if (parentElement != null) + if (parentElement is not null) { var key = parentElement.Value.Value; callback(key, GetDuplicationGuardKey(key), commentBlock, CommentBinding.Parent); @@ -127,17 +127,17 @@ namespace Semmle.Extraction.CommentProcessing // Heuristic to decide which is the "best" element associated with the comment. KeyValuePair? bestElement; - if (previousElement != null && previousElement.Value.Key.EndLine() == commentBlock.Location.StartLine()) + if (previousElement is not null && previousElement.Value.Key.EndLine() == commentBlock.Location.StartLine()) { // 1. If the comment is on the same line as the previous element, use that bestElement = previousElement; } - else if (nextElement != null && nextElement.Value.Key.StartLine() == commentBlock.Location.EndLine()) + else if (nextElement is not null && nextElement.Value.Key.StartLine() == commentBlock.Location.EndLine()) { // 2. If the comment is on the same line as the next element, use that bestElement = nextElement; } - else if (nextElement != null && previousElement != null && + else if (nextElement is not null && previousElement is not null && previousElement.Value.Key.EndLine() + 1 == commentBlock.Location.StartLine() && commentBlock.Location.EndLine() + 1 == nextElement.Value.Key.StartLine()) { @@ -145,12 +145,12 @@ namespace Semmle.Extraction.CommentProcessing // because it's ambiguous whether the comment refers to the next or previous element bestElement = parentElement; } - else if (nextElement != null && nextElement.Value.Key.StartLine() == commentBlock.Location.EndLine() + 1) + else if (nextElement is not null && nextElement.Value.Key.StartLine() == commentBlock.Location.EndLine() + 1) { // 4. If there is no gap after the comment, use "nextElement" bestElement = nextElement; } - else if (previousElement != null && previousElement.Value.Key.EndLine() + 1 == commentBlock.Location.StartLine()) + else if (previousElement is not null && previousElement.Value.Key.EndLine() + 1 == commentBlock.Location.StartLine()) { // 5. If there is no gap before the comment, use previousElement bestElement = previousElement; @@ -168,7 +168,7 @@ namespace Semmle.Extraction.CommentProcessing */ } - if (bestElement != null) + if (bestElement is not null) { var label = bestElement.Value.Value; callback(label, GetDuplicationGuardKey(label), commentBlock, CommentBinding.Best); @@ -231,7 +231,7 @@ namespace Semmle.Extraction.CommentProcessing // Generate binding information for one CommentBlock. private void GenerateBindings( - ICommentBlock block, + Comments.CommentBlock block, ElementStack elementStack, KeyValuePair? nextElement, CommentBindingCallback cb @@ -259,25 +259,25 @@ namespace Semmle.Extraction.CommentProcessing /// Where to send the results. /// true if there are more comments to process, false otherwise. private bool GenerateBindings( - IEnumerator> commentEnumerator, + IEnumerator> commentEnumerator, KeyValuePair? nextElement, ElementStack elementStack, CommentBindingCallback cb ) { - CommentBlock? block = null; + Comments.CommentBlock? block = null; // Iterate comments until the commentEnumerator has gone past nextElement - while (nextElement == null || Compare(commentEnumerator.Current.Value.Location, nextElement.Value.Key) < 0) + while (nextElement is null || Compare(commentEnumerator.Current.Value.Location, nextElement.Value.Key) < 0) { if (block is null) - block = new CommentBlock(commentEnumerator.Current.Value); + block = new Comments.CommentBlock(commentEnumerator.Current.Value); if (!block.CombinesWith(commentEnumerator.Current.Value)) { // Start of a new block, so generate the bindings for the old block first. GenerateBindings(block, elementStack, nextElement, cb); - block = new CommentBlock(commentEnumerator.Current.Value); + block = new Comments.CommentBlock(commentEnumerator.Current.Value); } else { @@ -320,7 +320,7 @@ namespace Semmle.Extraction.CommentProcessing var elementStack = new ElementStack(); using IEnumerator> elementEnumerator = elements.GetEnumerator(); - using IEnumerator> commentEnumerator = comments.GetEnumerator(); + using IEnumerator> commentEnumerator = comments.GetEnumerator(); if (!commentEnumerator.MoveNext()) { // There are no comments to process. @@ -343,54 +343,12 @@ namespace Semmle.Extraction.CommentProcessing } } - internal class CommentBlock : ICommentBlock - { - private readonly List lines; - - public IEnumerable CommentLines => lines; - - public Location Location { get; private set; } - - public CommentBlock(ICommentLine firstLine) - { - lines = new List { firstLine }; - Location = firstLine.Location; - } - - /// - /// Determine whether commentlines should be merged. - /// - /// A comment line to be appended to this comment block. - /// Whether the new line should be appended to this block. - public bool CombinesWith(ICommentLine newLine) - { - if (!CommentLines.Any()) - return true; - - var sameFile = Location.SourceTree == newLine.Location.SourceTree; - var sameRow = Location.EndLine() == newLine.Location.StartLine(); - var sameColumn = Location.EndLine() + 1 == newLine.Location.StartLine(); - var nextRow = Location.StartColumn() == newLine.Location.StartColumn(); - var adjacent = sameFile && (sameRow || (sameColumn && nextRow)); - - return - newLine.Type == CommentLineType.MultilineContinuation || - adjacent; - } - - /// - /// Adds a comment line to the this comment block. - /// - /// The line to add. - public void AddCommentLine(ICommentLine line) - { - Location = !lines.Any() - ? line.Location - : Location.Create( - line.Location.SourceTree!, - new TextSpan(Location.SourceSpan.Start, line.Location.SourceSpan.End - Location.SourceSpan.Start)); - - lines.Add(line); - } - } + /// + /// Callback for generated comment associations. + /// + /// The label of the element + /// The duplication guard key of the element, if any + /// The comment block associated with the element + /// The relationship between the commentblock and the element + internal delegate void CommentBindingCallback(Label elementLabel, Key? duplicationGuardKey, Comments.CommentBlock commentBlock, CommentBinding binding); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Accessor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Accessor.cs index d5a20e8eef6..5223d902cfd 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Accessor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Accessor.cs @@ -13,7 +13,7 @@ namespace Semmle.Extraction.CSharp.Entities /// Gets the property symbol associated accessor `symbol`, or `null` /// if there is no associated symbol. /// - public static IPropertySymbol GetPropertySymbol(IMethodSymbol symbol) + public static IPropertySymbol? GetPropertySymbol(IMethodSymbol symbol) { // Usually, the property/indexer can be fetched from the associated symbol if (symbol.AssociatedSymbol is IPropertySymbol prop) @@ -29,7 +29,7 @@ namespace Semmle.Extraction.CSharp.Entities /// /// Gets the property symbol associated with this accessor. /// - private IPropertySymbol PropertySymbol => GetPropertySymbol(Symbol); + private IPropertySymbol? PropertySymbol => GetPropertySymbol(Symbol); public new Accessor OriginalDefinition => Create(Context, Symbol.OriginalDefinition); @@ -37,10 +37,10 @@ namespace Semmle.Extraction.CSharp.Entities { PopulateMethod(trapFile); PopulateModifiers(trapFile); - ContainingType.PopulateGenerics(); + ContainingType!.PopulateGenerics(); var prop = PropertySymbol; - if (prop == null) + if (prop is null) { Context.ModelError(Symbol, "Unhandled accessor associated symbol"); return; @@ -52,12 +52,12 @@ namespace Semmle.Extraction.CSharp.Entities if (SymbolEqualityComparer.Default.Equals(Symbol, prop.GetMethod)) { kind = 1; - unboundAccessor = Create(Context, prop.OriginalDefinition.GetMethod); + unboundAccessor = Create(Context, prop.OriginalDefinition.GetMethod!); } else if (SymbolEqualityComparer.Default.Equals(Symbol, prop.SetMethod)) { kind = 2; - unboundAccessor = Create(Context, prop.OriginalDefinition.SetMethod); + unboundAccessor = Create(Context, prop.OriginalDefinition.SetMethod!); } else { @@ -72,7 +72,7 @@ namespace Semmle.Extraction.CSharp.Entities Overrides(trapFile); - if (Symbol.FromSource() && Block == null) + if (Symbol.FromSource() && Block is null) { trapFile.compiler_generated(this); } @@ -86,11 +86,11 @@ namespace Semmle.Extraction.CSharp.Entities public static new Accessor Create(Context cx, IMethodSymbol symbol) => AccessorFactory.Instance.CreateEntityFromSymbol(cx, symbol); - private class AccessorFactory : ICachedEntityFactory + private class AccessorFactory : CachedEntityFactory { public static AccessorFactory Instance { get; } = new AccessorFactory(); - public Accessor Create(Context cx, IMethodSymbol init) => new Accessor(cx, init); + public override Accessor Create(Context cx, IMethodSymbol init) => new Accessor(cx, init); } } } diff --git a/csharp/extractor/Semmle.Extraction/Entities/Assembly.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Assembly.cs similarity index 71% rename from csharp/extractor/Semmle.Extraction/Entities/Assembly.cs rename to csharp/extractor/Semmle.Extraction.CSharp/Entities/Assembly.cs index 42fe1f2b30b..c0ffce70dbe 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/Assembly.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Assembly.cs @@ -1,20 +1,23 @@ using Microsoft.CodeAnalysis; +using Semmle.Extraction.CSharp; using System.IO; -namespace Semmle.Extraction.Entities +namespace Semmle.Extraction.CSharp.Entities { - public class Assembly : Location + internal class Assembly : Extraction.Entities.Location { + public override Context Context => (Context)base.Context; + private readonly string assemblyPath; private readonly IAssemblySymbol assembly; private Assembly(Context cx, Microsoft.CodeAnalysis.Location? init) : base(cx, init) { - if (init == null) + if (init is null) { // This is the output assembly - assemblyPath = cx.Extractor.OutputPath; + assemblyPath = ((TracingExtractor)cx.Extractor).OutputPath; assembly = cx.Compilation.Assembly; } else @@ -28,7 +31,7 @@ namespace Semmle.Extraction.Entities public override void Populate(TextWriter trapFile) { - if (assemblyPath != null) + if (assemblyPath is not null) { trapFile.assemblies(this, File.Create(Context, assemblyPath), assembly.ToString() ?? "", assembly.Identity.Name, assembly.Identity.Version.ToString()); @@ -38,7 +41,7 @@ namespace Semmle.Extraction.Entities public override bool NeedsPopulation => true; public override int GetHashCode() => - Symbol == null ? 91187354 : Symbol.GetHashCode(); + Symbol is null ? 91187354 : Symbol.GetHashCode(); public override bool Equals(object? obj) { @@ -48,20 +51,20 @@ namespace Semmle.Extraction.Entities return false; } - public static Location Create(Context cx, Microsoft.CodeAnalysis.Location loc) => AssemblyConstructorFactory.Instance.CreateEntity(cx, loc, loc); + public static Extraction.Entities.Location Create(Context cx, Microsoft.CodeAnalysis.Location loc) => AssemblyConstructorFactory.Instance.CreateEntity(cx, loc, loc); - private class AssemblyConstructorFactory : ICachedEntityFactory + private class AssemblyConstructorFactory : CachedEntityFactory { public static AssemblyConstructorFactory Instance { get; } = new AssemblyConstructorFactory(); - public Assembly Create(Context cx, Microsoft.CodeAnalysis.Location? init) => new Assembly(cx, init); + public override Assembly Create(Context cx, Microsoft.CodeAnalysis.Location? init) => new Assembly(cx, init); } private static readonly object outputAssemblyCacheKey = new object(); public static Assembly CreateOutputAssembly(Context cx) { - if (cx.Extractor.OutputPath == null) + if (cx.Extractor.Standalone) throw new InternalError("Attempting to create the output assembly in standalone extraction mode"); return AssemblyConstructorFactory.Instance.CreateEntity(cx, outputAssemblyCacheKey, null); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Attribute.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Attribute.cs index ba1d2093765..b1bfb508d75 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Attribute.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Attribute.cs @@ -10,7 +10,7 @@ namespace Semmle.Extraction.CSharp.Entities { bool IExpressionParentEntity.IsTopLevelParent => true; - private readonly AttributeSyntax attributeSyntax; + private readonly AttributeSyntax? attributeSyntax; private readonly IEntity entity; private Attribute(Context cx, AttributeData attributeData, IEntity entity) @@ -51,9 +51,9 @@ namespace Semmle.Extraction.CSharp.Entities trapFile.attributes(this, type.TypeRef, entity); trapFile.attribute_location(this, Location); - if (attributeSyntax is object) + if (attributeSyntax is not null) { - if (Context.Extractor.OutputPath != null) + if (!Context.Extractor.Standalone) { trapFile.attribute_location(this, Assembly.CreateOutputAssembly(Context)); } @@ -66,18 +66,18 @@ namespace Semmle.Extraction.CSharp.Entities private void ExtractArguments(TextWriter trapFile) { - var ctorArguments = attributeSyntax?.ArgumentList?.Arguments.Where(a => a.NameEquals == null).ToList(); + var ctorArguments = attributeSyntax?.ArgumentList?.Arguments.Where(a => a.NameEquals is null).ToList(); var childIndex = 0; for (var i = 0; i < Symbol.ConstructorArguments.Length; i++) { var constructorArgument = Symbol.ConstructorArguments[i]; var paramName = Symbol.AttributeConstructor?.Parameters[i].Name; - var argSyntax = ctorArguments?.SingleOrDefault(a => a.NameColon != null && a.NameColon.Name.Identifier.Text == paramName); + var argSyntax = ctorArguments?.SingleOrDefault(a => a.NameColon is not null && a.NameColon.Name.Identifier.Text == paramName); - if (argSyntax == null && // couldn't find named argument + if (argSyntax is null && // couldn't find named argument ctorArguments?.Count > childIndex && // there're more arguments - ctorArguments[childIndex].NameColon == null) // the argument is positional + ctorArguments[childIndex].NameColon is null) // the argument is positional { argSyntax = ctorArguments[childIndex]; } @@ -93,18 +93,18 @@ namespace Semmle.Extraction.CSharp.Entities { var expr = CreateExpressionFromArgument( namedArgument.Value, - attributeSyntax?.ArgumentList.Arguments.Single(a => a.NameEquals?.Name?.Identifier.Text == namedArgument.Key).Expression, + attributeSyntax?.ArgumentList?.Arguments.Single(a => a.NameEquals?.Name?.Identifier.Text == namedArgument.Key).Expression, this, childIndex++); - if (expr is object) + if (expr is not null) { trapFile.expr_argument_name(expr, namedArgument.Key); } } } - private Expression CreateExpressionFromArgument(TypedConstant constant, ExpressionSyntax syntax, IExpressionParentEntity parent, + private Expression? CreateExpressionFromArgument(TypedConstant constant, ExpressionSyntax? syntax, IExpressionParentEntity parent, int childIndex) { return syntax is null @@ -114,11 +114,14 @@ namespace Semmle.Extraction.CSharp.Entities public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.OptionalLabel; - public override Microsoft.CodeAnalysis.Location ReportingLocation => attributeSyntax?.Name.GetLocation(); + public override Microsoft.CodeAnalysis.Location? ReportingLocation => attributeSyntax?.Name.GetLocation(); + + private Semmle.Extraction.Entities.Location? location; - private Semmle.Extraction.Entities.Location location; private Semmle.Extraction.Entities.Location Location => - location ?? (location = Context.CreateLocation(attributeSyntax is null ? entity.ReportingLocation : attributeSyntax.Name.GetLocation())); + location ??= Context.CreateLocation(attributeSyntax is null + ? entity.ReportingLocation + : attributeSyntax.Name.GetLocation()); public override bool NeedsPopulation => true; @@ -136,11 +139,11 @@ namespace Semmle.Extraction.CSharp.Entities return AttributeFactory.Instance.CreateEntity(cx, attributeData, init); } - private class AttributeFactory : ICachedEntityFactory<(AttributeData attributeData, IEntity receiver), Attribute> + private class AttributeFactory : CachedEntityFactory<(AttributeData attributeData, IEntity receiver), Attribute> { public static readonly AttributeFactory Instance = new AttributeFactory(); - public Attribute Create(Context cx, (AttributeData attributeData, IEntity receiver) init) => + public override Attribute Create(Context cx, (AttributeData attributeData, IEntity receiver) init) => new Attribute(cx, init.attributeData, init.receiver); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CachedEntity.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CachedEntity.cs new file mode 100644 index 00000000000..603a1d822b1 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CachedEntity.cs @@ -0,0 +1,12 @@ +namespace Semmle.Extraction.CSharp.Entities +{ + internal abstract class CachedEntity : Extraction.CachedEntity where T : notnull + { + public override Context Context => (Context)base.Context; + + protected CachedEntity(Context context, T symbol) + : base(context, symbol) + { + } + } +} diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Symbol.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CachedSymbol.cs similarity index 82% rename from csharp/extractor/Semmle.Extraction.CSharp/Entities/Symbol.cs rename to csharp/extractor/Semmle.Extraction.CSharp/Entities/CachedSymbol.cs index 2ac5c6e90e5..d6fb0d65e5d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Symbol.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CachedSymbol.cs @@ -9,12 +9,14 @@ using System.Reflection.Metadata.Ecma335; namespace Semmle.Extraction.CSharp.Entities { - public abstract class CachedSymbol : CachedEntity where T : ISymbol + internal abstract class CachedSymbol : CachedEntity where T : class, ISymbol { protected CachedSymbol(Context cx, T init) - : base(cx, init) { } + : base(cx, init) + { + } - public virtual Type ContainingType => Symbol.ContainingType != null ? Type.Create(Context, Symbol.ContainingType) : null; + public virtual Type? ContainingType => Symbol.ContainingType is not null ? Type.Create(Context, Symbol.ContainingType) : null; public void PopulateModifiers(TextWriter trapFile) { @@ -63,23 +65,23 @@ namespace Semmle.Extraction.CSharp.Entities /// The location which is stored in the database and is used when highlighing source code. /// It's generally short, e.g. a method name. /// - public override Microsoft.CodeAnalysis.Location ReportingLocation => Symbol.Locations.FirstOrDefault(); + public override Microsoft.CodeAnalysis.Location? ReportingLocation => Symbol.Locations.FirstOrDefault(); /// /// The full text span of the entity, e.g. for binding comments. /// - public virtual Microsoft.CodeAnalysis.Location FullLocation => Symbol.Locations.FirstOrDefault(); + public virtual Microsoft.CodeAnalysis.Location? FullLocation => Symbol.Locations.FirstOrDefault(); public virtual IEnumerable Locations { get { var loc = ReportingLocation; - if (loc != null) + if (loc is not null) { // Some built in operators lack locations, so loc is null. yield return Context.CreateLocation(ReportingLocation); - if (Context.Extractor.OutputPath != null && loc.Kind == LocationKind.SourceFile) + if (!Context.Extractor.Standalone && loc.Kind == LocationKind.SourceFile) yield return Assembly.CreateOutputAssembly(Context); } } @@ -97,7 +99,7 @@ namespace Semmle.Extraction.CSharp.Entities protected virtual T BodyDeclaringSymbol => Symbol; - public BlockSyntax Block + public BlockSyntax? Block { get { @@ -108,7 +110,7 @@ namespace Semmle.Extraction.CSharp.Entities } } - public ExpressionSyntax ExpressionBody + public ExpressionSyntax? ExpressionBody { get { @@ -134,7 +136,7 @@ namespace Semmle.Extraction.CSharp.Entities trapFile.metadata_handle(this, Location, MetadataTokens.GetToken(handle.Value)); } - private static System.Reflection.PropertyInfo GetPropertyInfo(object o, string name) + private static System.Reflection.PropertyInfo? GetPropertyInfo(object o, string name) { return o.GetType().GetProperty(name, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetProperty); } @@ -149,17 +151,14 @@ namespace Semmle.Extraction.CSharp.Entities if (handleProp is null) { var underlyingSymbolProp = GetPropertyInfo(Symbol, "UnderlyingSymbol"); - if (underlyingSymbolProp is object) + if (underlyingSymbolProp?.GetValue(Symbol) is object underlying) { - if (underlyingSymbolProp.GetValue(Symbol) is object underlying) - { - handleProp = GetPropertyInfo(underlying, "Handle"); - handleObj = underlying; - } + handleProp = GetPropertyInfo(underlying, "Handle"); + handleObj = underlying; } } - if (handleProp is object) + if (handleProp is not null) { switch (handleProp.GetValue(handleObj)) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs index e06b6aebe7f..a3394885d1c 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs @@ -1,12 +1,11 @@ -using Semmle.Extraction.CommentProcessing; using Semmle.Extraction.Entities; using System.IO; namespace Semmle.Extraction.CSharp.Entities { - internal class CommentBlock : CachedEntity + internal class CommentBlock : CachedEntity { - private CommentBlock(Context cx, ICommentBlock init) + private CommentBlock(Context cx, Comments.CommentBlock init) : base(cx, init) { } public override void Populate(TextWriter trapFile) @@ -35,13 +34,13 @@ namespace Semmle.Extraction.CSharp.Entities Context.TrapWriter.Writer.commentblock_binding(this, entity, binding); } - public static CommentBlock Create(Context cx, ICommentBlock block) => CommentBlockFactory.Instance.CreateEntity(cx, block, block); + public static CommentBlock Create(Context cx, Comments.CommentBlock block) => CommentBlockFactory.Instance.CreateEntity(cx, block, block); - private class CommentBlockFactory : ICachedEntityFactory + private class CommentBlockFactory : CachedEntityFactory { public static CommentBlockFactory Instance { get; } = new CommentBlockFactory(); - public CommentBlock Create(Context cx, ICommentBlock init) => new CommentBlock(cx, init); + public override CommentBlock Create(Context cx, Comments.CommentBlock init) => new CommentBlock(cx, init); } public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NoLabel; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs index 102ea262b39..a1c9fb5c643 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs @@ -1,10 +1,10 @@ -using Semmle.Extraction.CommentProcessing; using Semmle.Extraction.Entities; using System.IO; +using System; namespace Semmle.Extraction.CSharp.Entities { - internal class CommentLine : CachedEntity<(Microsoft.CodeAnalysis.Location, string)>, ICommentLine + internal class CommentLine : CachedEntity<(Microsoft.CodeAnalysis.Location, string)> { private CommentLine(Context cx, Microsoft.CodeAnalysis.Location loc, CommentLineType type, string text, string raw) : base(cx, (loc, text)) @@ -19,7 +19,7 @@ namespace Semmle.Extraction.CSharp.Entities public string Text { get { return Symbol.Item2; } } public string RawText { get; private set; } - private Location location; + private Location? location; public override void Populate(TextWriter trapFile) { @@ -28,7 +28,7 @@ namespace Semmle.Extraction.CSharp.Entities trapFile.commentline_location(this, location); } - public override Microsoft.CodeAnalysis.Location ReportingLocation => location.Symbol; + public override Microsoft.CodeAnalysis.Location? ReportingLocation => location?.Symbol; public override bool NeedsPopulation => true; @@ -44,11 +44,11 @@ namespace Semmle.Extraction.CSharp.Entities return CommentLineFactory.Instance.CreateEntity(cx, init, init); } - private class CommentLineFactory : ICachedEntityFactory<(Microsoft.CodeAnalysis.Location, CommentLineType, string, string), CommentLine> + private class CommentLineFactory : CachedEntityFactory<(Microsoft.CodeAnalysis.Location, CommentLineType, string, string), CommentLine> { public static CommentLineFactory Instance { get; } = new CommentLineFactory(); - public CommentLine Create(Context cx, (Microsoft.CodeAnalysis.Location, CommentLineType, string, string) init) => + public override CommentLine Create(Context cx, (Microsoft.CodeAnalysis.Location, CommentLineType, string, string) init) => new CommentLine(cx, init.Item1, init.Item2, init.Item3, init.Item4); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilations/Compilation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilations/Compilation.cs index 800e35d5ff4..1c929c1ad5a 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilations/Compilation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilations/Compilation.cs @@ -6,7 +6,7 @@ using Semmle.Util; namespace Semmle.Extraction.CSharp.Entities { - public class Compilation : CachedEntity + internal class Compilation : CachedEntity { private static (string Cwd, string[] Args) settings; private static int hashCode; @@ -25,13 +25,15 @@ namespace Semmle.Extraction.CSharp.Entities } } +#nullable disable warnings private Compilation(Context cx) : base(cx, null) { } +#nullable restore warnings public override void Populate(TextWriter trapFile) { - var assembly = Extraction.Entities.Assembly.CreateOutputAssembly(Context); + var assembly = Assembly.CreateOutputAssembly(Context); trapFile.compilations(this, FileUtils.ConvertToUnix(Compilation.Settings.Cwd)); trapFile.compilation_assembly(this, assembly); @@ -45,7 +47,7 @@ namespace Semmle.Extraction.CSharp.Entities // Files index = 0; - foreach (var file in Context.Compilation.SyntaxTrees.Select(tree => Extraction.Entities.File.Create(Context, tree.FilePath))) + foreach (var file in Context.Compilation.SyntaxTrees.Select(tree => File.Create(Context, tree.FilePath))) { trapFile.compilation_compiling_files(this, index++, file); } @@ -54,7 +56,8 @@ namespace Semmle.Extraction.CSharp.Entities index = 0; foreach (var file in Context.Compilation.References .OfType() - .Select(r => Extraction.Entities.File.Create(Context, r.FilePath))) + .Where(r => r.FilePath is not null) + .Select(r => File.Create(Context, r.FilePath!))) { trapFile.compilation_referencing_files(this, index++, file); } @@ -90,11 +93,11 @@ namespace Semmle.Extraction.CSharp.Entities public override bool NeedsPopulation => Context.IsAssemblyScope; - private class CompilationFactory : ICachedEntityFactory + private class CompilationFactory : CachedEntityFactory { public static CompilationFactory Instance { get; } = new CompilationFactory(); - public Compilation Create(Context cx, object init) => new Compilation(cx); + public override Compilation Create(Context cx, object? init) => new Compilation(cx); } private static readonly object compilationCacheKey = new object(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs index d04e80324ff..1424ba31f4d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs @@ -5,10 +5,11 @@ using System.Linq; using Microsoft.CodeAnalysis.CSharp; using Semmle.Extraction.Entities; using System.IO; +using System.Diagnostics.CodeAnalysis; namespace Semmle.Extraction.CSharp.Entities { - public class Constructor : Method + internal class Constructor : Method { private Constructor(Context cx, IMethodSymbol init) : base(cx, init) { } @@ -17,7 +18,7 @@ namespace Semmle.Extraction.CSharp.Entities { PopulateMethod(trapFile); PopulateModifiers(trapFile); - ContainingType.PopulateGenerics(); + ContainingType!.PopulateGenerics(); trapFile.constructors(this, Symbol.ContainingType.Name, ContainingType, (Constructor)OriginalDefinition); trapFile.constructor_location(this, Location); @@ -39,7 +40,7 @@ namespace Semmle.Extraction.CSharp.Entities var syntax = Syntax; var initializer = syntax?.Initializer; - if (initializer == null) + if (initializer is null) return; ITypeSymbol initializerType; @@ -48,7 +49,7 @@ namespace Semmle.Extraction.CSharp.Entities switch (initializer.Kind()) { case SyntaxKind.BaseConstructorInitializer: - initializerType = Symbol.ContainingType.BaseType; + initializerType = Symbol.ContainingType.BaseType!; break; case SyntaxKind.ThisConstructorInitializer: initializerType = Symbol.ContainingType; @@ -69,9 +70,8 @@ namespace Semmle.Extraction.CSharp.Entities var init = new Expression(initInfo); - var target = Constructor.Create(Context, (IMethodSymbol)symbolInfo.Symbol); - - if (target == null) + var target = Constructor.Create(Context, (IMethodSymbol?)symbolInfo.Symbol); + if (target is null) { Context.ModelError(Symbol, "Unable to resolve call"); return; @@ -86,7 +86,7 @@ namespace Semmle.Extraction.CSharp.Entities } } - private ConstructorDeclarationSyntax Syntax + private ConstructorDeclarationSyntax? Syntax { get { @@ -97,9 +97,10 @@ namespace Semmle.Extraction.CSharp.Entities } } - public static new Constructor Create(Context cx, IMethodSymbol constructor) + [return: NotNullIfNotNull("constructor")] + public static new Constructor? Create(Context cx, IMethodSymbol? constructor) { - if (constructor == null) + if (constructor is null) return null; switch (constructor.MethodKind) @@ -116,40 +117,40 @@ namespace Semmle.Extraction.CSharp.Entities { if (Symbol.IsStatic) trapFile.Write("static"); - trapFile.WriteSubId(ContainingType); + trapFile.WriteSubId(ContainingType!); AddParametersToId(Context, trapFile, Symbol); trapFile.Write(";constructor"); } - private ConstructorDeclarationSyntax GetSyntax() => + private ConstructorDeclarationSyntax? GetSyntax() => Symbol.DeclaringSyntaxReferences.Select(r => r.GetSyntax()).OfType().FirstOrDefault(); - public override Microsoft.CodeAnalysis.Location FullLocation => ReportingLocation; + public override Microsoft.CodeAnalysis.Location? FullLocation => ReportingLocation; - public override Microsoft.CodeAnalysis.Location ReportingLocation + public override Microsoft.CodeAnalysis.Location? ReportingLocation { get { var syn = GetSyntax(); - if (syn != null) + if (syn is not null) { return syn.Identifier.GetLocation(); } if (Symbol.IsImplicitlyDeclared) { - return ContainingType.ReportingLocation; + return ContainingType!.ReportingLocation; } return Symbol.ContainingType.Locations.FirstOrDefault(); } } - private class ConstructorFactory : ICachedEntityFactory + private class ConstructorFactory : CachedEntityFactory { public static ConstructorFactory Instance { get; } = new ConstructorFactory(); - public Constructor Create(Context cx, IMethodSymbol init) => new Constructor(cx, init); + public override Constructor Create(Context cx, IMethodSymbol init) => new Constructor(cx, init); } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Conversion.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Conversion.cs index 05a752cb35c..c3d59f94de7 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Conversion.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Conversion.cs @@ -13,7 +13,7 @@ namespace Semmle.Extraction.CSharp.Entities public static new Conversion Create(Context cx, IMethodSymbol symbol) => ConversionFactory.Instance.CreateEntityFromSymbol(cx, symbol); - public override Microsoft.CodeAnalysis.Location ReportingLocation + public override Microsoft.CodeAnalysis.Location? ReportingLocation { get { @@ -26,11 +26,11 @@ namespace Semmle.Extraction.CSharp.Entities } } - private class ConversionFactory : ICachedEntityFactory + private class ConversionFactory : CachedEntityFactory { public static ConversionFactory Instance { get; } = new ConversionFactory(); - public Conversion Create(Context cx, IMethodSymbol init) => new Conversion(cx, init); + public override Conversion Create(Context cx, IMethodSymbol init) => new Conversion(cx, init); } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Destructor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Destructor.cs index 0862baa3600..f569e46acbe 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Destructor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Destructor.cs @@ -12,7 +12,7 @@ namespace Semmle.Extraction.CSharp.Entities { PopulateMethod(trapFile); PopulateModifiers(trapFile); - ContainingType.PopulateGenerics(); + ContainingType!.PopulateGenerics(); trapFile.destructors(this, string.Format("~{0}", Symbol.ContainingType.Name), ContainingType, OriginalDefinition(Context, this, Symbol)); trapFile.destructor_location(this, Location); @@ -20,17 +20,17 @@ namespace Semmle.Extraction.CSharp.Entities private static new Destructor OriginalDefinition(Context cx, Destructor original, IMethodSymbol symbol) { - return symbol.OriginalDefinition == null || SymbolEqualityComparer.Default.Equals(symbol.OriginalDefinition, symbol) ? original : Create(cx, symbol.OriginalDefinition); + return symbol.OriginalDefinition is null || SymbolEqualityComparer.Default.Equals(symbol.OriginalDefinition, symbol) ? original : Create(cx, symbol.OriginalDefinition); } public static new Destructor Create(Context cx, IMethodSymbol symbol) => DestructorFactory.Instance.CreateEntityFromSymbol(cx, symbol); - private class DestructorFactory : ICachedEntityFactory + private class DestructorFactory : CachedEntityFactory { public static DestructorFactory Instance { get; } = new DestructorFactory(); - public Destructor Create(Context cx, IMethodSymbol init) => new Destructor(cx, init); + public override Destructor Create(Context cx, IMethodSymbol init) => new Destructor(cx, init); } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs index fe095ed4462..e92a42d8ab1 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs @@ -12,7 +12,7 @@ namespace Semmle.Extraction.CSharp.Entities public override void WriteId(TextWriter trapFile) { - trapFile.WriteSubId(ContainingType); + trapFile.WriteSubId(ContainingType!); trapFile.Write('.'); Method.AddExplicitInterfaceQualifierToId(Context, trapFile, Symbol.ExplicitInterfaceImplementations); trapFile.Write(Symbol.Name); @@ -24,7 +24,7 @@ namespace Semmle.Extraction.CSharp.Entities PopulateNullability(trapFile, Symbol.GetAnnotatedType()); var type = Type.Create(Context, Symbol.Type); - trapFile.events(this, Symbol.GetName(), ContainingType, type.TypeRef, Create(Context, Symbol.OriginalDefinition)); + trapFile.events(this, Symbol.GetName(), ContainingType!, type.TypeRef, Create(Context, Symbol.OriginalDefinition)); var adder = Symbol.AddMethod; var remover = Symbol.RemoveMethod; @@ -47,7 +47,7 @@ namespace Semmle.Extraction.CSharp.Entities trapFile.explicitly_implements(this, explicitInterface.TypeRef); foreach (var syntax in declSyntaxReferences.OfType()) - TypeMention.Create(Context, syntax.ExplicitInterfaceSpecifier.Name, this, explicitInterface); + TypeMention.Create(Context, syntax.ExplicitInterfaceSpecifier!.Name, this, explicitInterface); } foreach (var l in Locations) @@ -65,11 +65,11 @@ namespace Semmle.Extraction.CSharp.Entities public static Event Create(Context cx, IEventSymbol symbol) => EventFactory.Instance.CreateEntityFromSymbol(cx, symbol); - private class EventFactory : ICachedEntityFactory + private class EventFactory : CachedEntityFactory { public static EventFactory Instance { get; } = new EventFactory(); - public Event Create(Context cx, IEventSymbol init) => new Event(cx, init); + public override Event Create(Context cx, IEventSymbol init) => new Event(cx, init); } public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NoLabel; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs index 82be6a6e406..72346c3f808 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs @@ -11,15 +11,15 @@ namespace Semmle.Extraction.CSharp.Entities /// /// Gets the event symbol associated with this accessor. /// - private IEventSymbol EventSymbol => Symbol.AssociatedSymbol as IEventSymbol; + private IEventSymbol? EventSymbol => Symbol.AssociatedSymbol as IEventSymbol; public override void Populate(TextWriter trapFile) { PopulateMethod(trapFile); - ContainingType.PopulateGenerics(); + ContainingType!.PopulateGenerics(); var @event = EventSymbol; - if (@event == null) + if (@event is null) { Context.ModelError(Symbol, "Unhandled event accessor associated symbol"); return; @@ -31,12 +31,12 @@ namespace Semmle.Extraction.CSharp.Entities if (SymbolEqualityComparer.Default.Equals(Symbol, @event.AddMethod)) { kind = 1; - unboundAccessor = Create(Context, @event.OriginalDefinition.AddMethod); + unboundAccessor = Create(Context, @event.OriginalDefinition.AddMethod!); } else if (SymbolEqualityComparer.Default.Equals(Symbol, @event.RemoveMethod)) { kind = 2; - unboundAccessor = Create(Context, @event.OriginalDefinition.RemoveMethod); + unboundAccessor = Create(Context, @event.OriginalDefinition.RemoveMethod!); } else { @@ -55,11 +55,11 @@ namespace Semmle.Extraction.CSharp.Entities public static new EventAccessor Create(Context cx, IMethodSymbol symbol) => EventAccessorFactory.Instance.CreateEntityFromSymbol(cx, symbol); - private class EventAccessorFactory : ICachedEntityFactory + private class EventAccessorFactory : CachedEntityFactory { public static EventAccessorFactory Instance { get; } = new EventAccessorFactory(); - public EventAccessor Create(Context cx, IMethodSymbol init) => new EventAccessor(cx, init); + public override EventAccessor Create(Context cx, IMethodSymbol init) => new EventAccessor(cx, init); } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs index fe62bf8323b..cdf17311440 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs @@ -12,11 +12,11 @@ namespace Semmle.Extraction.CSharp.Entities internal class Expression : FreshEntity, IExpressionParentEntity { private readonly IExpressionInfo info; - public AnnotatedTypeSymbol? Type { get; } + public AnnotatedTypeSymbol? Type { get; private set; } public Extraction.Entities.Location Location { get; } public ExprKind Kind { get; } - internal Expression(IExpressionInfo info) + internal Expression(IExpressionInfo info, bool shouldPopulate = true) : base(info.Context) { this.info = info; @@ -24,7 +24,10 @@ namespace Semmle.Extraction.CSharp.Entities Kind = info.Kind; Type = info.Type; - TryPopulate(); + if (shouldPopulate) + { + TryPopulate(); + } } protected sealed override void Populate(TextWriter trapFile) @@ -57,7 +60,15 @@ namespace Semmle.Extraction.CSharp.Entities type.PopulateGenerics(); } - public override Microsoft.CodeAnalysis.Location ReportingLocation => Location.Symbol; + public override Location? ReportingLocation => Location.Symbol; + + internal void SetType(ITypeSymbol? type) + { + if (type is not null) + { + Type = new AnnotatedTypeSymbol(type, type.NullableAnnotation); + } + } bool IExpressionParentEntity.IsTopLevelParent => false; @@ -66,15 +77,15 @@ namespace Semmle.Extraction.CSharp.Entities /// /// The value. /// The string representation. - public static string ValueAsString(object value) + public static string ValueAsString(object? value) { - return value == null + return value is null ? "null" : value is bool b ? b ? "true" : "false" - : value.ToString(); + : value.ToString()!; } /// @@ -118,10 +129,11 @@ namespace Semmle.Extraction.CSharp.Entities /// /// Creates a generated expression from a typed constant. /// - public static Expression CreateGenerated(Context cx, TypedConstant constant, IExpressionParentEntity parent, - int childIndex, Semmle.Extraction.Entities.Location location) + public static Expression? CreateGenerated(Context cx, TypedConstant constant, IExpressionParentEntity parent, + int childIndex, Extraction.Entities.Location location) { - if (constant.IsNull) + if (constant.IsNull || + constant.Type is null) { return Literal.CreateGeneratedNullLiteral(cx, parent, childIndex, location); } @@ -132,11 +144,11 @@ namespace Semmle.Extraction.CSharp.Entities return Literal.CreateGenerated(cx, parent, childIndex, constant.Type, constant.Value, location); case TypedConstantKind.Enum: // Enum value is generated in the following format: (Enum)value - Action createChild = (parent, index) => Literal.CreateGenerated(cx, parent, index, ((INamedTypeSymbol)constant.Type).EnumUnderlyingType, constant.Value, location); - var cast = Cast.CreateGenerated(cx, parent, childIndex, constant.Type, constant.Value, createChild, location); + Action createChild = (parent, index) => Literal.CreateGenerated(cx, parent, index, ((INamedTypeSymbol)constant.Type).EnumUnderlyingType!, constant.Value, location); + var cast = Cast.CreateGenerated(cx, parent, childIndex, constant.Type!, constant.Value, createChild, location); return cast; case TypedConstantKind.Type: - var type = ((ITypeSymbol)constant.Value).OriginalDefinition; + var type = ((ITypeSymbol)constant.Value!).OriginalDefinition; return TypeOf.CreateGenerated(cx, parent, childIndex, type, location); case TypedConstantKind.Array: // Single dimensional arrays are in the following format: @@ -145,6 +157,7 @@ namespace Semmle.Extraction.CSharp.Entities // // itemI is generated recursively. return NormalArrayCreation.CreateGenerated(cx, parent, childIndex, constant.Type, constant.Values, location); + case TypedConstantKind.Error: default: cx.ExtractionError("Couldn't extract constant in attribute", constant.ToString(), location); return null; @@ -213,7 +226,7 @@ namespace Semmle.Extraction.CSharp.Entities switch (method.MethodKind) { case MethodKind.BuiltinOperator: - if (method.ContainingType != null && method.ContainingType.TypeKind == Microsoft.CodeAnalysis.TypeKind.Delegate) + if (method.ContainingType is not null && method.ContainingType.TypeKind == Microsoft.CodeAnalysis.TypeKind.Delegate) return CallType.UserOperator; return CallType.BuiltInOperator; case MethodKind.Constructor: @@ -232,7 +245,7 @@ namespace Semmle.Extraction.CSharp.Entities public static bool IsDynamic(Context cx, ExpressionSyntax node) { var ti = cx.GetTypeInfo(node).ConvertedType; - return ti != null && ti.TypeKind == Microsoft.CodeAnalysis.TypeKind.Dynamic; + return ti is not null && ti.TypeKind == Microsoft.CodeAnalysis.TypeKind.Dynamic; } /// @@ -242,7 +255,7 @@ namespace Semmle.Extraction.CSharp.Entities /// The qualifier of the conditional access. protected static ExpressionSyntax FindConditionalQualifier(ExpressionSyntax node) { - for (SyntaxNode n = node; n != null; n = n.Parent) + for (SyntaxNode? n = node; n is not null; n = n.Parent) { if (n.Parent is ConditionalAccessExpressionSyntax conditionalAccess && conditionalAccess.WhenNotNull == n) @@ -288,7 +301,7 @@ namespace Semmle.Extraction.CSharp.Entities } trapFile.expr_argument(expr, mode); - if (arg.NameColon != null) + if (arg.NameColon is not null) { trapFile.expr_argument_name(expr, arg.NameColon.Name.Identifier.Text); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/ExpressionInfo.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/ExpressionInfo.cs index feb1eaa2d93..3474ad4a818 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/ExpressionInfo.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/ExpressionInfo.cs @@ -15,10 +15,10 @@ namespace Semmle.Extraction.CSharp.Entities public IExpressionParentEntity Parent { get; } public int Child { get; } public bool IsCompilerGenerated { get; } - public string ExprValue { get; } + public string? ExprValue { get; } public ExpressionInfo(Context cx, AnnotatedTypeSymbol? type, Extraction.Entities.Location location, ExprKind kind, - IExpressionParentEntity parent, int child, bool isCompilerGenerated, string value) + IExpressionParentEntity parent, int child, bool isCompilerGenerated, string? value) { Context = cx; Type = type; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/ExpressionNodeInfo.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/ExpressionNodeInfo.cs index 6968ca65abb..5f417d4b4b9 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/ExpressionNodeInfo.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/ExpressionNodeInfo.cs @@ -48,7 +48,7 @@ namespace Semmle.Extraction.CSharp.Entities var type = ResolvedType; - if (type.Symbol == null) + if (type.Symbol is null) type.Symbol = (TypeInfo.Type ?? TypeInfo.ConvertedType).DisambiguateType(); // Roslyn workaround: It can't work out the type of "new object[0]" @@ -59,7 +59,7 @@ namespace Semmle.Extraction.CSharp.Entities { var elementType = Context.GetType(arrayCreation.Type.ElementType); - if (elementType.Symbol != null) + if (elementType.Symbol is not null) // There seems to be no way to create an array with a nullable element at present. return new AnnotatedTypeSymbol(Context.Compilation.CreateArrayTypeSymbol(elementType.Symbol, arrayCreation.Type.RankSpecifiers.Count), NullableAnnotation.NotAnnotated); } @@ -74,13 +74,13 @@ namespace Semmle.Extraction.CSharp.Entities } } - private Microsoft.CodeAnalysis.Location location; + private Microsoft.CodeAnalysis.Location? location; public Microsoft.CodeAnalysis.Location CodeAnalysisLocation { get { - if (location == null) + if (location is null) location = Node.FixedLocation(); return location; } @@ -92,7 +92,7 @@ namespace Semmle.Extraction.CSharp.Entities public SemanticModel Model => Context.GetModel(Node); - public string ExprValue + public string? ExprValue { get { @@ -111,13 +111,13 @@ namespace Semmle.Extraction.CSharp.Entities } } - private Extraction.Entities.Location cachedLocation; + private Extraction.Entities.Location? cachedLocation; public Extraction.Entities.Location Location { get { - if (cachedLocation == null) + if (cachedLocation is null) cachedLocation = Context.CreateLocation(CodeAnalysisLocation); return cachedLocation; } @@ -164,7 +164,7 @@ namespace Semmle.Extraction.CSharp.Entities { get { - if (cachedSymbolInfo.Symbol == null && cachedSymbolInfo.CandidateReason == CandidateReason.None) + if (cachedSymbolInfo.Symbol is null && cachedSymbolInfo.CandidateReason == CandidateReason.None) cachedSymbolInfo = Model.GetSymbolInfo(Node); return cachedSymbolInfo; } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArrayCreation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArrayCreation.cs index 3d6d3b95722..c7860d2ad93 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArrayCreation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArrayCreation.cs @@ -21,9 +21,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { protected ExplicitArrayCreation(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.ARRAY_CREATION)) { } - protected abstract ArrayTypeSyntax TypeSyntax { get; } + protected abstract ArrayTypeSyntax? TypeSyntax { get; } - public abstract InitializerExpressionSyntax Initializer { get; } + public abstract InitializerExpressionSyntax? Initializer { get; } protected override void PopulateExpression(TextWriter trapFile) { @@ -32,6 +32,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions if (TypeSyntax is null) { Context.ModelError(Syntax, "Array has unexpected type syntax"); + return; } var firstLevelSizes = TypeSyntax.RankSpecifiers.First()?.Sizes ?? SyntaxFactory.SeparatedList(); @@ -60,7 +61,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions TypeMention.Create(Context, TypeSyntax, this, Type); } - private void SetArraySizes(InitializerExpressionSyntax initializer, int rank) + private void SetArraySizes(InitializerExpressionSyntax? initializer, int rank) { for (var level = 0; level < rank; level++) { @@ -82,7 +83,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions protected override ArrayTypeSyntax TypeSyntax => Syntax.Type; - public override InitializerExpressionSyntax Initializer => Syntax.Initializer; + public override InitializerExpressionSyntax? Initializer => Syntax.Initializer; public static Expression Create(ExpressionNodeInfo info) => new NormalArrayCreation(info).TryPopulate(); @@ -122,9 +123,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { private StackAllocArrayCreation(ExpressionNodeInfo info) : base(info) { } - protected override ArrayTypeSyntax TypeSyntax => Syntax.Type as ArrayTypeSyntax; + protected override ArrayTypeSyntax? TypeSyntax => Syntax.Type as ArrayTypeSyntax; - public override InitializerExpressionSyntax Initializer => Syntax.Initializer; + public override InitializerExpressionSyntax? Initializer => Syntax.Initializer; protected override void PopulateExpression(TextWriter trapFile) { @@ -157,7 +158,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions protected override void PopulateExpression(TextWriter trapFile) { - if (Syntax.Initializer != null) + if (Syntax.Initializer is not null) { ArrayInitializer.Create(new ExpressionNodeInfo(Context, Syntax.Initializer, this, InitializerIndex)); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Assignment.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Assignment.cs index 3325c8075a3..553d6d8349a 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Assignment.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Assignment.cs @@ -84,7 +84,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions var kind = GetAssignmentOperation(cx, syntax); var leftType = cx.GetType(syntax.Left); - if (leftType.Symbol != null && leftType.Symbol.SpecialType != SpecialType.None) + if (leftType.Symbol is not null && leftType.Symbol.SpecialType != SpecialType.None) { // In Mono, the builtin types did not specify their operator invocation // even though EVERY operator has an invocation in C#. (This is a flaw in the dbscheme and should be fixed). diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs index 74dcc9ff85f..5153c856a25 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs @@ -23,12 +23,13 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions private static ExprKind GetKind(Context cx, BinaryExpressionSyntax node) { - var k = GetBinaryTokenKind(cx, node.OperatorToken.Kind()); + var k = GetBinaryTokenKind(cx, node); return GetCallType(cx, node).AdjustKind(k); } - private static ExprKind GetBinaryTokenKind(Context cx, SyntaxKind kind) + private static ExprKind GetBinaryTokenKind(Context cx, BinaryExpressionSyntax node) { + var kind = node.OperatorToken.Kind(); switch (kind) { case SyntaxKind.LessThanToken: return ExprKind.LT; @@ -54,7 +55,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions case SyntaxKind.QuestionQuestionToken: return ExprKind.NULL_COALESCING; // !! And the rest default: - cx.ModelError($"Unhandled operator type {kind}"); + cx.ModelError(node, $"Unhandled operator type {kind}"); return ExprKind.UNKNOWN; } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Cast.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Cast.cs index 2afe7a9b37e..c747deceaf0 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Cast.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Cast.cs @@ -32,7 +32,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions public override Microsoft.CodeAnalysis.Location ReportingLocation => Syntax.GetLocation(); - public static Expression CreateGenerated(Context cx, IExpressionParentEntity parent, int childIndex, Microsoft.CodeAnalysis.ITypeSymbol type, object value, Action createChild, Extraction.Entities.Location location) + public static Expression CreateGenerated(Context cx, IExpressionParentEntity parent, int childIndex, Microsoft.CodeAnalysis.ITypeSymbol type, object? value, Action createChild, Extraction.Entities.Location location) { var info = new ExpressionInfo( cx, diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ElementAccess.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ElementAccess.cs index e860731ece3..9e7786a385d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ElementAccess.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ElementAccess.cs @@ -42,14 +42,14 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions } } - public sealed override Microsoft.CodeAnalysis.Location ReportingLocation => base.ReportingLocation; + public sealed override Microsoft.CodeAnalysis.Location? ReportingLocation => base.ReportingLocation; private static ExprKind GetKind(Context cx, ExpressionSyntax qualifier) { var qualifierType = cx.GetType(qualifier); // This is a compilation error, so make a guess and continue. - if (qualifierType.Symbol == null) + if (qualifierType.Symbol is null) return ExprKind.ARRAY_ACCESS; if (qualifierType.Symbol.TypeKind == Microsoft.CodeAnalysis.TypeKind.Pointer) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Factory.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Factory.cs index 48cd73944f3..63389822cad 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Factory.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Factory.cs @@ -12,7 +12,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions // to the extent that the stack has been known to overflow. using (info.Context.StackGuard) { - if (info.Node == null) + if (info.Node is null) { info.Context.ModelError("Attempt to create a null expression"); return new Unknown(info); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs index 96d8dec039a..2d617cdb1b9 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs @@ -3,7 +3,7 @@ using Semmle.Extraction.Kinds; namespace Semmle.Extraction.CSharp.Entities.Expressions { - internal class ImplicitCast : Expression + internal sealed class ImplicitCast : Expression { public Expression Expr { @@ -23,7 +23,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions Expr = Factory.Create(info.SetParent(this, 0)); var target = Method.Create(Context, method); - if (target != null) + if (target is not null) Context.TrapWriter.Writer.expr_call(this, target); else Context.ModelError(info.Node, "Failed to resolve target for operator invocation"); @@ -44,7 +44,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions var convertedType = info.ConvertedType; var conversion = info.Conversion; - if (conversion.MethodSymbol != null) + if (conversion.MethodSymbol is not null) { var convertedToDelegate = Entities.Type.IsDelegate(convertedType.Symbol); @@ -65,15 +65,15 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions return Factory.Create(info); } - if (resolvedType.Symbol != null) + if (resolvedType.Symbol is not null) return new ImplicitCast(info, conversion.MethodSymbol); } var implicitUpcast = conversion.IsImplicit && - convertedType.Symbol != null && + convertedType.Symbol is not null && !conversion.IsBoxing && ( - resolvedType.Symbol == null || + resolvedType.Symbol is null || conversion.IsReference || convertedType.Symbol.SpecialType == SpecialType.System_Object) ; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Initializer.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Initializer.cs index 6f53f1f144e..29afbce9016 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Initializer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Initializer.cs @@ -139,7 +139,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions var invocation = new Expression(new ExpressionInfo(Context, voidType, Context.CreateLocation(i.GetLocation()), ExprKind.METHOD_INVOCATION, this, child++, false, null)); - if (addMethod != null) + if (addMethod is not null) trapFile.expr_call(invocation, addMethod); else Context.ModelError(Syntax, "Unable to find an Add() method for collection initializer"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs index 408556dff63..a50b68f75b7 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs @@ -29,7 +29,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions } var child = -1; - string memberName = null; + string? memberName = null; var target = TargetSymbol; switch (Syntax.Expression) { @@ -51,14 +51,18 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions case SimpleNameSyntax simpleName when (Kind == ExprKind.METHOD_INVOCATION): // Unqualified method call; `M()` memberName = simpleName.Identifier.Text; - if (target != null && !target.IsStatic) + if (target is not null && !target.IsStatic) { // Implicit `this` qualifier; add explicitly - - if (Context.GetModel(Syntax).GetEnclosingSymbol(Location.Symbol.SourceSpan.Start) is IMethodSymbol callingMethod) + if (Location.Symbol is not null && + Context.GetModel(Syntax).GetEnclosingSymbol(Location.Symbol.SourceSpan.Start) is IMethodSymbol callingMethod) + { This.CreateImplicit(Context, callingMethod.ContainingType, Location, this, child++); + } else + { Context.ModelError(Syntax, "Couldn't determine implicit this type"); + } } else { @@ -75,7 +79,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions var isDynamicCall = IsDynamicCall(info); if (isDynamicCall) { - if (memberName != null) + if (memberName is not null) trapFile.dynamic_member_name(this, memberName); else Context.ModelError(Syntax, "Unable to get name for dynamic call."); @@ -83,7 +87,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions PopulateArguments(trapFile, Syntax.ArgumentList, child); - if (target == null) + if (target is null) { if (!isDynamicCall && !IsDelegateLikeCall(info)) Context.ModelError(Syntax, "Unable to resolve target for call. (Compilation error?)"); @@ -105,13 +109,13 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions public SymbolInfo SymbolInfo => info.SymbolInfo; - public IMethodSymbol TargetSymbol + public IMethodSymbol? TargetSymbol { get { var si = SymbolInfo; - if (si.Symbol != null) + if (si.Symbol is not null) return si.Symbol as IMethodSymbol; if (si.CandidateReason == CandidateReason.OverloadResolutionFailure) @@ -144,7 +148,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions return IsDelegateLikeCall(info, IsDelegateInvoke); } - private static bool IsDelegateLikeCall(ExpressionNodeInfo info, Func check) + private static bool IsDelegateLikeCall(ExpressionNodeInfo info, Func check) { var si = info.SymbolInfo; @@ -159,7 +163,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions if (si.CandidateReason == CandidateReason.LateBound && node.Expression is IdentifierNameSyntax && IsDynamic(info.Context, node.Expression) && - si.Symbol == null) + si.Symbol is null) { return true; } @@ -167,15 +171,15 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions return check(si.Symbol); } - private static bool IsFunctionPointer(ISymbol symbol) + private static bool IsFunctionPointer(ISymbol? symbol) { - return symbol != null && + return symbol is not null && symbol.Kind == SymbolKind.FunctionPointerType; } - private static bool IsDelegateInvoke(ISymbol symbol) + private static bool IsDelegateInvoke(ISymbol? symbol) { - return symbol != null && + return symbol is not null && symbol.Kind == SymbolKind.Method && ((IMethodSymbol)symbol).MethodKind == MethodKind.DelegateInvoke; } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Lambda.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Lambda.cs index 2cd23101b1c..532c4a7abf1 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Lambda.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Lambda.cs @@ -17,7 +17,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions private void VisitParameter(ParameterSyntax p) { - var symbol = Context.GetModel(p).GetDeclaredSymbol(p); + var symbol = Context.GetModel(p).GetDeclaredSymbol(p)!; Parameter.Create(Context, symbol, this); } @@ -59,7 +59,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions public static Lambda Create(ExpressionNodeInfo info, SimpleLambdaExpressionSyntax node) => new Lambda(info, node); private Lambda(ExpressionNodeInfo info, AnonymousMethodExpressionSyntax node) : - this(info.SetKind(ExprKind.ANONYMOUS_METHOD), node.Body, node.ParameterList == null ? Enumerable.Empty() : node.ParameterList.Parameters) + this(info.SetKind(ExprKind.ANONYMOUS_METHOD), node.Body, node.ParameterList is null ? Enumerable.Empty() : node.ParameterList.Parameters) { } public static Lambda Create(ExpressionNodeInfo info, AnonymousMethodExpressionSyntax node) => new Lambda(info, node); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Literal.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Literal.cs index 51acebef5c8..bac257e9e34 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Literal.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Literal.cs @@ -35,7 +35,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions return GetExprKind(type, info.Node, info.Context); } - private static ExprKind GetExprKind(ITypeSymbol type, ExpressionSyntax expr, Context context) + private static ExprKind GetExprKind(ITypeSymbol? type, ExpressionSyntax? expr, Context context) { switch (type?.SpecialType) { @@ -75,7 +75,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions case null: default: - if (expr is object) + if (expr is not null) context.ModelError(expr, "Unhandled literal type"); else context.ModelError("Unhandled literal type"); @@ -83,7 +83,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions } } - public static Expression CreateGenerated(Context cx, IExpressionParentEntity parent, int childIndex, ITypeSymbol type, object value, + public static Expression CreateGenerated(Context cx, IExpressionParentEntity parent, int childIndex, ITypeSymbol type, object? value, Extraction.Entities.Location location) { var info = new ExpressionInfo( diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/MemberAccess.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/MemberAccess.cs index fa9d3c9a6dd..426145e2d27 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/MemberAccess.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/MemberAccess.cs @@ -4,14 +4,14 @@ using Semmle.Extraction.Kinds; namespace Semmle.Extraction.CSharp.Entities.Expressions { - internal class MemberAccess : Expression + internal sealed class MemberAccess : Expression { - private MemberAccess(ExpressionNodeInfo info, ExpressionSyntax qualifier, ISymbol target) : base(info) + private MemberAccess(ExpressionNodeInfo info, ExpressionSyntax qualifier, ISymbol? target) : base(info) { var trapFile = info.Context.TrapWriter.Writer; Qualifier = Create(Context, qualifier, this, -1); - if (target == null) + if (target is null) { if (info.Kind != ExprKind.DYNAMIC_MEMBER_ACCESS) Context.ModelError(info.Node, "Could not determine target for member access"); @@ -59,14 +59,14 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions var symbol = target.Symbol ?? info.Context.GetSymbolInfo(name).Symbol; - if (symbol == null && target.CandidateSymbols.Length >= 1) + if (symbol is null && target.CandidateSymbols.Length >= 1) { // Pick the first symbol. This could occur for something like `nameof(Foo.Bar)` // where `Bar` is a method group. Technically, we don't know which symbol is accessed. symbol = target.CandidateSymbols[0]; } - if (symbol == null) + if (symbol is null) { info.Context.ModelError(info.Node, "Failed to determine symbol for member access"); // Default to property access - this can still give useful results but diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Name.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Name.cs index faf5c9e2bd1..f6e861ed923 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Name.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Name.cs @@ -13,19 +13,26 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions var target = symbolInfo.Symbol; - if (target == null && symbolInfo.CandidateReason == CandidateReason.OverloadResolutionFailure) + if (target is null && + symbolInfo.CandidateReason == CandidateReason.OverloadResolutionFailure && + info.Node.Parent.IsKind(SyntaxKind.SuppressNullableWarningExpression)) { - // The expression is probably a cast - target = info.Context.GetSymbolInfo((CSharpSyntaxNode)info.Node.Parent).Symbol; + target = symbolInfo.CandidateSymbols.FirstOrDefault(); } - if (target == null && (symbolInfo.CandidateReason == CandidateReason.Ambiguous || symbolInfo.CandidateReason == CandidateReason.MemberGroup)) + if (target is null && symbolInfo.CandidateReason == CandidateReason.OverloadResolutionFailure) + { + // The expression is probably a cast + target = info.Context.GetSymbolInfo((CSharpSyntaxNode)info.Node.Parent!).Symbol; + } + + if (target is null && (symbolInfo.CandidateReason == CandidateReason.Ambiguous || symbolInfo.CandidateReason == CandidateReason.MemberGroup)) { // Pick one at random - they probably resolve to the same ID target = symbolInfo.CandidateSymbols.First(); } - if (target == null) + if (target is null) { if (IsInsideIfDirective(info.Node)) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ObjectCreation/AnonymousObjectCreation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ObjectCreation/AnonymousObjectCreation.cs index df291c4e9fd..38b5a17e264 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ObjectCreation/AnonymousObjectCreation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ObjectCreation/AnonymousObjectCreation.cs @@ -18,22 +18,25 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions protected override void PopulateExpression(TextWriter trapFile) { var target = Context.GetSymbolInfo(Syntax); - var method = (IMethodSymbol)target.Symbol; - - if (method != null) + var method = (IMethodSymbol?)target.Symbol; + if (method is not null) { trapFile.expr_call(this, Method.Create(Context, method)); } + var child = 0; - var objectInitializer = Syntax.Initializers.Any() ? - new Expression(new ExpressionInfo(Context, Type, Location, ExprKind.OBJECT_INIT, this, -1, false, null)) : - null; + if (!Syntax.Initializers.Any()) + { + return; + } + + var objectInitializer = new Expression(new ExpressionInfo(Context, Type, Location, ExprKind.OBJECT_INIT, this, -1, false, null)); foreach (var init in Syntax.Initializers) { // Create an "assignment" - var property = Context.GetModel(init).GetDeclaredSymbol(init); + var property = Context.GetModel(init).GetDeclaredSymbol(init)!; var propEntity = Property.Create(Context, property); var type = property.GetAnnotatedType(); var loc = Context.CreateLocation(init.GetLocation()); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ObjectCreation/BaseObjectCreation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ObjectCreation/BaseObjectCreation.cs index 6f2c38b63db..ffe27923793 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ObjectCreation/BaseObjectCreation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ObjectCreation/BaseObjectCreation.cs @@ -17,7 +17,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions protected override void PopulateExpression(TextWriter trapFile) { - if (Syntax.ArgumentList != null) + if (Syntax.ArgumentList is not null) { PopulateArguments(trapFile, Syntax.ArgumentList, 0); } @@ -41,7 +41,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions } } - if (Syntax.Initializer != null) + if (Syntax.Initializer is not null) { switch (Syntax.Initializer.Kind()) { @@ -52,7 +52,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions ObjectInitializer.Create(new ExpressionNodeInfo(Context, Syntax.Initializer, this, -1).SetType(Type)); break; default: - Context.ModelError("Unhandled initializer in object creation"); + Context.ModelError(Syntax.Initializer, "Unhandled initializer in object creation"); break; } } @@ -68,7 +68,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions private static bool IsDynamicObjectCreation(Context cx, BaseObjectCreationExpressionSyntax node) { - return node.ArgumentList != null && + return node.ArgumentList is not null && node.ArgumentList.Arguments.Any(arg => IsDynamic(cx, arg.Expression)); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/PropertyPattern.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/PropertyPattern.cs index 7bd13e7a8a3..e12073ac6b2 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/PropertyPattern.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/PropertyPattern.cs @@ -14,6 +14,11 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions foreach (var sub in pp.Subpatterns) { var p = Expressions.Pattern.Create(cx, sub.Pattern, this, child++); + if (sub.NameColon is null) + { + Context.ModelError(sub, "Expected to find 'Name:' in pattern."); + continue; + } trapFile.exprorstmt_name(p, sub.NameColon.Name.ToString()); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/RelationalPattern.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/RelationalPattern.cs index c1a140005dc..471ab5ca48f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/RelationalPattern.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Patterns/RelationalPattern.cs @@ -22,7 +22,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions SyntaxKind.GreaterThanEqualsToken => ExprKind.GE_PATTERN, SyntaxKind.LessThanToken => ExprKind.LT_PATTERN, SyntaxKind.GreaterThanToken => ExprKind.GT_PATTERN, - _ => throw new InternalError(operatorToken.Parent, $"Relation pattern with operator token '{operatorToken.Kind()}' is not supported."), + _ => throw new InternalError(operatorToken.Parent!, $"Relation pattern with operator token '{operatorToken.Kind()}' is not supported."), }; } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Query.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Query.cs index f919aaca568..026119efeab 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Query.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Query.cs @@ -21,12 +21,12 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions /// private class QueryCall : Expression { - public QueryCall(Context cx, IMethodSymbol method, SyntaxNode clause, IExpressionParentEntity parent, int child) + public QueryCall(Context cx, IMethodSymbol? method, SyntaxNode clause, IExpressionParentEntity parent, int child) : base(new ExpressionInfo(cx, method?.GetAnnotatedReturnType(), cx.CreateLocation(clause.GetLocation()), ExprKind.METHOD_INVOCATION, parent, child, false, null)) { - if (method != null) + if (method is not null) cx.TrapWriter.Writer.expr_call(this, Method.Create(cx, method)); } } @@ -36,11 +36,11 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions /// private abstract class Clause { - protected readonly IMethodSymbol method; + protected readonly IMethodSymbol? method; protected readonly List arguments = new List(); protected readonly SyntaxNode node; - protected Clause(IMethodSymbol method, SyntaxNode node) + protected Clause(IMethodSymbol? method, SyntaxNode node) { this.method = method; this.node = node; @@ -48,15 +48,15 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions public ExpressionSyntax Expr => arguments.First(); - public CallClause WithCallClause(IMethodSymbol newMethod, SyntaxNode newNode) => + public CallClause WithCallClause(IMethodSymbol? newMethod, SyntaxNode newNode) => new CallClause(this, newMethod, newNode); - public LetClause WithLetClause(IMethodSymbol newMethod, SyntaxNode newNode, ISymbol newDeclaration, SyntaxToken newName) => + public LetClause WithLetClause(IMethodSymbol? newMethod, SyntaxNode newNode, ISymbol newDeclaration, SyntaxToken newName) => new LetClause(this, newMethod, newNode, newDeclaration, newName); public Clause AddArgument(ExpressionSyntax arg) { - if (arg != null) + if (arg is not null) arguments.Add(arg); return this; } @@ -66,10 +66,10 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions var type = cx.GetType(Expr); AnnotatedTypeSymbol? declType; - TypeSyntax declTypeSyntax = null; + TypeSyntax? declTypeSyntax = null; if (getElement) { - if (node is FromClauseSyntax from && from.Type != null) + if (node is FromClauseSyntax from && from.Type is not null) { declTypeSyntax = from.Type; declType = cx.GetType(from.Type); @@ -116,7 +116,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions private static AnnotatedTypeSymbol? GetEnumerableElementType(Context cx, INamedTypeSymbol type) { var et = GetEnumerableType(cx, type); - if (et != null) + if (et is not null) return et; return type.AllInterfaces @@ -126,7 +126,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions .FirstOrDefault(); } - private static AnnotatedTypeSymbol? GetElementType(Context cx, ITypeSymbol symbol) => + private static AnnotatedTypeSymbol? GetElementType(Context cx, ITypeSymbol? symbol) => symbol switch { IArrayTypeSymbol a => a.GetAnnotatedElementType(), @@ -150,7 +150,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions private readonly ISymbol declaration; private readonly SyntaxToken name; - public RangeClause(IMethodSymbol method, SyntaxNode node, ISymbol declaration, SyntaxToken name) : base(method, node) + public RangeClause(IMethodSymbol? method, SyntaxNode node, ISymbol declaration, SyntaxToken name) : base(method, node) { this.declaration = declaration; this.name = name; @@ -165,9 +165,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions private readonly Clause operand; private readonly ISymbol declaration; private readonly SyntaxToken name; - private ISymbol intoDeclaration; + private ISymbol? intoDeclaration; - public LetClause(Clause operand, IMethodSymbol method, SyntaxNode node, ISymbol declaration, SyntaxToken name) : base(method, node) + public LetClause(Clause operand, IMethodSymbol? method, SyntaxNode node, ISymbol declaration, SyntaxToken name) : base(method, node) { this.operand = operand; this.declaration = declaration; @@ -182,13 +182,13 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions private void DeclareIntoVariable(Context cx, IExpressionParentEntity parent, int intoChild, bool getElement) { - if (intoDeclaration != null) + if (intoDeclaration is not null) DeclareRangeVariable(cx, parent, intoChild, getElement, intoDeclaration, name); } public override Expression Populate(Context cx, IExpressionParentEntity parent, int child) { - if (method == null) + if (method is null) cx.ModelError(node, "Unable to determine target of query expression"); var callExpr = new QueryCall(cx, method, node, parent, child); @@ -204,7 +204,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { private readonly Clause operand; - public CallClause(Clause operand, IMethodSymbol method, SyntaxNode node) : base(method, node) + public CallClause(Clause operand, IMethodSymbol? method, SyntaxNode node) : base(method, node) { this.operand = operand; } @@ -230,7 +230,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions var info = cx.GetModel(node).GetQueryClauseInfo(node.FromClause); var method = info.OperationInfo.Symbol as IMethodSymbol; - var clauseExpr = new RangeClause(method, node.FromClause, cx.GetModel(node).GetDeclaredSymbol(node.FromClause), node.FromClause.Identifier).AddArgument(node.FromClause.Expression); + var clauseExpr = new RangeClause(method, node.FromClause, cx.GetModel(node).GetDeclaredSymbol(node.FromClause)!, node.FromClause.Identifier).AddArgument(node.FromClause.Expression); foreach (var qc in node.Body.Clauses) { @@ -248,7 +248,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions clauseExpr = clauseExpr.WithCallClause(method, orderByClause).AddArgument(ordering.Expression); - if (method == null) + if (method is null) cx.ModelError(ordering, "Could not determine method call for orderby clause"); } break; @@ -259,25 +259,25 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions case SyntaxKind.FromClause: var fromClause = (FromClauseSyntax)qc; clauseExpr = clauseExpr. - WithLetClause(method, fromClause, cx.GetModel(node).GetDeclaredSymbol(fromClause), fromClause.Identifier). + WithLetClause(method, fromClause, cx.GetModel(node).GetDeclaredSymbol(fromClause)!, fromClause.Identifier). AddArgument(fromClause.Expression); break; case SyntaxKind.LetClause: var letClause = (LetClauseSyntax)qc; - clauseExpr = clauseExpr.WithLetClause(method, letClause, cx.GetModel(node).GetDeclaredSymbol(letClause), letClause.Identifier). + clauseExpr = clauseExpr.WithLetClause(method, letClause, cx.GetModel(node).GetDeclaredSymbol(letClause)!, letClause.Identifier). AddArgument(letClause.Expression); break; case SyntaxKind.JoinClause: var joinClause = (JoinClauseSyntax)qc; - clauseExpr = clauseExpr.WithLetClause(method, joinClause, cx.GetModel(node).GetDeclaredSymbol(joinClause), joinClause.Identifier). + clauseExpr = clauseExpr.WithLetClause(method, joinClause, cx.GetModel(node).GetDeclaredSymbol(joinClause)!, joinClause.Identifier). AddArgument(joinClause.InExpression). AddArgument(joinClause.LeftExpression). AddArgument(joinClause.RightExpression); - if (joinClause.Into != null) + if (joinClause.Into is not null) { - var into = cx.GetModel(node).GetDeclaredSymbol(joinClause.Into); + var into = cx.GetModel(node).GetDeclaredSymbol(joinClause.Into)!; ((LetClause)clauseExpr).WithInto(into); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Switch.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Switch.cs index 3dbe496cd4c..8b0f27db9f3 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Switch.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Switch.cs @@ -13,11 +13,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions public static Expression Create(ExpressionNodeInfo info) => new Switch(info).TryPopulate(); - public Expression SwitchedExpr { get; private set; } - protected override void PopulateExpression(TextWriter trapFile) { - SwitchedExpr = Expression.Create(Context, Syntax.GoverningExpression, this, -1); + Expression.Create(Context, Syntax.GoverningExpression, this, -1); for (var i = 0; i < Syntax.Arms.Count; i++) { new SwitchCase(Context, Syntax.Arms[i], this, i); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/TypeAccess.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/TypeAccess.cs index 54b9730e3d7..0d93df6beec 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/TypeAccess.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/TypeAccess.cs @@ -15,7 +15,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { case SyntaxKind.SimpleMemberAccessExpression: var maes = (MemberAccessExpressionSyntax)Syntax; - if (Type?.Symbol.ContainingType is null) + if (Type?.Symbol?.ContainingType is null) { // namespace qualifier TypeMention.Create(Context, maes.Name, this, Type, Syntax.GetLocation()); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/VariableDeclaration.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/VariableDeclaration.cs index 674f45b3f21..62c407258f9 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/VariableDeclaration.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/VariableDeclaration.cs @@ -3,7 +3,9 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.CSharp.Populators; using Semmle.Extraction.Kinds; -using Semmle.Extraction.Entities; +using System.Collections.Generic; +using System.Linq; +using System.Collections.Immutable; namespace Semmle.Extraction.CSharp.Entities.Expressions { @@ -11,14 +13,14 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { private VariableDeclaration(IExpressionInfo info) : base(info) { } - public static VariableDeclaration Create(Context cx, ISymbol symbol, AnnotatedTypeSymbol? type, TypeSyntax optionalSyntax, Extraction.Entities.Location exprLocation, bool isVar, IExpressionParentEntity parent, int child) + public static VariableDeclaration Create(Context cx, ISymbol symbol, AnnotatedTypeSymbol? type, TypeSyntax? optionalSyntax, Extraction.Entities.Location exprLocation, bool isVar, IExpressionParentEntity parent, int child) { var ret = new VariableDeclaration(new ExpressionInfo(cx, type, exprLocation, ExprKind.LOCAL_VAR_DECL, parent, child, false, null)); cx.Try(null, null, () => { var l = LocalVariable.Create(cx, symbol); l.PopulateManual(ret, isVar); - if (optionalSyntax != null) + if (optionalSyntax is not null) TypeMention.Create(cx, optionalSyntax, ret, type); }); return ret; @@ -27,7 +29,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions private static VariableDeclaration CreateSingle(Context cx, DeclarationExpressionSyntax node, SingleVariableDesignationSyntax designation, IExpressionParentEntity parent, int child) { var variableSymbol = cx.GetModel(designation).GetDeclaredSymbol(designation) as ILocalSymbol; - if (variableSymbol == null) + if (variableSymbol is null) { cx.ModelError(node, "Failed to determine local variable"); return Create(cx, node, (AnnotatedTypeSymbol?)null, parent, child); @@ -47,16 +49,17 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions /// Create a tuple expression representing a parenthesized variable declaration. /// That is, we consider `var (x, y) = ...` to be equivalent to `(var x, var y) = ...`. /// - public static Expression CreateParenthesized(Context cx, DeclarationExpressionSyntax node, ParenthesizedVariableDesignationSyntax designation, IExpressionParentEntity parent, int child) + public static Expression CreateParenthesized(Context cx, DeclarationExpressionSyntax node, ParenthesizedVariableDesignationSyntax designation, IExpressionParentEntity parent, int child, INamedTypeSymbol? t) { - AnnotatedTypeSymbol? type = null; // Should ideally be a corresponding tuple type + var type = t is null ? (AnnotatedTypeSymbol?)null : new AnnotatedTypeSymbol(t, t.NullableAnnotation); var tuple = new Expression(new ExpressionInfo(cx, type, cx.CreateLocation(node.GetLocation()), ExprKind.TUPLE, parent, child, false, null)); cx.Try(null, null, () => { - var child0 = 0; - foreach (var variable in designation.Variables) - Create(cx, node, variable, tuple, child0++); + for (var child0 = 0; child0 < designation.Variables.Count; child0++) + { + Create(cx, node, designation.Variables[child0], tuple, child0, t?.TypeArguments[child0] as INamedTypeSymbol); + } }); return tuple; @@ -64,18 +67,21 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions public static Expression CreateParenthesized(Context cx, VarPatternSyntax varPattern, ParenthesizedVariableDesignationSyntax designation, IExpressionParentEntity parent, int child) { - AnnotatedTypeSymbol? type = null; // Should ideally be a corresponding tuple type - var tuple = new Expression(new ExpressionInfo(cx, type, cx.CreateLocation(varPattern.GetLocation()), ExprKind.TUPLE, parent, child, false, null)); + var tuple = new Expression( + new ExpressionInfo(cx, null, cx.CreateLocation(varPattern.GetLocation()), ExprKind.TUPLE, parent, child, false, null), + shouldPopulate: false); + var elementTypes = new List(); cx.Try(null, null, () => { var child0 = 0; foreach (var variable in designation.Variables) { + Expression sub; switch (variable) { case ParenthesizedVariableDesignationSyntax paren: - CreateParenthesized(cx, varPattern, paren, tuple, child0++); + sub = CreateParenthesized(cx, varPattern, paren, tuple, child0++); break; case SingleVariableDesignationSyntax single: if (cx.GetModel(variable).GetDeclaredSymbol(single) is ILocalSymbol local) @@ -83,6 +89,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions var decl = Create(cx, variable, local.GetAnnotatedType(), tuple, child0++); var l = LocalVariable.Create(cx, local); l.PopulateManual(decl, true); + sub = decl; } else { @@ -90,26 +97,43 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions } break; case DiscardDesignationSyntax discard: - new Discard(cx, discard, tuple, child0++); + sub = new Discard(cx, discard, tuple, child0++); + if (!sub.Type.HasValue || sub.Type.Value.Symbol is null) + { + // The type is only updated in memory, it will not be written to the trap file. + sub.SetType(cx.Compilation.GetSpecialType(SpecialType.System_Object)); + } break; default: throw new InternalError(variable, "Unhandled designation type"); } + + elementTypes.Add(sub.Type.HasValue && sub.Type.Value.Symbol?.Kind != SymbolKind.ErrorType + ? sub.Type.Value.Symbol + : null); } }); + INamedTypeSymbol? tupleType = null; + if (!elementTypes.Any(et => et is null)) + { + tupleType = cx.Compilation.CreateTupleTypeSymbol(elementTypes.ToImmutableArray()!); + } + + tuple.SetType(tupleType); + tuple.TryPopulate(); + return tuple; } - - private static Expression Create(Context cx, DeclarationExpressionSyntax node, VariableDesignationSyntax designation, IExpressionParentEntity parent, int child) + private static Expression Create(Context cx, DeclarationExpressionSyntax node, VariableDesignationSyntax? designation, IExpressionParentEntity parent, int child, INamedTypeSymbol? declarationType) { switch (designation) { case SingleVariableDesignationSyntax single: return CreateSingle(cx, node, single, parent, child); case ParenthesizedVariableDesignationSyntax paren: - return CreateParenthesized(cx, node, paren, parent, child); + return CreateParenthesized(cx, node, paren, parent, child, declarationType); case DiscardDesignationSyntax discard: var type = cx.GetType(discard); return Create(cx, node, type, parent, child); @@ -120,19 +144,19 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions } public static Expression Create(Context cx, DeclarationExpressionSyntax node, IExpressionParentEntity parent, int child) => - Create(cx, node, node.Designation, parent, child); + Create(cx, node, node.Designation, parent, child, cx.GetTypeInfo(node).Type.DisambiguateType() as INamedTypeSymbol); public static VariableDeclaration Create(Context cx, CSharpSyntaxNode c, AnnotatedTypeSymbol? type, IExpressionParentEntity parent, int child) => new VariableDeclaration(new ExpressionInfo(cx, type, cx.CreateLocation(c.FixedLocation()), ExprKind.LOCAL_VAR_DECL, parent, child, false, null)); public static VariableDeclaration Create(Context cx, CatchDeclarationSyntax d, bool isVar, IExpressionParentEntity parent, int child) { - var symbol = cx.GetModel(d).GetDeclaredSymbol(d); + var symbol = cx.GetModel(d).GetDeclaredSymbol(d)!; var type = symbol.GetAnnotatedType(); var ret = Create(cx, d, type, parent, child); cx.Try(d, null, () => { - var declSymbol = cx.GetModel(d).GetDeclaredSymbol(d); + var declSymbol = cx.GetModel(d).GetDeclaredSymbol(d)!; var l = LocalVariable.Create(cx, declSymbol); l.PopulateManual(ret, isVar); TypeMention.Create(cx, d.Type, ret, type); @@ -145,11 +169,11 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions var ret = Create(cx, d, type, parent, child); cx.Try(d, null, () => { - var declSymbol = cx.GetModel(d).GetDeclaredSymbol(d); + var declSymbol = cx.GetModel(d).GetDeclaredSymbol(d)!; var localVar = LocalVariable.Create(cx, declSymbol); localVar.PopulateManual(ret, isVar); - if (d.Initializer != null) + if (d.Initializer is not null) { Create(cx, d.Initializer.Value, ret, 0); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs index 1c435ebd7d1..2fe1cf224ab 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs @@ -17,7 +17,7 @@ namespace Semmle.Extraction.CSharp.Entities type = new Lazy(() => Entities.Type.Create(cx, Symbol.Type)); } - public static Field Create(Context cx, IFieldSymbol field) => FieldFactory.Instance.CreateEntityFromSymbol(cx, field); + public static Field Create(Context cx, IFieldSymbol field) => FieldFactory.Instance.CreateEntityFromSymbol(cx, field.CorrespondingTupleField ?? field); // Do not populate backing fields. // Populate Tuple fields. @@ -28,7 +28,7 @@ namespace Semmle.Extraction.CSharp.Entities { PopulateMetadataHandle(trapFile); PopulateAttributes(); - ContainingType.PopulateGenerics(); + ContainingType!.PopulateGenerics(); PopulateNullability(trapFile, Symbol.GetAnnotatedType()); var unboundFieldKey = Field.Create(Context, Symbol.OriginalDefinition); @@ -61,13 +61,13 @@ namespace Semmle.Extraction.CSharp.Entities foreach (var initializer in Symbol.DeclaringSyntaxReferences .Select(n => n.GetSyntax()) .OfType() - .Where(n => n.Initializer != null)) + .Where(n => n.Initializer is not null)) { Context.PopulateLater(() => { var loc = Context.CreateLocation(initializer.GetLocation()); - var fieldAccess = AddInitializerAssignment(trapFile, initializer.Initializer.Value, loc, null, ref child); + var fieldAccess = AddInitializerAssignment(trapFile, initializer.Initializer!.Value, loc, null, ref child); if (!Symbol.IsStatic) { @@ -79,7 +79,7 @@ namespace Semmle.Extraction.CSharp.Entities foreach (var initializer in Symbol.DeclaringSyntaxReferences .Select(n => n.GetSyntax()) .OfType() - .Where(n => n.EqualsValue != null)) + .Where(n => n.EqualsValue is not null)) { // Mark fields that have explicit initializers. var constValue = Symbol.HasConstantValue @@ -88,7 +88,7 @@ namespace Semmle.Extraction.CSharp.Entities var loc = Context.CreateLocation(initializer.GetLocation()); - AddInitializerAssignment(trapFile, initializer.EqualsValue.Value, loc, constValue, ref child); + AddInitializerAssignment(trapFile, initializer.EqualsValue!.Value, loc, constValue, ref child); } if (IsSourceDeclaration) @@ -105,7 +105,7 @@ namespace Semmle.Extraction.CSharp.Entities } private Expression AddInitializerAssignment(TextWriter trapFile, ExpressionSyntax initializer, Extraction.Entities.Location loc, - string constValue, ref int child) + string? constValue, ref int child) { var type = Symbol.GetAnnotatedType(); var simpleAssignExpr = new Expression(new ExpressionInfo(Context, type, loc, ExprKind.SIMPLE_ASSIGN, this, child++, false, constValue)); @@ -122,7 +122,7 @@ namespace Semmle.Extraction.CSharp.Entities { trapFile.WriteSubId(Type); trapFile.Write(" "); - trapFile.WriteSubId(ContainingType); + trapFile.WriteSubId(ContainingType!); trapFile.Write('.'); trapFile.Write(Symbol.Name); trapFile.Write(";field"); @@ -130,11 +130,11 @@ namespace Semmle.Extraction.CSharp.Entities bool IExpressionParentEntity.IsTopLevelParent => true; - private class FieldFactory : ICachedEntityFactory + private class FieldFactory : CachedEntityFactory { public static FieldFactory Instance { get; } = new FieldFactory(); - public Field Create(Context cx, IFieldSymbol init) => new Field(cx, init); + public override Field Create(Context cx, IFieldSymbol init) => new Field(cx, init); } public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.PushesLabel; } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/File.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/File.cs new file mode 100644 index 00000000000..f22edb158aa --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/File.cs @@ -0,0 +1,82 @@ +using Microsoft.CodeAnalysis; +using Semmle.Util; +using System; +using System.IO; +using System.Linq; + +namespace Semmle.Extraction.CSharp.Entities +{ + internal class File : Extraction.Entities.File + { + public override Context Context => (Context)base.Context; + + protected File(Context cx, string path) + : base(cx, path) + { + } + + public override void Populate(TextWriter trapFile) + { + trapFile.files(this, TransformedPath.Value, TransformedPath.NameWithoutExtension, TransformedPath.Extension); + + if (TransformedPath.ParentDirectory is PathTransformer.ITransformedPath dir) + trapFile.containerparent(Extraction.Entities.Folder.Create(Context, dir), this); + + var trees = Context.Compilation.SyntaxTrees.Where(t => t.FilePath == originalPath); + + if (trees.Any()) + { + foreach (var text in trees.Select(tree => tree.GetText())) + { + var rawText = text.ToString() ?? ""; + var lineCounts = LineCounter.ComputeLineCounts(rawText); + if (rawText.Length > 0 && rawText[rawText.Length - 1] != '\n') + lineCounts.Total++; + + trapFile.numlines(this, lineCounts); + Context.TrapWriter.Archive(originalPath, TransformedPath, text.Encoding ?? System.Text.Encoding.Default); + } + } + else if (IsPossiblyTextFile()) + { + try + { + System.Text.Encoding encoding; + var lineCount = 0; + using (var sr = new StreamReader(originalPath, detectEncodingFromByteOrderMarks: true)) + { + while (sr.ReadLine() is not null) + { + lineCount++; + } + encoding = sr.CurrentEncoding; + } + + trapFile.numlines(this, new LineCounts() { Total = lineCount, Code = 0, Comment = 0 }); + Context.TrapWriter.Archive(originalPath, TransformedPath, encoding ?? System.Text.Encoding.Default); + } + catch (Exception exc) + { + Context.ExtractionError($"Couldn't read file: {originalPath}. {exc.Message}", null, null, exc.StackTrace); + } + } + + trapFile.file_extraction_mode(this, Context.Extractor.Standalone ? 1 : 0); + } + + private bool IsPossiblyTextFile() + { + var extension = TransformedPath.Extension.ToLowerInvariant(); + return !extension.Equals("dll") && !extension.Equals("exe"); + } + + public static File Create(Context cx, string path) => FileFactory.Instance.CreateEntity(cx, (typeof(File), path), path); + + private class FileFactory : CachedEntityFactory + { + public static FileFactory Instance { get; } = new FileFactory(); + + public override File Create(Context cx, string init) => new File(cx, init); + } + } +} diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/FreshEntity.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/FreshEntity.cs new file mode 100644 index 00000000000..79144e109d4 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/FreshEntity.cs @@ -0,0 +1,12 @@ +namespace Semmle.Extraction.CSharp.Entities +{ + internal abstract class FreshEntity : Extraction.FreshEntity + { + public override Context Context => (Context)base.Context; + + protected FreshEntity(Context cx) + : base(cx) + { + } + } +} diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/IExpressionInfo.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/IExpressionInfo.cs index 1a43a6fbd02..ea43b19456c 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/IExpressionInfo.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/IExpressionInfo.cs @@ -46,7 +46,7 @@ namespace Semmle.Extraction.CSharp.Entities /// If the expression does not have a value, then this /// is null. /// - string ExprValue { get; } + string? ExprValue { get; } NullableFlowState FlowState { get; } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs index 04bc319c93e..3da002f8b2c 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs @@ -17,7 +17,7 @@ namespace Semmle.Extraction.CSharp.Entities PopulateNullability(trapFile, Symbol.GetAnnotatedType()); var type = Type.Create(Context, Symbol.Type); - trapFile.indexers(this, Symbol.GetName(useMetadataName: true), ContainingType, type.TypeRef, OriginalDefinition); + trapFile.indexers(this, Symbol.GetName(useMetadataName: true), ContainingType!, type.TypeRef, OriginalDefinition); foreach (var l in Locations) trapFile.indexer_location(this, l); @@ -42,7 +42,7 @@ namespace Semmle.Extraction.CSharp.Entities if (IsSourceDeclaration) { var expressionBody = ExpressionBody; - if (expressionBody != null) + if (expressionBody is not null) { // The expression may need to reference parameters in the getter. // So we need to arrange that the expression is populated after the getter. @@ -63,7 +63,7 @@ namespace Semmle.Extraction.CSharp.Entities trapFile.explicitly_implements(this, explicitInterface.TypeRef); foreach (var syntax in declSyntaxReferences) - TypeMention.Create(Context, syntax.ExplicitInterfaceSpecifier.Name, this, explicitInterface); + TypeMention.Create(Context, syntax.ExplicitInterfaceSpecifier!.Name, this, explicitInterface); } @@ -75,7 +75,7 @@ namespace Semmle.Extraction.CSharp.Entities public override void WriteId(TextWriter trapFile) { - trapFile.WriteSubId(ContainingType); + trapFile.WriteSubId(ContainingType!); trapFile.Write('.'); trapFile.Write(Symbol.MetadataName); trapFile.Write('('); @@ -98,11 +98,11 @@ namespace Semmle.Extraction.CSharp.Entities bool IExpressionParentEntity.IsTopLevelParent => true; - private class IndexerFactory : ICachedEntityFactory + private class IndexerFactory : CachedEntityFactory { public static IndexerFactory Instance { get; } = new IndexerFactory(); - public Indexer Create(Context cx, IPropertySymbol init) => new Indexer(cx, init); + public override Indexer Create(Context cx, IPropertySymbol init) => new Indexer(cx, init); } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs index efc50f7f85f..a94401f2a93 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs @@ -22,11 +22,11 @@ namespace Semmle.Extraction.CSharp.Entities public static new LocalFunction Create(Context cx, IMethodSymbol field) => LocalFunctionFactory.Instance.CreateEntityFromSymbol(cx, field); - private class LocalFunctionFactory : ICachedEntityFactory + private class LocalFunctionFactory : CachedEntityFactory { public static LocalFunctionFactory Instance { get; } = new LocalFunctionFactory(); - public LocalFunction Create(Context cx, IMethodSymbol init) => new LocalFunction(cx, init); + public override LocalFunction Create(Context cx, IMethodSymbol init) => new LocalFunction(cx, init); } public override void Populate(TextWriter trapFile) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs index 4f9a0c4f7f6..ee685731c60 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs @@ -49,15 +49,15 @@ namespace Semmle.Extraction.CSharp.Entities { if (Symbol is ILocalSymbol local && local.HasConstantValue) { - trapFile.constant_value(this, Expression.ValueAsString(local.ConstantValue)); + trapFile.constant_value(this, Expression.ValueAsString(local.ConstantValue!)); } } - private class LocalVariableFactory : ICachedEntityFactory + private class LocalVariableFactory : CachedEntityFactory { public static LocalVariableFactory Instance { get; } = new LocalVariableFactory(); - public LocalVariable Create(Context cx, ISymbol init) => new LocalVariable(cx, init); + public override LocalVariable Create(Context cx, ISymbol init) => new LocalVariable(cx, init); } public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NeedsLabel; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs index 8a1572796bc..61c8d768521 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs @@ -3,12 +3,13 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.CSharp.Populators; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; namespace Semmle.Extraction.CSharp.Entities { - public abstract class Method : CachedSymbol, IExpressionParentEntity, IStatementParentEntity + internal abstract class Method : CachedSymbol, IExpressionParentEntity, IStatementParentEntity { protected Method(Context cx, IMethodSymbol init) : base(cx, init) { } @@ -25,7 +26,7 @@ namespace Semmle.Extraction.CSharp.Entities { // Non-generic reduced extensions must be extracted exactly like the // non-reduced counterparts - parameters = Symbol.ReducedFrom.Parameters; + parameters = Symbol.ReducedFrom!.Parameters; } else { @@ -73,16 +74,16 @@ namespace Semmle.Extraction.CSharp.Entities var block = Block; var expr = ExpressionBody; - if (block != null || expr != null) + if (block is not null || expr is not null) { Context.PopulateLater( () => { ExtractInitializers(trapFile); - if (block != null) + if (block is not null) Statements.Block.Create(Context, block, this, 0); else - Expression.Create(Context, expr, this, 0); + Expression.Create(Context, expr!, this, 0); NumberOfLines(trapFile, BodyDeclaringSymbol, this); }); @@ -95,7 +96,10 @@ namespace Semmle.Extraction.CSharp.Entities { var node = (CSharpSyntaxNode)decl.GetSyntax(); var lineCounts = node.Accept(new AstLineCounter()); - trapFile.numlines(callable, lineCounts); + if (lineCounts is not null) + { + trapFile.numlines(callable, lineCounts); + } } } @@ -110,11 +114,11 @@ namespace Semmle.Extraction.CSharp.Entities if (IsSourceDeclaration) { foreach (var syntax in Symbol.DeclaringSyntaxReferences.Select(d => d.GetSyntax()).OfType()) - TypeMention.Create(Context, syntax.ExplicitInterfaceSpecifier.Name, this, explicitInterface); + TypeMention.Create(Context, syntax.ExplicitInterfaceSpecifier!.Name, this, explicitInterface); } } - if (Symbol.OverriddenMethod != null) + if (Symbol.OverriddenMethod is not null) { trapFile.overrides(this, Method.Create(Context, Symbol.OverriddenMethod)); } @@ -128,7 +132,7 @@ namespace Semmle.Extraction.CSharp.Entities m.Symbol.ReturnType.BuildOrWriteId(m.Context, trapFile, m.Symbol); trapFile.Write(" "); - trapFile.WriteSubId(m.ContainingType); + trapFile.WriteSubId(m.ContainingType!); AddExplicitInterfaceQualifierToId(m.Context, trapFile, m.Symbol.ExplicitInterfaceImplementations); @@ -189,7 +193,7 @@ namespace Semmle.Extraction.CSharp.Entities var index = 0; var @params = method.MethodKind == MethodKind.ReducedExtension - ? method.ReducedFrom.Parameters + ? method.ReducedFrom!.Parameters : method.Parameters; foreach (var param in @params) @@ -232,9 +236,10 @@ namespace Semmle.Extraction.CSharp.Entities /// /// /// - public static Method Create(Context cx, IMethodSymbol methodDecl) + [return: NotNullIfNotNull("methodDecl")] + public static Method? Create(Context cx, IMethodSymbol? methodDecl) { - if (methodDecl == null) + if (methodDecl is null) return null; var methodKind = methodDecl.MethodKind; @@ -278,10 +283,10 @@ namespace Semmle.Extraction.CSharp.Entities public Method OriginalDefinition => IsReducedExtension - ? Create(Context, Symbol.ReducedFrom) + ? Create(Context, Symbol.ReducedFrom!) : Create(Context, Symbol.OriginalDefinition); - public override Microsoft.CodeAnalysis.Location FullLocation => ReportingLocation; + public override Location? FullLocation => ReportingLocation; public override bool IsSourceDeclaration => Symbol.IsSourceDeclaration(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs index b644558f799..1d56da2623f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs @@ -8,7 +8,7 @@ namespace Semmle.Extraction.CSharp.Entities private Modifier(Context cx, string init) : base(cx, init) { } - public override Microsoft.CodeAnalysis.Location ReportingLocation => null; + public override Location? ReportingLocation => null; public override void WriteId(TextWriter trapFile) { @@ -72,7 +72,7 @@ namespace Semmle.Extraction.CSharp.Entities public static void ExtractModifiers(Context cx, TextWriter trapFile, IEntity key, ISymbol symbol) { - var interfaceDefinition = symbol.ContainingType != null + var interfaceDefinition = symbol.ContainingType is not null && symbol.ContainingType.Kind == SymbolKind.NamedType && symbol.ContainingType.TypeKind == TypeKind.Interface; @@ -140,11 +140,11 @@ namespace Semmle.Extraction.CSharp.Entities return ModifierFactory.Instance.CreateEntity(cx, (typeof(Modifier), modifier), modifier); } - private class ModifierFactory : ICachedEntityFactory + private class ModifierFactory : CachedEntityFactory { public static ModifierFactory Instance { get; } = new ModifierFactory(); - public Modifier Create(Context cx, string init) => new Modifier(cx, init); + public override Modifier Create(Context cx, string init) => new Modifier(cx, init); } public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.OptionalLabel; } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs index 31e66656eb3..7d77ef276cb 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs @@ -8,13 +8,13 @@ namespace Semmle.Extraction.CSharp.Entities private Namespace(Context cx, INamespaceSymbol init) : base(cx, init) { } - public override Microsoft.CodeAnalysis.Location ReportingLocation => null; + public override Location? ReportingLocation => null; public override void Populate(TextWriter trapFile) { trapFile.namespaces(this, Symbol.Name); - if (Symbol.ContainingNamespace != null) + if (Symbol.ContainingNamespace is not null) { var parent = Create(Context, Symbol.ContainingNamespace); trapFile.parent_namespace(this, parent); @@ -36,11 +36,11 @@ namespace Semmle.Extraction.CSharp.Entities public static Namespace Create(Context cx, INamespaceSymbol ns) => NamespaceFactory.Instance.CreateEntityFromSymbol(cx, ns); - private class NamespaceFactory : ICachedEntityFactory + private class NamespaceFactory : CachedEntityFactory { public static NamespaceFactory Instance { get; } = new NamespaceFactory(); - public Namespace Create(Context cx, INamespaceSymbol init) => new Namespace(cx, init); + public override Namespace Create(Context cx, INamespaceSymbol init) => new Namespace(cx, init); } public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NoLabel; @@ -49,7 +49,7 @@ namespace Semmle.Extraction.CSharp.Entities private string QualifiedName => Symbol.ToDisplayString(); - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is Namespace ns && QualifiedName == ns.QualifiedName; } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/NamespaceDeclaration.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/NamespaceDeclaration.cs index 3a19417008a..749c7ae22c8 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/NamespaceDeclaration.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/NamespaceDeclaration.cs @@ -28,7 +28,7 @@ namespace Semmle.Extraction.CSharp.Entities public override void Populate(TextWriter trapFile) { - var @namespace = (INamespaceSymbol)Context.GetModel(node).GetSymbolInfo(node.Name).Symbol; + var @namespace = (INamespaceSymbol)Context.GetModel(node).GetSymbolInfo(node.Name).Symbol!; var ns = Namespace.Create(Context, @namespace); trapFile.namespace_declarations(this, ns); trapFile.namespace_declaration_location(this, Context.CreateLocation(node.Name.GetLocation())); @@ -40,7 +40,7 @@ namespace Semmle.Extraction.CSharp.Entities member.Accept(visitor); } - if (parent != null) + if (parent is not null) { trapFile.parent_namespace_declaration(this, parent); } @@ -52,11 +52,11 @@ namespace Semmle.Extraction.CSharp.Entities return NamespaceDeclarationFactory.Instance.CreateEntity(cx, decl, init); } - private class NamespaceDeclarationFactory : ICachedEntityFactory<(NamespaceDeclarationSyntax decl, NamespaceDeclaration parent), NamespaceDeclaration> + private class NamespaceDeclarationFactory : CachedEntityFactory<(NamespaceDeclarationSyntax decl, NamespaceDeclaration parent), NamespaceDeclaration> { public static readonly NamespaceDeclarationFactory Instance = new NamespaceDeclarationFactory(); - public NamespaceDeclaration Create(Context cx, (NamespaceDeclarationSyntax decl, NamespaceDeclaration parent) init) => + public override NamespaceDeclaration Create(Context cx, (NamespaceDeclarationSyntax decl, NamespaceDeclaration parent) init) => new NamespaceDeclaration(cx, init.decl, init.parent); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/NonGeneratedSourceLocation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/NonGeneratedSourceLocation.cs new file mode 100644 index 00000000000..141c428dd55 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/NonGeneratedSourceLocation.cs @@ -0,0 +1,65 @@ +using Microsoft.CodeAnalysis; +using System.IO; + +namespace Semmle.Extraction.CSharp.Entities +{ + internal class NonGeneratedSourceLocation : Extraction.Entities.SourceLocation + { + public override Context Context => (Context)base.Context; + + protected NonGeneratedSourceLocation(Context cx, Location init) + : base(cx, init) + { + Position = init.GetLineSpan(); + FileEntity = File.Create(Context, Position.Path); + } + + public static NonGeneratedSourceLocation Create(Context cx, Location loc) => SourceLocationFactory.Instance.CreateEntity(cx, loc, loc); + + public override void Populate(TextWriter trapFile) + { + trapFile.locations_default(this, FileEntity, + Position.Span.Start.Line + 1, Position.Span.Start.Character + 1, + Position.Span.End.Line + 1, Position.Span.End.Character); + + var mapped = Symbol!.GetMappedLineSpan(); + if (mapped.HasMappedPath && mapped.IsValid) + { + var mappedLoc = Create(Context, Location.Create(mapped.Path, default, mapped.Span)); + + trapFile.locations_mapped(this, mappedLoc); + } + } + + public FileLinePositionSpan Position + { + get; + } + + public File FileEntity + { + get; + } + + public override void WriteId(TextWriter trapFile) + { + trapFile.Write("loc,"); + trapFile.WriteSubId(FileEntity); + trapFile.Write(','); + trapFile.Write(Position.Span.Start.Line + 1); + trapFile.Write(','); + trapFile.Write(Position.Span.Start.Character + 1); + trapFile.Write(','); + trapFile.Write(Position.Span.End.Line + 1); + trapFile.Write(','); + trapFile.Write(Position.Span.End.Character); + } + + private class SourceLocationFactory : CachedEntityFactory + { + public static SourceLocationFactory Instance { get; } = new SourceLocationFactory(); + + public override NonGeneratedSourceLocation Create(Context cx, Location init) => new NonGeneratedSourceLocation(cx, init); + } + } +} diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs index 90f17d70ea4..0b16b4ec9fe 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs @@ -30,7 +30,7 @@ namespace Semmle.Extraction.CSharp.Entities { PopulateMethod(trapFile); PopulateModifiers(trapFile); - ContainingType.PopulateGenerics(); + ContainingType!.PopulateGenerics(); var returnType = Type.Create(Context, Symbol.ReturnType); trapFile.methods(this, Name, ContainingType, returnType.TypeRef, OriginalDefinition); @@ -55,11 +55,11 @@ namespace Semmle.Extraction.CSharp.Entities public static new OrdinaryMethod Create(Context cx, IMethodSymbol method) => OrdinaryMethodFactory.Instance.CreateEntityFromSymbol(cx, method); - private class OrdinaryMethodFactory : ICachedEntityFactory + private class OrdinaryMethodFactory : CachedEntityFactory { public static OrdinaryMethodFactory Instance { get; } = new OrdinaryMethodFactory(); - public OrdinaryMethod Create(Context cx, IMethodSymbol init) => new OrdinaryMethod(cx, init); + public override OrdinaryMethod Create(Context cx, IMethodSymbol init) => new OrdinaryMethod(cx, init); } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs index b1b7a5444fe..3bbf38a71eb 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs @@ -7,12 +7,12 @@ using System.IO; namespace Semmle.Extraction.CSharp.Entities { - public class Parameter : CachedSymbol, IExpressionParentEntity + internal class Parameter : CachedSymbol, IExpressionParentEntity { - protected IEntity Parent { get; set; } + protected IEntity? Parent { get; set; } protected Parameter Original { get; } - protected Parameter(Context cx, IParameterSymbol init, IEntity parent, Parameter original) + protected Parameter(Context cx, IParameterSymbol init, IEntity? parent, Parameter? original) : base(cx, init) { Parent = parent; @@ -67,7 +67,7 @@ namespace Semmle.Extraction.CSharp.Entities } } - public static Parameter Create(Context cx, IParameterSymbol param, IEntity parent, Parameter original = null) => + public static Parameter Create(Context cx, IParameterSymbol param, IEntity parent, Parameter? original = null) => ParameterFactory.Instance.CreateEntity(cx, param, (param, parent, original)); public static Parameter Create(Context cx, IParameterSymbol param) => @@ -75,8 +75,12 @@ namespace Semmle.Extraction.CSharp.Entities public override void WriteId(TextWriter trapFile) { - if (Parent == null) + if (Parent is null) Parent = Method.Create(Context, Symbol.ContainingSymbol as IMethodSymbol); + + if (Parent is null) + throw new InternalError(Symbol, "Couldn't get parent of symbol."); + trapFile.WriteSubId(Parent); trapFile.Write('_'); trapFile.Write(Ordinal); @@ -107,7 +111,7 @@ namespace Semmle.Extraction.CSharp.Entities Context.ModelError(Symbol, "Inconsistent parameter declaration"); var type = Type.Create(Context, Symbol.Type); - trapFile.@params(this, Name, type.TypeRef, Ordinal, ParamKind, Parent, Original); + trapFile.@params(this, Name, type.TypeRef, Ordinal, ParamKind, Parent!, Original); foreach (var l in Symbol.Locations) trapFile.param_location(this, Context.CreateLocation(l)); @@ -130,9 +134,9 @@ namespace Semmle.Extraction.CSharp.Entities foreach (var syntax in Symbol.DeclaringSyntaxReferences .Select(d => d.GetSyntax()) .OfType() - .Where(s => s.Type != null)) + .Where(s => s.Type is not null)) { - TypeMention.Create(Context, syntax.Type, this, type); + TypeMention.Create(Context, syntax.Type!, this, type); } } @@ -142,22 +146,22 @@ namespace Semmle.Extraction.CSharp.Entities // We should really define param_default(param, string) // And use parameter child #0 to encode the default expression. var defaultValue = GetParameterDefaultValue(Symbol); - if (defaultValue == null) + if (defaultValue is null) { // In case this parameter belongs to an accessor of an indexer, we need // to get the default value from the corresponding parameter belonging // to the indexer itself var method = (IMethodSymbol)Symbol.ContainingSymbol; - if (method != null) + if (method is not null) { var i = method.Parameters.IndexOf(Symbol); - var indexer = (IPropertySymbol)method.AssociatedSymbol; - if (indexer != null) + var indexer = (IPropertySymbol?)method.AssociatedSymbol; + if (indexer is not null) defaultValue = GetParameterDefaultValue(indexer.Parameters[i]); } } - if (defaultValue != null) + if (defaultValue is not null) { Context.PopulateLater(() => { @@ -171,17 +175,17 @@ namespace Semmle.Extraction.CSharp.Entities bool IExpressionParentEntity.IsTopLevelParent => true; - private static EqualsValueClauseSyntax GetParameterDefaultValue(IParameterSymbol parameter) + private static EqualsValueClauseSyntax? GetParameterDefaultValue(IParameterSymbol parameter) { var syntax = parameter.DeclaringSyntaxReferences.Select(@ref => @ref.GetSyntax()).OfType().FirstOrDefault(); return syntax?.Default; } - private class ParameterFactory : ICachedEntityFactory<(IParameterSymbol, IEntity, Parameter), Parameter> + private class ParameterFactory : CachedEntityFactory<(IParameterSymbol, IEntity?, Parameter?), Parameter> { public static ParameterFactory Instance { get; } = new ParameterFactory(); - public Parameter Create(Context cx, (IParameterSymbol, IEntity, Parameter) init) => new Parameter(cx, init.Item1, init.Item2, init.Item3); + public override Parameter Create(Context cx, (IParameterSymbol, IEntity?, Parameter?) init) => new Parameter(cx, init.Item1, init.Item2, init.Item3); } public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.OptionalLabel; @@ -211,53 +215,55 @@ namespace Semmle.Extraction.CSharp.Entities return 98735267; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { - return obj != null && obj.GetType() == typeof(VarargsType); + return obj is not null && obj.GetType() == typeof(VarargsType); } public static VarargsType Create(Context cx) => VarargsTypeFactory.Instance.CreateEntity(cx, typeof(VarargsType), null); - private class VarargsTypeFactory : ICachedEntityFactory + private class VarargsTypeFactory : CachedEntityFactory { public static VarargsTypeFactory Instance { get; } = new VarargsTypeFactory(); - public VarargsType Create(Context cx, string init) => new VarargsType(cx); + public override VarargsType Create(Context cx, string? init) => new VarargsType(cx); } } internal class VarargsParam : Parameter { +#nullable disable warnings private VarargsParam(Context cx, Method methodKey) : base(cx, null, methodKey, null) { } +#nullable restore warnings public override void Populate(TextWriter trapFile) { var typeKey = VarargsType.Create(Context); // !! Maybe originaldefinition is wrong - trapFile.@params(this, "", typeKey, Ordinal, Kind.None, Parent, this); + trapFile.@params(this, "", typeKey, Ordinal, Kind.None, Parent!, this); trapFile.param_location(this, GeneratedLocation.Create(Context)); } - protected override int Ordinal => ((Method)Parent).OriginalDefinition.Symbol.Parameters.Length; + protected override int Ordinal => ((Method)Parent!).OriginalDefinition.Symbol.Parameters.Length; public override int GetHashCode() { return 9873567; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { - return obj != null && obj.GetType() == typeof(VarargsParam); + return obj is not null && obj.GetType() == typeof(VarargsParam); } public static VarargsParam Create(Context cx, Method method) => VarargsParamFactory.Instance.CreateEntity(cx, typeof(VarargsParam), method); - private class VarargsParamFactory : ICachedEntityFactory + private class VarargsParamFactory : CachedEntityFactory { public static VarargsParamFactory Instance { get; } = new VarargsParamFactory(); - public VarargsParam Create(Context cx, Method init) => new VarargsParam(cx, init); + public override VarargsParam Create(Context cx, Method init) => new VarargsParam(cx, init); } } @@ -268,24 +274,24 @@ namespace Semmle.Extraction.CSharp.Entities private ConstructedExtensionParameter(Context cx, Method method, Parameter original) : base(cx, original.Symbol, method, original) { - constructedType = method.Symbol.ReceiverType; + constructedType = method.Symbol.ReceiverType!; } public override void Populate(TextWriter trapFile) { var typeKey = Type.Create(Context, constructedType); - trapFile.@params(this, Original.Symbol.Name, typeKey.TypeRef, 0, Kind.This, Parent, Original); + trapFile.@params(this, Original.Symbol.Name, typeKey.TypeRef, 0, Kind.This, Parent!, Original); trapFile.param_location(this, Original.Location); } public static ConstructedExtensionParameter Create(Context cx, Method method, Parameter parameter) => - ExtensionParamFactory.Instance.CreateEntity(cx, (new SymbolEqualityWrapper(parameter.Symbol), new SymbolEqualityWrapper(method.Symbol.ReceiverType)), (method, parameter)); + ExtensionParamFactory.Instance.CreateEntity(cx, (new SymbolEqualityWrapper(parameter.Symbol), new SymbolEqualityWrapper(method.Symbol.ReceiverType!)), (method, parameter)); - private class ExtensionParamFactory : ICachedEntityFactory<(Method, Parameter), ConstructedExtensionParameter> + private class ExtensionParamFactory : CachedEntityFactory<(Method, Parameter), ConstructedExtensionParameter> { public static ExtensionParamFactory Instance { get; } = new ExtensionParamFactory(); - public ConstructedExtensionParameter Create(Context cx, (Method, Parameter) init) => + public override ConstructedExtensionParameter Create(Context cx, (Method, Parameter) init) => new ConstructedExtensionParameter(cx, init.Item1, init.Item2); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/PreprocessorDirectives/LineDirective.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/PreprocessorDirectives/LineDirective.cs index 470f54f379a..06fafd1e4ce 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/PreprocessorDirectives/LineDirective.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/PreprocessorDirectives/LineDirective.cs @@ -26,12 +26,12 @@ namespace Semmle.Extraction.CSharp.Entities if (trivia.Line.IsKind(SyntaxKind.NumericLiteralToken)) { - var value = (int)trivia.Line.Value; + var value = (int)trivia.Line.Value!; trapFile.directive_line_value(this, value); if (!string.IsNullOrWhiteSpace(trivia.File.ValueText)) { - var file = Extraction.Entities.File.Create(Context, trivia.File.ValueText); + var file = File.Create(Context, trivia.File.ValueText); trapFile.directive_line_file(this, file); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/PreprocessorDirectives/PragmaChecksumDirective.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/PreprocessorDirectives/PragmaChecksumDirective.cs index 572b77e2c73..caa77ceec33 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/PreprocessorDirectives/PragmaChecksumDirective.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/PreprocessorDirectives/PragmaChecksumDirective.cs @@ -12,7 +12,7 @@ namespace Semmle.Extraction.CSharp.Entities protected override void PopulatePreprocessor(TextWriter trapFile) { - var file = Extraction.Entities.File.Create(Context, trivia.File.ValueText); + var file = File.Create(Context, trivia.File.ValueText); trapFile.pragma_checksums(this, file, trivia.Guid.ToString(), trivia.Bytes.ToString()); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs index 085047d0812..5fb77cf4dc2 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs @@ -25,7 +25,7 @@ namespace Semmle.Extraction.CSharp.Entities { trapFile.WriteSubId(Type); trapFile.Write(" "); - trapFile.WriteSubId(ContainingType); + trapFile.WriteSubId(ContainingType!); trapFile.Write('.'); Method.AddExplicitInterfaceQualifierToId(Context, trapFile, Symbol.ExplicitInterfaceImplementations); trapFile.Write(Symbol.Name); @@ -42,7 +42,7 @@ namespace Semmle.Extraction.CSharp.Entities PopulateRefKind(trapFile, Symbol.RefKind); var type = Type; - trapFile.properties(this, Symbol.GetName(), ContainingType, type.TypeRef, Create(Context, Symbol.OriginalDefinition)); + trapFile.properties(this, Symbol.GetName(), ContainingType!, type.TypeRef, Create(Context, Symbol.OriginalDefinition)); var getter = Symbol.GetMethod; var setter = Symbol.SetMethod; @@ -63,7 +63,7 @@ namespace Semmle.Extraction.CSharp.Entities trapFile.explicitly_implements(this, explicitInterface.TypeRef); foreach (var syntax in declSyntaxReferences) - TypeMention.Create(Context, syntax.ExplicitInterfaceSpecifier.Name, this, explicitInterface); + TypeMention.Create(Context, syntax.ExplicitInterfaceSpecifier!.Name, this, explicitInterface); } foreach (var l in Locations) @@ -72,7 +72,7 @@ namespace Semmle.Extraction.CSharp.Entities if (IsSourceDeclaration && Symbol.FromSource()) { var expressionBody = ExpressionBody; - if (expressionBody != null) + if (expressionBody is not null) { Context.PopulateLater(() => Expression.Create(Context, expressionBody, this, 0)); } @@ -80,11 +80,11 @@ namespace Semmle.Extraction.CSharp.Entities var child = 1; foreach (var initializer in declSyntaxReferences .Select(n => n.Initializer) - .Where(i => i != null)) + .Where(i => i is not null)) { Context.PopulateLater(() => { - var loc = Context.CreateLocation(initializer.GetLocation()); + var loc = Context.CreateLocation(initializer!.GetLocation()); var annotatedType = AnnotatedTypeSymbol.CreateNotAnnotated(Symbol.Type); var simpleAssignExpr = new Expression(new ExpressionInfo(Context, annotatedType, loc, ExprKind.SIMPLE_ASSIGN, this, child++, false, null)); Expression.CreateFromNode(new ExpressionNodeInfo(Context, initializer.Value, simpleAssignExpr, 0)); @@ -124,11 +124,11 @@ namespace Semmle.Extraction.CSharp.Entities return isIndexer ? Indexer.Create(cx, prop) : PropertyFactory.Instance.CreateEntityFromSymbol(cx, prop); } - private class PropertyFactory : ICachedEntityFactory + private class PropertyFactory : CachedEntityFactory { public static PropertyFactory Instance { get; } = new PropertyFactory(); - public Property Create(Context cx, IPropertySymbol init) => new Property(cx, init); + public override Property Create(Context cx, IPropertySymbol init) => new Property(cx, init); } public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.PushesLabel; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Case.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Case.cs index 73a2582060b..40b75751b59 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Case.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Case.cs @@ -72,7 +72,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { Expressions.Pattern.Create(Context, Stmt.Pattern, this, 0); - if (Stmt.WhenClause != null) + if (Stmt.WhenClause is not null) { Expression.Create(Context, Stmt.WhenClause.Condition, this, 1); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Catch.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Catch.cs index f7484c72d56..95c1fdfb99c 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Catch.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Catch.cs @@ -14,17 +14,17 @@ namespace Semmle.Extraction.CSharp.Entities.Statements protected override void PopulateStatement(TextWriter trapFile) { - var isSpecificCatchClause = Stmt.Declaration != null; - var hasVariableDeclaration = isSpecificCatchClause && Stmt.Declaration.Identifier.RawKind != 0; + var isSpecificCatchClause = Stmt.Declaration is not null; + var hasVariableDeclaration = isSpecificCatchClause && Stmt.Declaration!.Identifier.RawKind != 0; if (hasVariableDeclaration) // A catch clause of the form 'catch(Ex ex) { ... }' { - var decl = Expressions.VariableDeclaration.Create(Context, Stmt.Declaration, false, this, 0); + var decl = Expressions.VariableDeclaration.Create(Context, Stmt.Declaration!, false, this, 0); trapFile.catch_type(this, Type.Create(Context, decl.Type).TypeRef, true); } else if (isSpecificCatchClause) // A catch clause of the form 'catch(Ex) { ... }' { - trapFile.catch_type(this, Type.Create(Context, Context.GetType(Stmt.Declaration.Type)).TypeRef, true); + trapFile.catch_type(this, Type.Create(Context, Context.GetType(Stmt.Declaration!.Type)).TypeRef, true); } else // A catch clause of the form 'catch { ... }' { @@ -32,7 +32,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements trapFile.catch_type(this, exception, false); } - if (Stmt.Filter != null) + if (Stmt.Filter is not null) { // For backward compatibility, the catch filter clause is child number 2. Expression.Create(Context, Stmt.Filter.FilterExpression, this, 2); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/ExpressionStatement.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/ExpressionStatement.cs index 76d369bde23..92a34afb1c2 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/ExpressionStatement.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/ExpressionStatement.cs @@ -17,7 +17,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements protected override void PopulateStatement(TextWriter trapFile) { - if (Stmt.Expression != null) + if (Stmt.Expression is not null) Expression.Create(Context, Stmt.Expression, this, 0); else Context.ModelError(Stmt, "Invalid expression statement"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/For.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/For.cs index 678373168f6..ac89d7499df 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/For.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/For.cs @@ -21,7 +21,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { var child = -1; - if (Stmt.Declaration != null) + if (Stmt.Declaration is not null) VariableDeclarations.Populate(Context, Stmt.Declaration, this, child, childIncrement: -1); foreach (var init in Stmt.Initializers) @@ -29,7 +29,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements Expression.Create(Context, init, this, child--); } - if (Stmt.Condition != null) + if (Stmt.Condition is not null) { Expression.Create(Context, Stmt.Condition, this, 0); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/ForEach.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/ForEach.cs index bd9b1528a13..1074ddc3b16 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/ForEach.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/ForEach.cs @@ -32,7 +32,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements Expression.Create(Context, Stmt.Expression, this, 1); var semanticModel = Context.GetModel(Stmt); - var typeSymbol = semanticModel.GetDeclaredSymbol(Stmt); + var typeSymbol = semanticModel.GetDeclaredSymbol(Stmt)!; var type = typeSymbol.GetAnnotatedType(); var location = Context.CreateLocation(Stmt.Identifier.GetLocation()); @@ -51,31 +51,31 @@ namespace Semmle.Extraction.CSharp.Entities.Statements trapFile.foreach_stmt_info(this, info.IsAsynchronous); - if (info.GetEnumeratorMethod != null) + if (info.GetEnumeratorMethod is not null) { var m = Method.Create(Context, info.GetEnumeratorMethod); trapFile.foreach_stmt_desugar(this, m, ForeachSymbolType.GetEnumeratorMethod); } - if (info.MoveNextMethod != null) + if (info.MoveNextMethod is not null) { var m = Method.Create(Context, info.MoveNextMethod); trapFile.foreach_stmt_desugar(this, m, ForeachSymbolType.MoveNextMethod); } - if (info.DisposeMethod != null) + if (info.DisposeMethod is not null) { var m = Method.Create(Context, info.DisposeMethod); trapFile.foreach_stmt_desugar(this, m, ForeachSymbolType.DisposeMethod); } - if (info.CurrentProperty != null) + if (info.CurrentProperty is not null) { var p = Property.Create(Context, info.CurrentProperty); trapFile.foreach_stmt_desugar(this, p, ForeachSymbolType.CurrentProperty); } - if (info.ElementType != null) + if (info.ElementType is not null) { var t = Type.Create(Context, info.ElementType); trapFile.foreach_stmt_desugar(this, t, ForeachSymbolType.ElementType); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/GlobalStatementsBlock.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/GlobalStatementsBlock.cs index 011437fac9b..0b31313278a 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/GlobalStatementsBlock.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/GlobalStatementsBlock.cs @@ -15,7 +15,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements this.parent = parent; } - public override Microsoft.CodeAnalysis.Location ReportingLocation + public override Microsoft.CodeAnalysis.Location? ReportingLocation { get { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Goto.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Goto.cs index 20548dd056a..44f5ef257e4 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Goto.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Goto.cs @@ -36,12 +36,12 @@ namespace Semmle.Extraction.CSharp.Entities.Statements switch (GetKind(Stmt)) { case StmtKind.GOTO: - var target = ((IdentifierNameSyntax)Stmt.Expression).Identifier.Text; + var target = ((IdentifierNameSyntax)Stmt.Expression!).Identifier.Text; trapFile.exprorstmt_name(this, target); break; case StmtKind.GOTO_CASE: - Expr = Expression.Create(Context, Stmt.Expression, this, 0); - ConstantValue = Switch.LabelForValue(Context.GetModel(Stmt).GetConstantValue(Stmt.Expression).Value); + Expr = Expression.Create(Context, Stmt.Expression!, this, 0); + ConstantValue = Switch.LabelForValue(Context.GetModel(Stmt).GetConstantValue(Stmt.Expression!).Value); break; case StmtKind.GOTO_DEFAULT: ConstantValue = Switch.DefaultLabel; @@ -49,9 +49,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements } } - public Expression Expr { get; private set; } + public Expression? Expr { get; private set; } - public object ConstantValue { get; private set; } + public object? ConstantValue { get; private set; } public bool IsDefault => ConstantValue == Switch.DefaultLabel; } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/If.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/If.cs index 010cd9a793b..aa26d4fa01b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/If.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/If.cs @@ -22,7 +22,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements Create(Context, Stmt.Statement, this, 1); - if (Stmt.Else != null) + if (Stmt.Else is not null) Create(Context, Stmt.Else.Statement, this, 2); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Labeled.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Labeled.cs index d549008daaf..137ad37bacc 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Labeled.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Labeled.cs @@ -8,7 +8,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements { private readonly IStatementParentEntity parent; private readonly int child; - private Statement labelledStmt; + private Statement? labelledStmt; private Labeled(Context cx, LabeledStatementSyntax stmt, IStatementParentEntity parent, int child) : base(cx, stmt, StmtKind.LABEL, parent, child) @@ -33,6 +33,6 @@ namespace Semmle.Extraction.CSharp.Entities.Statements labelledStmt = Statement.Create(Context, Stmt.Statement, parent, child + 1); } - public override int NumberOfStatements => 1 + labelledStmt.NumberOfStatements; + public override int NumberOfStatements => 1 + labelledStmt?.NumberOfStatements ?? 0; } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/LocalFunction.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/LocalFunction.cs index aaebd538c67..3acf768d313 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/LocalFunction.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/LocalFunction.cs @@ -22,7 +22,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements /// /// Gets the IMethodSymbol for this local function statement. /// - private IMethodSymbol Symbol + private IMethodSymbol? Symbol { get { @@ -31,14 +31,16 @@ namespace Semmle.Extraction.CSharp.Entities.Statements } } - /// - /// Gets the function defined by this local statement. - /// - private Entities.LocalFunction Function => Entities.LocalFunction.Create(Context, Symbol); - protected override void PopulateStatement(TextWriter trapFile) { - trapFile.local_function_stmts(this, Function); + if (Symbol is null) + { + Context.ExtractionError("Could not get local function symbol", null, Context.CreateLocation(this.ReportingLocation), severity: Util.Logging.Severity.Warning); + return; + } + + var function = Entities.LocalFunction.Create(Context, Symbol); + trapFile.local_function_stmts(this, function); } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Return.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Return.cs index 64692630b4c..fa8181589b9 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Return.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Return.cs @@ -18,7 +18,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements protected override void PopulateStatement(TextWriter trapFile) { - if (Stmt.Expression != null) + if (Stmt.Expression is not null) Expression.Create(Context, Stmt.Expression, this, 0); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Switch.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Switch.cs index df73554f389..24c98de3982 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Switch.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Switch.cs @@ -13,7 +13,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements // Sometimes, the literal "null" is used as a label. // This is inconveniently represented by the "null" object. // This cannot be stored in a Dictionary<>, so substitute an object which can be. - public static object LabelForValue(object label) + public static object LabelForValue(object? label) { return label ?? nullLabel; } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Throw.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Throw.cs index dbff60d890b..6a37ae25b4f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Throw.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Throw.cs @@ -18,7 +18,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements protected override void PopulateStatement(TextWriter trapFile) { - if (Stmt.Expression != null) + if (Stmt.Expression is not null) Expression.Create(Context, Stmt.Expression, this, 0); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Try.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Try.cs index bb17c2beaa2..5d9421a7632 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Try.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Try.cs @@ -27,22 +27,10 @@ namespace Semmle.Extraction.CSharp.Entities.Statements Create(Context, Stmt.Block, this, 0); - if (Stmt.Finally != null) + if (Stmt.Finally is not null) { Create(Context, Stmt.Finally.Block, this, -1); } } - - public static SyntaxNodeOrToken NextNode(SyntaxNode node) - { - for (var i = node.Parent.ChildNodesAndTokens().GetEnumerator(); i.MoveNext();) - { - if (i.Current == node) - { - return i.MoveNext() ? i.Current : null; - } - } - return null; - } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Using.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Using.cs index b62e6556a8c..eba411bfe5f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Using.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Using.cs @@ -19,13 +19,13 @@ namespace Semmle.Extraction.CSharp.Entities.Statements protected override void PopulateStatement(TextWriter trapFile) { - if (Stmt.Declaration != null) + if (Stmt.Declaration is not null) VariableDeclarations.Populate(Context, Stmt.Declaration, this, -1, childIncrement: -1); - if (Stmt.Expression != null) + if (Stmt.Expression is not null) Expression.Create(Context, Stmt.Expression, this, 0); - if (Stmt.Statement != null) + if (Stmt.Statement is not null) Statement.Create(Context, Stmt.Statement, this, 1); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Yield.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Yield.cs index 605a7cacdbc..a59233fd942 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Yield.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/Yield.cs @@ -18,7 +18,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements protected override void PopulateStatement(TextWriter trapFile) { - if (Stmt.Expression != null) + if (Stmt.Expression is not null) { Expression.Create(Context, Stmt.Expression, this, 0); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/TypeMention.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/TypeMention.cs index e2fd72eae56..08a1fa3eb0f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/TypeMention.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/TypeMention.cs @@ -12,9 +12,9 @@ namespace Semmle.Extraction.CSharp.Entities private readonly TypeSyntax syntax; private readonly IEntity parent; private readonly Type type; - private readonly Microsoft.CodeAnalysis.Location loc; + private readonly Microsoft.CodeAnalysis.Location? loc; - private TypeMention(Context cx, TypeSyntax syntax, IEntity parent, Type type, Microsoft.CodeAnalysis.Location loc = null) + private TypeMention(Context cx, TypeSyntax syntax, IEntity parent, Type type, Microsoft.CodeAnalysis.Location? loc = null) : base(cx) { this.syntax = syntax; @@ -32,7 +32,7 @@ namespace Semmle.Extraction.CSharp.Entities case NullableTypeSyntax nts: // int[]? -> int[] -> int // int? -> int? - return Context.GetTypeInfo(nts.ElementType).Type.IsReferenceType + return Context.GetTypeInfo(nts.ElementType).Type?.IsReferenceType == true ? GetArrayElementType(nts.ElementType) : nts; case PointerTypeSyntax pts: @@ -103,7 +103,7 @@ namespace Semmle.Extraction.CSharp.Entities case SyntaxKind.QualifiedName: var qns = (QualifiedNameSyntax)syntax; var right = Create(Context, qns.Right, parent, type); - if (type.ContainingType is object) + if (type.ContainingType is not null) { // Type qualifier Create(Context, qns.Left, right, type.ContainingType); @@ -121,14 +121,14 @@ namespace Semmle.Extraction.CSharp.Entities trapFile.type_mention_location(this, Context.CreateLocation(loc)); } - public static TypeMention Create(Context cx, TypeSyntax syntax, IEntity parent, Type type, Microsoft.CodeAnalysis.Location loc = null) + public static TypeMention Create(Context cx, TypeSyntax syntax, IEntity parent, Type type, Microsoft.CodeAnalysis.Location? loc = null) { var ret = new TypeMention(cx, syntax, parent, type, loc); cx.Try(syntax, null, () => ret.Populate(cx.TrapWriter.Writer)); return ret; } - public static TypeMention Create(Context cx, TypeSyntax syntax, IEntity parent, AnnotatedTypeSymbol? type, Microsoft.CodeAnalysis.Location loc = null) => + public static TypeMention Create(Context cx, TypeSyntax syntax, IEntity parent, AnnotatedTypeSymbol? type, Microsoft.CodeAnalysis.Location? loc = null) => Create(cx, syntax, parent, Type.Create(cx, type?.Symbol), loc); public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.OptionalLabel; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs index 62a10396b4b..48381430428 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs @@ -40,11 +40,11 @@ namespace Semmle.Extraction.CSharp.Entities public static ArrayType Create(Context cx, IArrayTypeSymbol symbol) => ArrayTypeFactory.Instance.CreateEntityFromSymbol(cx, symbol); - private class ArrayTypeFactory : ICachedEntityFactory + private class ArrayTypeFactory : CachedEntityFactory { public static ArrayTypeFactory Instance { get; } = new ArrayTypeFactory(); - public ArrayType Create(Context cx, IArrayTypeSymbol init) => new ArrayType(cx, init); + public override ArrayType Create(Context cx, IArrayTypeSymbol init) => new ArrayType(cx, init); } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs index 2516afe037b..b43c4c741f4 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs @@ -11,7 +11,7 @@ namespace Semmle.Extraction.CSharp.Entities public static DynamicType Create(Context cx, IDynamicTypeSymbol type) => DynamicTypeFactory.Instance.CreateEntityFromSymbol(cx, type); - public override Microsoft.CodeAnalysis.Location ReportingLocation => Context.Compilation.ObjectType.Locations.FirstOrDefault(); + public override Microsoft.CodeAnalysis.Location? ReportingLocation => Context.Compilation.ObjectType.Locations.FirstOrDefault(); public override void Populate(TextWriter trapFile) { @@ -27,11 +27,11 @@ namespace Semmle.Extraction.CSharp.Entities trapFile.Write("dynamic;type"); } - private class DynamicTypeFactory : ICachedEntityFactory + private class DynamicTypeFactory : CachedEntityFactory { public static DynamicTypeFactory Instance { get; } = new DynamicTypeFactory(); - public DynamicType Create(Context cx, IDynamicTypeSymbol init) => new DynamicType(cx, init); + public override DynamicType Create(Context cx, IDynamicTypeSymbol init) => new DynamicType(cx, init); } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/FunctionPointerType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/FunctionPointerType.cs index 903de837a73..2bb9cd3a1b6 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/FunctionPointerType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/FunctionPointerType.cs @@ -32,11 +32,11 @@ namespace Semmle.Extraction.CSharp.Entities public static FunctionPointerType Create(Context cx, IFunctionPointerTypeSymbol symbol) => FunctionPointerTypeFactory.Instance.CreateEntityFromSymbol(cx, symbol); - private class FunctionPointerTypeFactory : ICachedEntityFactory + private class FunctionPointerTypeFactory : CachedEntityFactory { public static FunctionPointerTypeFactory Instance { get; } = new FunctionPointerTypeFactory(); - public FunctionPointerType Create(Context cx, IFunctionPointerTypeSymbol init) => new FunctionPointerType(cx, init); + public override FunctionPointerType Create(Context cx, IFunctionPointerTypeSymbol init) => new FunctionPointerType(cx, init); } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs index 4c250e22692..b607da5d998 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs @@ -36,7 +36,7 @@ namespace Semmle.Extraction.CSharp.Entities { if (Symbol.TypeKind == TypeKind.Error) { - Context.Extractor.MissingType(Symbol.ToString(), Context.FromSource); + Context.Extractor.MissingType(Symbol.ToString()!, Context.FromSource); return; } @@ -76,7 +76,7 @@ namespace Semmle.Extraction.CSharp.Entities PopulateType(trapFile, constructUnderlyingTupleType); - if (Symbol.EnumUnderlyingType != null) + if (Symbol.EnumUnderlyingType is not null) { trapFile.enum_underlying_type(this, Type.Create(Context, Symbol.EnumUnderlyingType).TypeRef); } @@ -108,7 +108,7 @@ namespace Semmle.Extraction.CSharp.Entities foreach (var l in GetLocations(Symbol)) yield return Context.CreateLocation(l); - if (Context.Extractor.OutputPath != null && Symbol.DeclaringSyntaxReferences.Any()) + if (!Context.Extractor.Standalone && Symbol.DeclaringSyntaxReferences.Any()) yield return Assembly.CreateOutputAssembly(Context); } } @@ -124,7 +124,7 @@ namespace Semmle.Extraction.CSharp.Entities ); } - public override Microsoft.CodeAnalysis.Location ReportingLocation => GetLocations(Symbol).FirstOrDefault(); + public override Microsoft.CodeAnalysis.Location? ReportingLocation => GetLocations(Symbol).FirstOrDefault(); private bool IsAnonymousType() => Symbol.IsAnonymousType || Symbol.Name.Contains("__AnonymousType"); @@ -149,18 +149,18 @@ namespace Semmle.Extraction.CSharp.Entities base.WriteQuotedId(trapFile); } - private class NamedTypeFactory : ICachedEntityFactory + private class NamedTypeFactory : CachedEntityFactory { public static NamedTypeFactory Instance { get; } = new NamedTypeFactory(); - public NamedType Create(Context cx, INamedTypeSymbol init) => new NamedType(cx, init, false); + public override NamedType Create(Context cx, INamedTypeSymbol init) => new NamedType(cx, init, false); } - private class UnderlyingTupleTypeFactory : ICachedEntityFactory + private class UnderlyingTupleTypeFactory : CachedEntityFactory { public static UnderlyingTupleTypeFactory Instance { get; } = new UnderlyingTupleTypeFactory(); - public NamedType Create(Context cx, INamedTypeSymbol init) => new NamedType(cx, init, true); + public override NamedType Create(Context cx, INamedTypeSymbol init) => new NamedType(cx, init, true); } // Do not create typerefs of constructed generics as they are always in the current trap file. @@ -186,11 +186,11 @@ namespace Semmle.Extraction.CSharp.Entities // `NamedType`s and `NamedTypeRef`s NamedTypeRefFactory.Instance.CreateEntity(cx, (typeof(NamedTypeRef), new SymbolEqualityWrapper(type)), type); - private class NamedTypeRefFactory : ICachedEntityFactory + private class NamedTypeRefFactory : CachedEntityFactory { public static NamedTypeRefFactory Instance { get; } = new NamedTypeRefFactory(); - public NamedTypeRef Create(Context cx, INamedTypeSymbol init) => new NamedTypeRef(cx, init); + public override NamedTypeRef Create(Context cx, INamedTypeSymbol init) => new NamedTypeRef(cx, init); } public override bool NeedsPopulation => true; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs index 33f7c44d21f..f65d7b17db6 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs @@ -22,18 +22,18 @@ namespace Semmle.Extraction.CSharp.Entities public override int GetHashCode() => 987354; - public override bool Equals(object obj) + public override bool Equals(object? obj) { - return obj != null && obj.GetType() == typeof(NullType); + return obj is not null && obj.GetType() == typeof(NullType); } public static Type Create(Context cx) => NullTypeFactory.Instance.CreateEntity(cx, typeof(NullType), null); - private class NullTypeFactory : ICachedEntityFactory + private class NullTypeFactory : CachedEntityFactory { public static NullTypeFactory Instance { get; } = new NullTypeFactory(); - public NullType Create(Context cx, ITypeSymbol init) => new NullType(cx); + public override NullType Create(Context cx, ITypeSymbol? init) => new NullType(cx); } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Nullability.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Nullability.cs index a0d9e2a1f8c..3cd85bdbdde 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Nullability.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Nullability.cs @@ -5,7 +5,7 @@ using System.Linq; namespace Semmle.Extraction.CSharp.Entities { - public sealed class Nullability + internal sealed class Nullability { public int Annotation { get; } @@ -64,7 +64,7 @@ namespace Semmle.Extraction.CSharp.Entities NullableParameters = method.GetAnnotatedTypeArguments().Select(a => new Nullability(a)).ToArray(); } - public override bool Equals(object other) + public override bool Equals(object? other) { return other is Nullability n && Annotation == n.Annotation && NullableParameters.SequenceEqual(n.NullableParameters); } @@ -96,7 +96,7 @@ namespace Semmle.Extraction.CSharp.Entities } } - public class NullabilityEntity : CachedEntity + internal class NullabilityEntity : CachedEntity { public NullabilityEntity(Context cx, Nullability init) : base(cx, init) { @@ -127,11 +127,11 @@ namespace Semmle.Extraction.CSharp.Entities public static NullabilityEntity Create(Context cx, Nullability init) => NullabilityFactory.Instance.CreateEntity(cx, init, init); - private class NullabilityFactory : ICachedEntityFactory + private class NullabilityFactory : CachedEntityFactory { public static NullabilityFactory Instance { get; } = new NullabilityFactory(); - public NullabilityEntity Create(Context cx, Nullability init) => new NullabilityEntity(cx, init); + public override NullabilityEntity Create(Context cx, Nullability init) => new NullabilityEntity(cx, init); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs index dd9b749f53e..635172a0e62 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs @@ -31,11 +31,11 @@ namespace Semmle.Extraction.CSharp.Entities public static PointerType Create(Context cx, IPointerTypeSymbol symbol) => PointerTypeFactory.Instance.CreateEntityFromSymbol(cx, symbol); - private class PointerTypeFactory : ICachedEntityFactory + private class PointerTypeFactory : CachedEntityFactory { public static PointerTypeFactory Instance { get; } = new PointerTypeFactory(); - public PointerType Create(Context cx, IPointerTypeSymbol init) => new PointerType(cx, init); + public override PointerType Create(Context cx, IPointerTypeSymbol init) => new PointerType(cx, init); } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs index 18321f84d32..ca3e182a99d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs @@ -13,13 +13,13 @@ namespace Semmle.Extraction.CSharp.Entities /// internal class TupleType : Type { - public static TupleType Create(Context cx, INamedTypeSymbol type) => TupleTypeFactory.Instance.CreateEntityFromSymbol(cx, type); + public static TupleType Create(Context cx, INamedTypeSymbol type) => TupleTypeFactory.Instance.CreateEntityFromSymbol(cx, type.TupleUnderlyingType ?? type); - private class TupleTypeFactory : ICachedEntityFactory + private class TupleTypeFactory : CachedEntityFactory { public static TupleTypeFactory Instance { get; } = new TupleTypeFactory(); - public TupleType Create(Context cx, INamedTypeSymbol init) => new TupleType(cx, init); + public override TupleType Create(Context cx, INamedTypeSymbol init) => new TupleType(cx, init); } private TupleType(Context cx, INamedTypeSymbol init) : base(cx, init) @@ -41,7 +41,7 @@ namespace Semmle.Extraction.CSharp.Entities PopulateType(trapFile); PopulateGenerics(); - var underlyingType = NamedType.CreateNamedTypeFromTupleType(Context, Symbol.TupleUnderlyingType ?? Symbol); + var underlyingType = NamedType.CreateNamedTypeFromTupleType(Context, Symbol); trapFile.tuple_underlying_type(this, underlyingType); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs index 3931c6ef1d8..e2522648678 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs @@ -8,11 +8,12 @@ using System.Linq; namespace Semmle.Extraction.CSharp.Entities { - public abstract class Type : CachedSymbol + internal abstract class Type : CachedSymbol { - protected Type(Context cx, ITypeSymbol init) +#nullable disable warnings + protected Type(Context cx, ITypeSymbol? init) : base(cx, init) { } - +#nullable restore warnings public override bool NeedsPopulation => base.NeedsPopulation || Symbol.TypeKind == TypeKind.Dynamic || Symbol.TypeKind == TypeKind.TypeParameter; @@ -20,7 +21,7 @@ namespace Semmle.Extraction.CSharp.Entities public static bool ConstructedOrParentIsConstructed(INamedTypeSymbol symbol) { return !SymbolEqualityComparer.Default.Equals(symbol, symbol.OriginalDefinition) || - symbol.ContainingType != null && ConstructedOrParentIsConstructed(symbol.ContainingType); + symbol.ContainingType is not null && ConstructedOrParentIsConstructed(symbol.ContainingType); } private static Kinds.TypeKind GetClassType(Context cx, ITypeSymbol t, bool constructUnderlyingTupleType) @@ -100,12 +101,12 @@ namespace Semmle.Extraction.CSharp.Entities } var containingType = ContainingType; - if (containingType != null && Symbol.Kind != SymbolKind.TypeParameter) + if (containingType is not null && Symbol.Kind != SymbolKind.TypeParameter) { var originalDefinition = Symbol.TypeKind == TypeKind.Error ? this : Create(Context, Symbol.OriginalDefinition); trapFile.nested_types(this, containingType, originalDefinition); } - else if (Symbol.ContainingNamespace != null) + else if (Symbol.ContainingNamespace is not null) { trapFile.parent_namespace(this, Namespace.Create(Context, Symbol.ContainingNamespace)); } @@ -115,7 +116,7 @@ namespace Semmle.Extraction.CSharp.Entities // They are in the namespace of the original object var elementType = array.ElementType; var ns = elementType.TypeKind == TypeKind.TypeParameter ? Context.Compilation.GlobalNamespace : elementType.ContainingNamespace; - if (ns != null) + if (ns is not null) trapFile.parent_namespace(this, Namespace.Create(Context, ns)); } @@ -124,15 +125,15 @@ namespace Semmle.Extraction.CSharp.Entities var elementType = pointer.PointedAtType; var ns = elementType.TypeKind == TypeKind.TypeParameter ? Context.Compilation.GlobalNamespace : elementType.ContainingNamespace; - if (ns != null) + if (ns is not null) trapFile.parent_namespace(this, Namespace.Create(Context, ns)); } - if (Symbol.BaseType != null && Symbol.BaseType.SpecialType == SpecialType.System_MulticastDelegate) + if (Symbol.BaseType is not null && Symbol.BaseType.SpecialType == SpecialType.System_MulticastDelegate) { // This is a delegate. // The method "Invoke" has the return type. - var invokeMethod = ((INamedTypeSymbol)Symbol).DelegateInvokeMethod; + var invokeMethod = ((INamedTypeSymbol)Symbol).DelegateInvokeMethod!; ExtractParametersForDelegateLikeType(trapFile, invokeMethod, t => trapFile.delegate_return_type(this, t)); } @@ -154,8 +155,8 @@ namespace Semmle.Extraction.CSharp.Entities baseLists = baseLists.Concat(declSyntaxReferences.OfType().Select(c => c.BaseList)); baseLists - .Where(bl => bl != null) - .SelectMany(bl => bl.Types) + .Where(bl => bl is not null) + .SelectMany(bl => bl!.Types) .Zip( baseTypes.Where(bt => bt.Symbol.SpecialType != SpecialType.System_Object), (s, t) => TypeMention.Create(Context, s.Type, this, t)) @@ -211,7 +212,7 @@ namespace Semmle.Extraction.CSharp.Entities /// public void PopulateGenerics() { - if (Symbol == null || !NeedsPopulation || !Context.ExtractGenerics(this)) + if (Symbol is null || !NeedsPopulation || !Context.ExtractGenerics(this)) return; var members = new List(); @@ -239,7 +240,7 @@ namespace Semmle.Extraction.CSharp.Entities Context.CreateEntity(member); } - if (Symbol.BaseType != null) + if (Symbol.BaseType is not null) Create(Context, Symbol.BaseType).PopulateGenerics(); foreach (var i in Symbol.Interfaces) @@ -258,10 +259,10 @@ namespace Semmle.Extraction.CSharp.Entities ExtractRecursive(); } - public static Type Create(Context cx, ITypeSymbol type) + public static Type Create(Context cx, ITypeSymbol? type) { type = type.DisambiguateType(); - return type == null + return type is null ? NullType.Create(cx) : (Type)cx.CreateEntity(type); } @@ -271,27 +272,27 @@ namespace Semmle.Extraction.CSharp.Entities public virtual int Dimension => 0; - public static bool IsDelegate(ITypeSymbol symbol) => - symbol != null && symbol.TypeKind == TypeKind.Delegate; + public static bool IsDelegate(ITypeSymbol? symbol) => + symbol is not null && symbol.TypeKind == TypeKind.Delegate; /// /// A copy of a delegate "Invoke" method or function pointer parameter. /// private class DelegateTypeParameter : Parameter { - private DelegateTypeParameter(Context cx, IParameterSymbol init, IEntity parent, Parameter original) + private DelegateTypeParameter(Context cx, IParameterSymbol init, IEntity parent, Parameter? original) : base(cx, init, parent, original) { } - public static new DelegateTypeParameter Create(Context cx, IParameterSymbol param, IEntity parent, Parameter original = null) => + public static new DelegateTypeParameter Create(Context cx, IParameterSymbol param, IEntity parent, Parameter? original = null) => // We need to use a different cache key than `param` to avoid mixing up // `DelegateTypeParameter`s and `Parameter`s DelegateTypeParameterFactory.Instance.CreateEntity(cx, (typeof(DelegateTypeParameter), new SymbolEqualityWrapper(param)), (param, parent, original)); - private class DelegateTypeParameterFactory : ICachedEntityFactory<(IParameterSymbol, IEntity, Parameter), DelegateTypeParameter> + private class DelegateTypeParameterFactory : CachedEntityFactory<(IParameterSymbol, IEntity, Parameter?), DelegateTypeParameter> { public static DelegateTypeParameterFactory Instance { get; } = new DelegateTypeParameterFactory(); - public DelegateTypeParameter Create(Context cx, (IParameterSymbol, IEntity, Parameter) init) => + public override DelegateTypeParameter Create(Context cx, (IParameterSymbol, IEntity, Parameter?) init) => new DelegateTypeParameter(cx, init.Item1, init.Item2, init.Item3); } } @@ -312,7 +313,7 @@ namespace Semmle.Extraction.CSharp.Entities public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NoLabel; - public override bool Equals(object obj) + public override bool Equals(object? obj) { var other = obj as Type; return other?.GetType() == GetType() && SymbolEqualityComparer.Default.Equals(other.Symbol, Symbol); @@ -326,7 +327,6 @@ namespace Semmle.Extraction.CSharp.Entities protected Type(Context cx, T init) : base(cx, init) { } - // todo: change this with .net 5 to be an override public new T Symbol => (T)base.Symbol; } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs index fc8ac7e39d8..dcddf5d5cf2 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs @@ -62,8 +62,8 @@ namespace Semmle.Extraction.CSharp.Entities var declSyntaxReferences = Symbol.DeclaringSyntaxReferences .Select(d => d.GetSyntax()) .Select(s => s.Parent) - .Where(p => p != null) - .Select(p => p.Parent) + .Where(p => p is not null) + .Select(p => p!.Parent) .ToArray(); var clauses = declSyntaxReferences.OfType().SelectMany(m => m.ConstraintClauses); clauses = clauses.Concat(declSyntaxReferences.OfType().SelectMany(c => c.ConstraintClauses)); @@ -127,11 +127,11 @@ namespace Semmle.Extraction.CSharp.Entities trapFile.Write(kind); } - private class TypeParameterFactory : ICachedEntityFactory + private class TypeParameterFactory : CachedEntityFactory { public static TypeParameterFactory Instance { get; } = new TypeParameterFactory(); - public TypeParameter Create(Context cx, ITypeParameterSymbol init) => new TypeParameter(cx, init); + public override TypeParameter Create(Context cx, ITypeParameterSymbol init) => new TypeParameter(cx, init); } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs index 2643363773b..49afa50ccc1 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs @@ -18,7 +18,7 @@ namespace Semmle.Extraction.CSharp.Entities var returnType = Type.Create(Context, Symbol.ReturnType); trapFile.operators(this, Symbol.Name, - OperatorSymbol(Context, Symbol.Name), + OperatorSymbol(Context, Symbol), ContainingType, returnType.TypeRef, (UserOperator)OriginalDefinition); @@ -58,15 +58,15 @@ namespace Semmle.Extraction.CSharp.Entities private bool IsImplicitOperator(out ITypeSymbol containingType) { containingType = Symbol.ContainingType; - if (containingType != null) + if (containingType is not null) { var containingNamedType = containingType as INamedTypeSymbol; - return containingNamedType == null || + return containingNamedType is null || !containingNamedType.GetMembers(Symbol.Name).Contains(Symbol); } var pointerType = Symbol.Parameters.Select(p => p.Type).OfType().FirstOrDefault(); - if (pointerType != null) + if (pointerType is not null) { containingType = pointerType; return true; @@ -176,20 +176,21 @@ namespace Semmle.Extraction.CSharp.Entities /// Extractor context. /// The method name. /// The converted name. - public static string OperatorSymbol(Context cx, string methodName) + private static string OperatorSymbol(Context cx, IMethodSymbol method) { + var methodName = method.Name; if (!OperatorSymbol(methodName, out var result)) - cx.ModelError($"Unhandled operator name in OperatorSymbol(): '{methodName}'"); + cx.ModelError(method, $"Unhandled operator name in OperatorSymbol(): '{methodName}'"); return result; } public static new UserOperator Create(Context cx, IMethodSymbol symbol) => UserOperatorFactory.Instance.CreateEntityFromSymbol(cx, symbol); - private class UserOperatorFactory : ICachedEntityFactory + private class UserOperatorFactory : CachedEntityFactory { public static UserOperatorFactory Instance { get; } = new UserOperatorFactory(); - public UserOperator Create(Context cx, IMethodSymbol init) => new UserOperator(cx, init); + public override UserOperator Create(Context cx, IMethodSymbol init) => new UserOperator(cx, init); } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/UsingDirective.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/UsingDirective.cs index 96ce5f339cf..d278d904253 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/UsingDirective.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/UsingDirective.cs @@ -42,12 +42,12 @@ namespace Semmle.Extraction.CSharp.Entities else { // A "using static" - var m = Type.Create(Context, (ITypeSymbol)info.Symbol); + var m = Type.Create(Context, (ITypeSymbol?)info.Symbol); trapFile.using_static_directives(this, m.TypeRef); trapFile.using_directive_location(this, Context.CreateLocation(ReportingLocation)); } - if (parent != null) + if (parent is not null) { trapFile.parent_namespace_declaration(this, parent); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Analyser.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Analyser.cs new file mode 100644 index 00000000000..3de3a68b656 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Analyser.cs @@ -0,0 +1,311 @@ +using System; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using System.IO; +using System.Linq; +using Semmle.Extraction.CSharp.Populators; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Diagnostics; +using Semmle.Util.Logging; + +namespace Semmle.Extraction.CSharp +{ + /// + /// Encapsulates a C# analysis task. + /// + public class Analyser : IDisposable + { + protected Extraction.Extractor? extractor; + protected CSharpCompilation? compilation; + protected Layout? layout; + protected CommonOptions? options; + + private readonly object progressMutex = new object(); + + // The bulk of the extraction work, potentially executed in parallel. + protected readonly List extractionTasks = new List(); + private int taskCount = 0; + + private readonly Stopwatch stopWatch = new Stopwatch(); + + private readonly IProgressMonitor progressMonitor; + + public ILogger Logger { get; } + + protected readonly bool addAssemblyTrapPrefix; + + public PathTransformer PathTransformer { get; } + + protected Analyser(IProgressMonitor pm, ILogger logger, bool addAssemblyTrapPrefix, PathTransformer pathTransformer) + { + Logger = logger; + this.addAssemblyTrapPrefix = addAssemblyTrapPrefix; + Logger.Log(Severity.Info, "EXTRACTION STARTING at {0}", DateTime.Now); + stopWatch.Start(); + progressMonitor = pm; + PathTransformer = pathTransformer; + } + + /// + /// Perform an analysis on a source file/syntax tree. + /// + /// Syntax tree to analyse. + public void AnalyseTree(SyntaxTree tree) + { + extractionTasks.Add(() => DoExtractTree(tree)); + } + +#nullable disable warnings + + /// + /// Enqueue all reference analysis tasks. + /// + public void AnalyseReferences() + { + foreach (var assembly in compilation.References.OfType()) + { + // CIL first - it takes longer. + if (options.CIL) + extractionTasks.Add(() => DoExtractCIL(assembly)); + extractionTasks.Add(() => DoAnalyseReferenceAssembly(assembly)); + } + } + + /// + /// Constructs the map from assembly string to its filename. + /// + /// Roslyn doesn't record the relationship between a filename and its assembly + /// information, so we need to retrieve this information manually. + /// + protected void SetReferencePaths() + { + foreach (var reference in compilation.References.OfType()) + { + try + { + var refPath = reference.FilePath!; + + /* This method is significantly faster and more lightweight than using + * System.Reflection.Assembly.ReflectionOnlyLoadFrom. It is also allows + * loading the same assembly from different locations. + */ + using var pereader = new System.Reflection.PortableExecutable.PEReader(new FileStream(refPath, FileMode.Open, FileAccess.Read, FileShare.Read)); + + var metadata = pereader.GetMetadata(); + string assemblyIdentity; + unsafe + { + var reader = new System.Reflection.Metadata.MetadataReader(metadata.Pointer, metadata.Length); + var def = reader.GetAssemblyDefinition(); + assemblyIdentity = reader.GetString(def.Name) + " " + def.Version; + } + extractor.SetAssemblyFile(assemblyIdentity, refPath); + + } + catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] + { + extractor.Message(new Message("Exception reading reference file", reference.FilePath, null, ex.StackTrace)); + } + } + } + + /// + /// Extract an assembly to a new trap file. + /// If the trap file exists, skip extraction to avoid duplicating + /// extraction within the snapshot. + /// + /// The assembly to extract. + private void DoAnalyseReferenceAssembly(PortableExecutableReference r) + { + try + { + var stopwatch = new Stopwatch(); + stopwatch.Start(); + + var assemblyPath = r.FilePath!; + var transformedAssemblyPath = PathTransformer.Transform(assemblyPath); + var projectLayout = layout.LookupProjectOrDefault(transformedAssemblyPath); + using var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedAssemblyPath, options.TrapCompression, discardDuplicates: true); + + var skipExtraction = options.Cache && File.Exists(trapWriter.TrapFile); + + if (!skipExtraction) + { + /* Note on parallel builds: + * + * The trap writer and source archiver both perform atomic moves + * of the file to the final destination. + * + * If the same source file or trap file are generated concurrently + * (by different parallel invocations of the extractor), then + * last one wins. + * + * Specifically, if two assemblies are analysed concurrently in a build, + * then there is a small amount of duplicated work but the output should + * still be correct. + */ + + // compilation.Clone() reduces memory footprint by allowing the symbols + // in c to be garbage collected. + Compilation c = compilation.Clone(); + + + if (c.GetAssemblyOrModuleSymbol(r) is IAssemblySymbol assembly) + { + var cx = new Context(extractor, c, trapWriter, new AssemblyScope(assembly, assemblyPath), addAssemblyTrapPrefix); + + foreach (var module in assembly.Modules) + { + AnalyseNamespace(cx, module.GlobalNamespace); + } + + Entities.Attribute.ExtractAttributes(cx, assembly, Entities.Assembly.Create(cx, assembly.GetSymbolLocation())); + + cx.PopulateAll(); + } + } + + ReportProgress(assemblyPath, trapWriter.TrapFile, stopwatch.Elapsed, skipExtraction ? AnalysisAction.UpToDate : AnalysisAction.Extracted); + } + catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] + { + Logger.Log(Severity.Error, " Unhandled exception analyzing {0}: {1}", r.FilePath, ex); + } + } + + private void DoExtractCIL(PortableExecutableReference r) + { + var stopwatch = new Stopwatch(); + stopwatch.Start(); + CIL.Analyser.ExtractCIL(layout, r.FilePath!, Logger, !options.Cache, options.PDB, options.TrapCompression, out var trapFile, out var extracted); + stopwatch.Stop(); + ReportProgress(r.FilePath, trapFile, stopwatch.Elapsed, extracted ? AnalysisAction.Extracted : AnalysisAction.UpToDate); + } + + private void DoExtractTree(SyntaxTree tree) + { + try + { + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var sourcePath = tree.FilePath; + var transformedSourcePath = PathTransformer.Transform(sourcePath); + + var projectLayout = layout.LookupProjectOrNull(transformedSourcePath); + var excluded = projectLayout is null; + var trapPath = excluded ? "" : projectLayout!.GetTrapPath(Logger, transformedSourcePath, options.TrapCompression); + var upToDate = false; + + if (!excluded) + { + // compilation.Clone() is used to allow symbols to be garbage collected. + using var trapWriter = projectLayout!.CreateTrapWriter(Logger, transformedSourcePath, options.TrapCompression, discardDuplicates: false); + + upToDate = options.Fast && FileIsUpToDate(sourcePath, trapWriter.TrapFile); + + if (!upToDate) + { + var cx = new Context(extractor, compilation.Clone(), trapWriter, new SourceScope(tree), addAssemblyTrapPrefix); + // Ensure that the file itself is populated in case the source file is totally empty + var root = tree.GetRoot(); + Entities.File.Create(cx, root.SyntaxTree.FilePath); + + var csNode = (CSharpSyntaxNode)root; + csNode.Accept(new CompilationUnitVisitor(cx)); + csNode.Accept(new DirectiveVisitor(cx)); + cx.PopulateAll(); + CommentPopulator.ExtractCommentBlocks(cx, cx.CommentGenerator); + cx.PopulateAll(); + } + } + + ReportProgress(sourcePath, trapPath, stopwatch.Elapsed, excluded + ? AnalysisAction.Excluded + : upToDate + ? AnalysisAction.UpToDate + : AnalysisAction.Extracted); + } + catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] + { + extractor.Message(new Message($"Unhandled exception processing syntax tree. {ex.Message}", tree.FilePath, null, ex.StackTrace)); + } + } + +#nullable restore warnings + + private static bool FileIsUpToDate(string src, string dest) + { + return File.Exists(dest) && + File.GetLastWriteTime(dest) >= File.GetLastWriteTime(src); + } + + private void AnalyseNamespace(Context cx, INamespaceSymbol ns) + { + foreach (var memberNamespace in ns.GetNamespaceMembers()) + { + AnalyseNamespace(cx, memberNamespace); + } + + foreach (var memberType in ns.GetTypeMembers()) + { + Entities.Type.Create(cx, memberType).ExtractRecursive(); + } + } + + private void ReportProgress(string src, string output, TimeSpan time, AnalysisAction action) + { + lock (progressMutex) + progressMonitor.Analysed(++taskCount, extractionTasks.Count, src, output, time, action); + } + + /// + /// Run all extraction tasks. + /// + /// The number of threads to use. + public void PerformExtraction(int numberOfThreads) + { + Parallel.Invoke( + new ParallelOptions { MaxDegreeOfParallelism = numberOfThreads }, + extractionTasks.ToArray()); + } + + public virtual void Dispose() + { + stopWatch.Stop(); + Logger.Log(Severity.Info, " Peak working set = {0} MB", Process.GetCurrentProcess().PeakWorkingSet64 / (1024 * 1024)); + + if (TotalErrors > 0) + Logger.Log(Severity.Info, "EXTRACTION FAILED with {0} error{1} in {2}", TotalErrors, TotalErrors == 1 ? "" : "s", stopWatch.Elapsed); + else + Logger.Log(Severity.Info, "EXTRACTION SUCCEEDED in {0}", stopWatch.Elapsed); + + Logger.Dispose(); + } + + /// + /// Number of errors encountered during extraction. + /// + private int ExtractorErrors => extractor?.Errors ?? 0; + + /// + /// Number of errors encountered by the compiler. + /// + public int CompilationErrors { get; set; } + + /// + /// Total number of errors reported. + /// + public int TotalErrors => CompilationErrors + ExtractorErrors; + + /// + /// Logs information about the extractor. + /// + public void LogExtractorInfo(string extractorVersion) + { + Logger.Log(Severity.Info, " Extractor: {0}", Environment.GetCommandLineArgs().First()); + Logger.Log(Severity.Info, " Extractor version: {0}", extractorVersion); + Logger.Log(Severity.Info, " Current working directory: {0}", Directory.GetCurrentDirectory()); + } + } +} diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/AnalysisAction.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/AnalysisAction.cs new file mode 100644 index 00000000000..12d28617ebd --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/AnalysisAction.cs @@ -0,0 +1,12 @@ +namespace Semmle.Extraction.CSharp +{ + /// + /// What action was performed when extracting a file. + /// + public enum AnalysisAction + { + Extracted, + UpToDate, + Excluded + } +} \ No newline at end of file diff --git a/csharp/extractor/Semmle.Extraction.CSharp/CompilerVersion.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/CompilerVersion.cs similarity index 79% rename from csharp/extractor/Semmle.Extraction.CSharp/CompilerVersion.cs rename to csharp/extractor/Semmle.Extraction.CSharp/Extractor/CompilerVersion.cs index f8da28affc4..8b91e367ff3 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/CompilerVersion.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/CompilerVersion.cs @@ -14,12 +14,12 @@ namespace Semmle.Extraction.CSharp public class CompilerVersion { private const string csc_rsp = "csc.rsp"; - private readonly string specifiedFramework = null; + private readonly string? specifiedFramework = null; /// /// The value specified by --compiler, or null. /// - public string SpecifiedCompiler + public string? SpecifiedCompiler { get; private set; @@ -28,12 +28,20 @@ namespace Semmle.Extraction.CSharp /// /// Why was the candidate exe rejected as a compiler? /// - public string SkipReason + public string? SkipReason { get; private set; } + private static readonly Dictionary knownCompilerNames = new Dictionary + { + { "csc.exe", "Microsoft" }, + { "csc2.exe", "Microsoft" }, + { "csc.dll", "Microsoft" }, + { "mcs.exe", "Novell" } + }; + /// /// Probes the compiler (if specified). /// @@ -43,7 +51,7 @@ namespace Semmle.Extraction.CSharp SpecifiedCompiler = options.CompilerName; specifiedFramework = options.Framework; - if (SpecifiedCompiler != null) + if (SpecifiedCompiler is not null) { if (!File.Exists(SpecifiedCompiler)) { @@ -52,30 +60,28 @@ namespace Semmle.Extraction.CSharp } // Reads the file details from the .exe - var versionInfo = FileVersionInfo.GetVersionInfo(SpecifiedCompiler); - var compilerDir = Path.GetDirectoryName(SpecifiedCompiler); - var knownCompilerNames = new Dictionary + if (compilerDir is null) { - { "csc.exe", "Microsoft" }, - { "csc2.exe", "Microsoft" }, - { "csc.dll", "Microsoft" }, - { "mcs.exe", "Novell" } - }; + SkipExtractionBecause("the compiler directory could not be retrieved"); + return; + } + var mscorlibExists = File.Exists(Path.Combine(compilerDir, "mscorlib.dll")); - if (specifiedFramework == null && mscorlibExists) + if (specifiedFramework is null && mscorlibExists) { specifiedFramework = compilerDir; } - if (!knownCompilerNames.TryGetValue(versionInfo.OriginalFilename, out var vendor)) + var versionInfo = FileVersionInfo.GetVersionInfo(SpecifiedCompiler); + if (!knownCompilerNames.TryGetValue(versionInfo.OriginalFilename ?? string.Empty, out var vendor)) { SkipExtractionBecause("the compiler name is not recognised"); return; } - if (versionInfo.LegalCopyright == null || !versionInfo.LegalCopyright.Contains(vendor)) + if (versionInfo.LegalCopyright is null || !versionInfo.LegalCopyright.Contains(vendor)) { SkipExtractionBecause($"the compiler isn't copyright {vendor}, but instead {versionInfo.LegalCopyright ?? ""}"); return; @@ -114,7 +120,7 @@ namespace Semmle.Extraction.CSharp /// /// Gets additional reference directories - the compiler directory. /// - public string AdditionalReferenceDirectories => SpecifiedCompiler != null ? Path.GetDirectoryName(SpecifiedCompiler) : null; + public string? AdditionalReferenceDirectories => SpecifiedCompiler is not null ? Path.GetDirectoryName(SpecifiedCompiler) : null; /// /// Adds @csc.rsp to the argument list to mimic csc.exe. @@ -134,6 +140,6 @@ namespace Semmle.Extraction.CSharp return args.Any(arg => new[] { "/noconfig", "-noconfig" }.Contains(arg.ToLowerInvariant())); } - public IEnumerable ArgsWithResponse { get; } + public IEnumerable ArgsWithResponse { get; } = Enumerable.Empty(); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Context.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Context.cs new file mode 100644 index 00000000000..cc389070149 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Context.cs @@ -0,0 +1,144 @@ +using Microsoft.CodeAnalysis; +using System; +using System.Diagnostics.CodeAnalysis; +using Semmle.Extraction.Entities; +using System.Collections.Generic; + +namespace Semmle.Extraction.CSharp +{ + /// + /// State that needs to be available throughout the extraction process. + /// There is one Context object per trap output file. + /// + internal class Context : Extraction.Context + { + /// + /// The program database provided by Roslyn. + /// There's one per syntax tree, which makes things awkward. + /// + public SemanticModel GetModel(SyntaxNode node) + { + // todo: when this context belongs to a SourceScope, the syntax tree can be retrieved from the scope, and + // the node parameter could be removed. Is there any case when we pass in a node that's not from the current + // tree? + if (cachedModel is null || node.SyntaxTree != cachedModel.SyntaxTree) + { + cachedModel = Compilation.GetSemanticModel(node.SyntaxTree); + } + + return cachedModel; + } + + private SemanticModel? cachedModel; + + /// + /// The current compilation unit. + /// + public Compilation Compilation { get; } + + internal CommentProcessor CommentGenerator { get; } = new CommentProcessor(); + + public Context(Extraction.Extractor e, Compilation c, TrapWriter trapWriter, IExtractionScope scope, bool addAssemblyTrapPrefix) + : base(e, trapWriter, addAssemblyTrapPrefix) + { + Compilation = c; + this.scope = scope; + } + + public bool FromSource => scope is SourceScope; + + private readonly IExtractionScope scope; + + public bool IsAssemblyScope => scope is AssemblyScope; + + private SyntaxTree? SourceTree => scope is SourceScope sc ? sc.SourceTree : null; + + /// + /// Whether the given symbol needs to be defined in this context. + /// This is the case if the symbol is contained in the source/assembly, or + /// of the symbol is a constructed generic. + /// + /// The symbol to populate. + public bool Defines(ISymbol symbol) => + !SymbolEqualityComparer.Default.Equals(symbol, symbol.OriginalDefinition) || + scope.InScope(symbol); + + public override void WithDuplicationGuard(Key key, Action a) + { + if (IsAssemblyScope) + { + // No need for a duplication guard when extracting assemblies, + // and the duplication guard could lead to method bodies being missed + // depending on trap import order. + a(); + } + else + { + base.WithDuplicationGuard(key, a); + } + } + + public override Extraction.Entities.Location CreateLocation() + { + return SourceTree is null + ? GeneratedLocation.Create(this) + : CreateLocation(Microsoft.CodeAnalysis.Location.Create(SourceTree, Microsoft.CodeAnalysis.Text.TextSpan.FromBounds(0, 0))); + } + + public override Extraction.Entities.Location CreateLocation(Microsoft.CodeAnalysis.Location? location) + { + return (location is null || location.Kind == LocationKind.None) + ? GeneratedLocation.Create(this) + : location.IsInSource + ? Entities.NonGeneratedSourceLocation.Create(this, location) + : Entities.Assembly.Create(this, location); + } + + /// + /// Register a program entity which can be bound to comments. + /// + /// Extractor context. + /// Program entity. + /// Location of the entity. + public void BindComments(Entity entity, Microsoft.CodeAnalysis.Location? l) + { + var duplicationGuardKey = GetCurrentTagStackKey(); + CommentGenerator.AddElement(entity.Label, duplicationGuardKey, l); + } + + protected override bool IsEntityDuplicationGuarded(IEntity entity, [NotNullWhen(true)] out Extraction.Entities.Location? loc) + { + if (CreateLocation(entity.ReportingLocation) is Entities.NonGeneratedSourceLocation l) + { + loc = l; + return true; + } + + loc = null; + return false; + } + + private readonly HashSet - private static IEnumerable ReadSyntaxTrees(IEnumerable sources, Analyser analyser, CSharpParseOptions parseOptions, Encoding encoding, IList ret) + private static IEnumerable ReadSyntaxTrees(IEnumerable sources, Analyser analyser, CSharpParseOptions? parseOptions, Encoding? encoding, IList ret) { return sources.Select(path => () => { @@ -318,68 +248,16 @@ namespace Semmle.Extraction.CSharp ILogger logger, CommonOptions options) { + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var canonicalPathCache = CanonicalPathCache.Create(logger, 1000); var pathTransformer = new PathTransformer(canonicalPathCache); - using var analyser = new Analyser(pm, logger, false, pathTransformer); - using var references = new BlockingCollection(); + using var analyser = new StandaloneAnalyser(pm, logger, false, pathTransformer); try { - var referenceTasks = referencePaths.Select(path => () => - { - var reference = MetadataReference.CreateFromFile(path); - references.Add(reference); - }); - - var syntaxTrees = new List(); - var syntaxTreeTasks = ReadSyntaxTrees(sources, analyser, null, null, syntaxTrees); - - var sw = new Stopwatch(); - sw.Start(); - - Parallel.Invoke( - new ParallelOptions { MaxDegreeOfParallelism = options.Threads }, - referenceTasks.Interleave(syntaxTreeTasks).ToArray()); - - if (syntaxTrees.Count == 0) - { - analyser.Logger.Log(Severity.Error, " No source files"); - ++analyser.CompilationErrors; - } - - var compilation = CSharpCompilation.Create( - "csharp.dll", - syntaxTrees, - references - ); - - analyser.InitializeStandalone(compilation, options); - analyser.AnalyseReferences(); - - foreach (var tree in compilation.SyntaxTrees) - { - analyser.AnalyseTree(tree); - } - - sw.Stop(); - analyser.Logger.Log(Severity.Info, " Models constructed in {0}", sw.Elapsed); - - sw.Restart(); - analyser.PerformExtraction(options.Threads); - sw.Stop(); - analyser.Logger.Log(Severity.Info, " Extraction took {0}", sw.Elapsed); - - foreach (var type in analyser.MissingNamespaces) - { - pm.MissingNamespace(type); - } - - foreach (var type in analyser.MissingTypes) - { - pm.MissingType(type); - } - - pm.MissingSummary(analyser.MissingTypes.Count(), analyser.MissingNamespaces.Count()); + AnalyseStandalone(analyser, sources, referencePaths, options, pm, stopwatch); } catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] { @@ -387,6 +265,160 @@ namespace Semmle.Extraction.CSharp } } + private static ExitCode Analyse(Stopwatch stopwatch, Analyser analyser, CommonOptions options, + Func, IEnumerable> getResolvedReferenceTasks, + Func, IEnumerable> getSyntaxTreeTasks, + Func, IEnumerable, CSharpCompilation> getCompilation, + Action initializeAnalyser, + Action analyseCompilation, + Action logPerformance, + Action postProcess) + { + using var references = new BlockingCollection(); + var referenceTasks = getResolvedReferenceTasks(references); + + var syntaxTrees = new List(); + var syntaxTreeTasks = getSyntaxTreeTasks(analyser, syntaxTrees); + + var sw = new Stopwatch(); + sw.Start(); + + Parallel.Invoke( + new ParallelOptions { MaxDegreeOfParallelism = options.Threads }, + referenceTasks.Interleave(syntaxTreeTasks).ToArray()); + + if (syntaxTrees.Count == 0) + { + analyser.Logger.Log(Severity.Error, " No source files"); + ++analyser.CompilationErrors; + if (analyser is TracingAnalyser) + { + return ExitCode.Failed; + } + } + + var compilation = getCompilation(syntaxTrees, references); + + initializeAnalyser(compilation, options); + analyseCompilation(); + analyser.AnalyseReferences(); + + foreach (var tree in compilation.SyntaxTrees) + { + analyser.AnalyseTree(tree); + } + + sw.Stop(); + analyser.Logger.Log(Severity.Info, " Models constructed in {0}", sw.Elapsed); + var elapsed = sw.Elapsed; + + var currentProcess = Process.GetCurrentProcess(); + var cpuTime1 = currentProcess.TotalProcessorTime; + var userTime1 = currentProcess.UserProcessorTime; + + sw.Restart(); + analyser.PerformExtraction(options.Threads); + sw.Stop(); + var cpuTime2 = currentProcess.TotalProcessorTime; + var userTime2 = currentProcess.UserProcessorTime; + + var performance = new Entities.PerformanceMetrics() + { + Frontend = new Entities.Timings() { Elapsed = elapsed, Cpu = cpuTime1, User = userTime1 }, + Extractor = new Entities.Timings() { Elapsed = sw.Elapsed, Cpu = cpuTime2 - cpuTime1, User = userTime2 - userTime1 }, + Total = new Entities.Timings() { Elapsed = stopwatch.Elapsed, Cpu = cpuTime2, User = userTime2 }, + PeakWorkingSet = currentProcess.PeakWorkingSet64 + }; + + logPerformance(performance); + analyser.Logger.Log(Severity.Info, " Extraction took {0}", sw.Elapsed); + + postProcess(); + + return analyser.TotalErrors == 0 ? ExitCode.Ok : ExitCode.Errors; + } + + private static void AnalyseStandalone( + StandaloneAnalyser analyser, + IEnumerable sources, + IEnumerable referencePaths, + CommonOptions options, + IProgressMonitor progressMonitor, + Stopwatch stopwatch) + { + Analyse(stopwatch, analyser, options, + references => GetResolvedReferencesStandalone(referencePaths, references), + (analyser, syntaxTrees) => ReadSyntaxTrees(sources, analyser, null, null, syntaxTrees), + (syntaxTrees, references) => CSharpCompilation.Create("csharp.dll", syntaxTrees, references), + (compilation, options) => analyser.InitializeStandalone(compilation, options), + () => { }, + _ => { }, + () => + { + foreach (var type in analyser.MissingNamespaces) + { + progressMonitor.MissingNamespace(type); + } + + foreach (var type in analyser.MissingTypes) + { + progressMonitor.MissingType(type); + } + + progressMonitor.MissingSummary(analyser.MissingTypes.Count(), analyser.MissingNamespaces.Count()); + }); + } + + private static ExitCode AnalyseTracing( + TracingAnalyser analyser, + CSharpCommandLineArguments compilerArguments, + Options options, + CanonicalPathCache canonicalPathCache, + Stopwatch stopwatch) + { + return Analyse(stopwatch, analyser, options, + references => ResolveReferences(compilerArguments, analyser, canonicalPathCache, references), + (analyser, syntaxTrees) => + { + return ReadSyntaxTrees( + compilerArguments.SourceFiles.Select(src => canonicalPathCache.GetCanonicalPath(src.Path)), + analyser, + compilerArguments.ParseOptions, + compilerArguments.Encoding, + syntaxTrees); + }, + (syntaxTrees, references) => + { + // csc.exe (CSharpCompiler.cs) also provides CompilationOptions + // .WithMetadataReferenceResolver(), + // .WithXmlReferenceResolver() and + // .WithSourceReferenceResolver(). + // These would be needed if we hadn't explicitly provided the source/references + // already. + return CSharpCompilation.Create( + compilerArguments.CompilationName, + syntaxTrees, + references, + compilerArguments.CompilationOptions. + WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default). + WithStrongNameProvider(new DesktopStrongNameProvider(compilerArguments.KeyFileSearchPaths)) + ); + }, + (compilation, options) => analyser.EndInitialize(compilerArguments, options, compilation), + () => analyser.AnalyseCompilation(), + performance => analyser.LogPerformance(performance), + () => { }); + } + + private static IEnumerable GetResolvedReferencesStandalone(IEnumerable referencePaths, BlockingCollection references) + { + return referencePaths.Select(path => () => + { + var reference = MetadataReference.CreateFromFile(path); + references.Add(reference); + }); + } + /// /// Gets the path to the `csharp.log` file written to by the C# extractor. /// diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/IProgressMonitor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/IProgressMonitor.cs new file mode 100644 index 00000000000..8f0fdb1adc0 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/IProgressMonitor.cs @@ -0,0 +1,44 @@ +using System; + +namespace Semmle.Extraction.CSharp +{ + /// + /// Callback for various extraction events. + /// (Used for display of progress). + /// + public interface IProgressMonitor + { + /// + /// Callback that a particular item has been analysed. + /// + /// The item number being processed. + /// The total number of items to process. + /// The name of the item, e.g. a source file. + /// The name of the item being output, e.g. a trap file. + /// The time to extract the item. + /// What action was taken for the file. + void Analysed(int item, int total, string source, string output, TimeSpan time, AnalysisAction action); + + /// + /// A "using namespace" directive was seen but the given + /// namespace could not be found. + /// Only called once for each @namespace. + /// + /// + void MissingNamespace(string @namespace); + + /// + /// An ErrorType was found. + /// Called once for each type name. + /// + /// The full/partial name of the type. + void MissingType(string type); + + /// + /// Report a summary of missing entities. + /// + /// The number of missing types. + /// The number of missing using namespace declarations. + void MissingSummary(int types, int namespaces); + } +} diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Options.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Options.cs similarity index 96% rename from csharp/extractor/Semmle.Extraction.CSharp/Options.cs rename to csharp/extractor/Semmle.Extraction.CSharp/Extractor/Options.cs index e20d4e846d4..981f3d32149 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Options.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Options.cs @@ -9,12 +9,12 @@ namespace Semmle.Extraction.CSharp /// /// The compiler exe, or null if unspecified. /// - public string CompilerName { get; set; } + public string? CompilerName { get; set; } /// /// Specified .Net Framework dir, or null if unspecified. /// - public string Framework { get; set; } + public string? Framework { get; set; } /// /// All other arguments passed to the compilation. diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/StandaloneAnalyser.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/StandaloneAnalyser.cs new file mode 100644 index 00000000000..23661234f98 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/StandaloneAnalyser.cs @@ -0,0 +1,32 @@ +using Microsoft.CodeAnalysis.CSharp; +using System.Collections.Generic; +using Semmle.Util.Logging; + +namespace Semmle.Extraction.CSharp +{ + public class StandaloneAnalyser : Analyser + { + public StandaloneAnalyser(IProgressMonitor pm, ILogger logger, bool addAssemblyTrapPrefix, PathTransformer pathTransformer) + : base(pm, logger, addAssemblyTrapPrefix, pathTransformer) + { + } + + public void InitializeStandalone(CSharpCompilation compilationIn, CommonOptions options) + { + compilation = compilationIn; + layout = new Layout(); + extractor = new StandaloneExtractor(Logger, PathTransformer); + this.options = options; + LogExtractorInfo(Extraction.Extractor.Version); + SetReferencePaths(); + } + +#nullable disable warnings + + public IEnumerable MissingTypes => extractor.MissingTypes; + + public IEnumerable MissingNamespaces => extractor.MissingNamespaces; + +#nullable restore warnings + } +} diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/TracingAnalyser.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/TracingAnalyser.cs new file mode 100644 index 00000000000..133148723fa --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/TracingAnalyser.cs @@ -0,0 +1,222 @@ +using System; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using System.IO; +using System.Linq; +using System.Collections.Generic; +using Semmle.Util.Logging; +using Semmle.Util; + + +namespace Semmle.Extraction.CSharp +{ + public class TracingAnalyser : Analyser, IDisposable + { + private Entities.Compilation? compilationEntity; + private IDisposable? compilationTrapFile; + + private bool init; + + public TracingAnalyser(IProgressMonitor pm, ILogger logger, bool addAssemblyTrapPrefix, PathTransformer pathTransformer) + : base(pm, logger, addAssemblyTrapPrefix, pathTransformer) + { + } + + /// + /// Start initialization of the analyser. + /// + /// The arguments passed to Roslyn. + /// A Boolean indicating whether to proceed with extraction. + public bool BeginInitialize(IEnumerable roslynArgs) + { + return init = LogRoslynArgs(roslynArgs, Extraction.Extractor.Version); + } + + /// + /// End initialization of the analyser. + /// + /// Arguments passed to csc. + /// Extractor options. + /// The Roslyn compilation. + /// A Boolean indicating whether to proceed with extraction. + public void EndInitialize( + CSharpCommandLineArguments commandLineArguments, + CommonOptions options, + CSharpCompilation compilation) + { + if (!init) + throw new InternalError("EndInitialize called without BeginInitialize returning true"); + this.layout = new Layout(); + this.options = options; + this.compilation = compilation; + this.extractor = new TracingExtractor(GetOutputName(compilation, commandLineArguments), Logger, PathTransformer); + LogDiagnostics(); + + SetReferencePaths(); + + CompilationErrors += FilteredDiagnostics.Count(); + } + + public override void Dispose() + { + compilationTrapFile?.Dispose(); + base.Dispose(); + } + + /// + /// Extracts compilation-wide entities, such as compilations and compiler diagnostics. + /// + public void AnalyseCompilation() + { + extractionTasks.Add(() => DoAnalyseCompilation()); + } + + /// + /// Logs information about the extractor, as well as the arguments to Roslyn. + /// + /// The arguments passed to Roslyn. + /// A Boolean indicating whether the same arguments have been logged previously. + private bool LogRoslynArgs(IEnumerable roslynArgs, string extractorVersion) + { + LogExtractorInfo(extractorVersion); + Logger.Log(Severity.Info, $" Arguments to Roslyn: {string.Join(' ', roslynArgs)}"); + + var tempFile = Extractor.GetCSharpArgsLogPath(Path.GetRandomFileName()); + + bool argsWritten; + using (var streamWriter = new StreamWriter(new FileStream(tempFile, FileMode.Append, FileAccess.Write))) + { + streamWriter.WriteLine($"# Arguments to Roslyn: {string.Join(' ', roslynArgs.Where(arg => !arg.StartsWith('@')))}"); + argsWritten = roslynArgs.WriteCommandLine(streamWriter); + } + + var hash = FileUtils.ComputeFileHash(tempFile); + var argsFile = Extractor.GetCSharpArgsLogPath(hash); + + if (argsWritten) + Logger.Log(Severity.Info, $" Arguments have been written to {argsFile}"); + + if (File.Exists(argsFile)) + { + try + { + File.Delete(tempFile); + } + catch (IOException e) + { + Logger.Log(Severity.Warning, $" Failed to remove {tempFile}: {e.Message}"); + } + return false; + } + + try + { + File.Move(tempFile, argsFile); + } + catch (IOException e) + { + Logger.Log(Severity.Warning, $" Failed to move {tempFile} to {argsFile}: {e.Message}"); + } + + return true; + } + + /// + /// Determine the path of the output dll/exe. + /// + /// Information about the compilation. + /// Cancellation token required. + /// The filename. + private static string GetOutputName(CSharpCompilation compilation, + CSharpCommandLineArguments commandLineArguments) + { + // There's no apparent way to access the output filename from the compilation, + // so we need to re-parse the command line arguments. + + if (commandLineArguments.OutputFileName is null) + { + // No output specified: Use name based on first filename + var entry = compilation.GetEntryPoint(System.Threading.CancellationToken.None); + if (entry is null) + { + if (compilation.SyntaxTrees.Length == 0) + throw new InvalidOperationException("No source files seen"); + + // Probably invalid, but have a go anyway. + var entryPointFile = compilation.SyntaxTrees.First().FilePath; + return Path.ChangeExtension(entryPointFile, ".exe"); + } + + var entryPointFilename = entry.Locations.First().SourceTree!.FilePath; + return Path.ChangeExtension(entryPointFilename, ".exe"); + } + + return Path.Combine(commandLineArguments.OutputDirectory, commandLineArguments.OutputFileName); + } + +#nullable disable warnings + + /// + /// Logs detailed information about this invocation, + /// in the event that errors were detected. + /// + /// A Boolean indicating whether to proceed with extraction. + private void LogDiagnostics() + { + foreach (var error in FilteredDiagnostics) + { + Logger.Log(Severity.Error, " Compilation error: {0}", error); + } + + if (FilteredDiagnostics.Any()) + { + foreach (var reference in compilation.References) + { + Logger.Log(Severity.Info, " Resolved reference {0}", reference.Display); + } + } + } + + private static readonly HashSet errorsToIgnore = new HashSet + { + "CS7027", // Code signing failure + "CS1589", // XML referencing not supported + "CS1569" // Error writing XML documentation + }; + + private IEnumerable FilteredDiagnostics + { + get + { + return extractor is null || extractor.Standalone || compilation is null ? Enumerable.Empty() : + compilation. + GetDiagnostics(). + Where(e => e.Severity >= DiagnosticSeverity.Error && !errorsToIgnore.Contains(e.Id)); + } + } + + private void DoAnalyseCompilation() + { + try + { + var assemblyPath = ((TracingExtractor?)extractor).OutputPath; + var transformedAssemblyPath = PathTransformer.Transform(assemblyPath); + var assembly = compilation.Assembly; + var projectLayout = layout.LookupProjectOrDefault(transformedAssemblyPath); + var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedAssemblyPath, options.TrapCompression, discardDuplicates: false); + compilationTrapFile = trapWriter; // Dispose later + var cx = new Context(extractor, compilation.Clone(), trapWriter, new AssemblyScope(assembly, assemblyPath), addAssemblyTrapPrefix); + + compilationEntity = Entities.Compilation.Create(cx); + } + catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] + { + Logger.Log(Severity.Error, " Unhandled exception analyzing {0}: {1}", "compilation", ex); + } + } + + public void LogPerformance(Entities.PerformanceMetrics p) => compilationEntity.PopulatePerformance(p); + +#nullable restore warnings + } +} diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Populators/AstLineCounter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Populators/AstLineCounter.cs index a185675ee27..2af399744ee 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Populators/AstLineCounter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Populators/AstLineCounter.cs @@ -15,7 +15,7 @@ namespace Semmle.Extraction.CSharp.Populators public override LineCounts VisitMethodDeclaration(MethodDeclarationSyntax method) { - return Visit(method.Identifier, method.Body ?? (SyntaxNode)method.ExpressionBody); + return Visit(method.Identifier, GetBody(method)); } public static LineCounts Visit(SyntaxToken identifier, SyntaxNode body) @@ -31,17 +31,22 @@ namespace Semmle.Extraction.CSharp.Populators public override LineCounts VisitConstructorDeclaration(ConstructorDeclarationSyntax method) { - return Visit(method.Identifier, (SyntaxNode)method.Body ?? method.ExpressionBody); + return Visit(method.Identifier, GetBody(method)); } public override LineCounts VisitDestructorDeclaration(DestructorDeclarationSyntax method) { - return Visit(method.Identifier, (SyntaxNode)method.Body ?? method.ExpressionBody); + return Visit(method.Identifier, GetBody(method)); } public override LineCounts VisitOperatorDeclaration(OperatorDeclarationSyntax node) { - return Visit(node.OperatorToken, node.Body ?? (SyntaxNode)node.ExpressionBody); + return Visit(node.OperatorToken, GetBody(node)); + } + + private static SyntaxNode GetBody(BaseMethodDeclarationSyntax node) + { + return node.Body ?? (SyntaxNode)node.ExpressionBody!; } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Populators/CommentPopulator.cs b/csharp/extractor/Semmle.Extraction.CSharp/Populators/CommentPopulator.cs index 325746274ff..03261416508 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Populators/CommentPopulator.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Populators/CommentPopulator.cs @@ -1,6 +1,5 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Semmle.Extraction.CommentProcessing; using Semmle.Extraction.CSharp.Entities; using System; @@ -9,9 +8,9 @@ namespace Semmle.Extraction.CSharp.Populators /// /// Populators for comments. /// - public static class CommentPopulator + internal static class CommentPopulator { - public static void ExtractCommentBlocks(Context cx, ICommentGenerator gen) + public static void ExtractCommentBlocks(Context cx, CommentProcessor gen) { cx.Try(null, null, () => { @@ -25,7 +24,7 @@ namespace Semmle.Extraction.CSharp.Populators // When the duplication guard key exists, it means that the entity is guarded against // trap duplication (). // We must therefore also guard comment construction. - if (duplicationGuardKey != null) + if (duplicationGuardKey is not null) cx.WithDuplicationGuard(duplicationGuardKey, a); else a(); @@ -64,7 +63,7 @@ namespace Semmle.Extraction.CSharp.Populators trimmedLine = trimmedLine.Trim(); var span = Microsoft.CodeAnalysis.Text.TextSpan.FromBounds(currentLocation, currentLocation + fullLine.Length); - var location = Microsoft.CodeAnalysis.Location.Create(trivia.SyntaxTree, span); + var location = Microsoft.CodeAnalysis.Location.Create(trivia.SyntaxTree!, span); var commentType = CommentLineType.XmlDoc; cx.CommentGenerator.AddComment(CommentLine.Create(cx, location, commentType, trimmedLine, fullLine)); } @@ -111,7 +110,7 @@ namespace Semmle.Extraction.CSharp.Populators trimmedLine = trimmedLine.Trim(); var span = Microsoft.CodeAnalysis.Text.TextSpan.FromBounds(currentLocation, currentLocation + fullLine.Length); - var location = Microsoft.CodeAnalysis.Location.Create(trivia.SyntaxTree, span); + var location = Microsoft.CodeAnalysis.Location.Create(trivia.SyntaxTree!, span); var commentType = line == 0 ? CommentLineType.Multiline : CommentLineType.MultilineContinuation; cx.CommentGenerator.AddComment(CommentLine.Create(cx, location, commentType, trimmedLine, fullLine)); currentLocation = nextLineLocation; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Populators/CompilationUnitVisitor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Populators/CompilationUnitVisitor.cs index 58e5404ccd7..6116c3511a1 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Populators/CompilationUnitVisitor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Populators/CompilationUnitVisitor.cs @@ -10,8 +10,10 @@ namespace Semmle.Extraction.CSharp.Populators { internal class CompilationUnitVisitor : TypeOrNamespaceVisitor { +#nullable disable warnings public CompilationUnitVisitor(Context cx) : base(cx, cx.TrapWriter.Writer, null) { } +#nullable restore warnings public override void VisitExternAliasDirective(ExternAliasDirectiveSyntax node) { @@ -57,13 +59,13 @@ namespace Semmle.Extraction.CSharp.Populators return; } - var entryPoint = Cx.Compilation.GetEntryPoint(System.Threading.CancellationToken.None); + var entryPoint = Cx.Compilation.GetEntryPoint(System.Threading.CancellationToken.None)!; var entryMethod = Method.Create(Cx, entryPoint); var block = GlobalStatementsBlock.Create(Cx, entryMethod); for (var i = 0; i < globalStatements.Count; i++) { - if (globalStatements[i].Statement is object) + if (globalStatements[i].Statement is not null) { Statement.Create(Cx, globalStatements[i].Statement, block, i); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Populators/Locations.cs b/csharp/extractor/Semmle.Extraction.CSharp/Populators/Locations.cs index 22f6c8ec6a9..55da4bdcfb2 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Populators/Locations.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Populators/Locations.cs @@ -15,7 +15,7 @@ namespace Semmle.Extraction.CSharp.Populators /// Extended location. public static Location ExtendLocation(this Location l1, SyntaxNode n2) { - if (n2 == null) + if (n2 is null) { return l1; } @@ -58,21 +58,21 @@ namespace Semmle.Extraction.CSharp.Populators case SyntaxKind.DelegateDeclaration: { var decl = (DelegateDeclarationSyntax)node; - return decl.Identifier.GetLocation().ExtendLocation(decl.TypeParameterList); + return decl.Identifier.GetLocation().ExtendLocation(decl.TypeParameterList!); } case SyntaxKind.ClassDeclaration: case SyntaxKind.StructDeclaration: case SyntaxKind.InterfaceDeclaration: { var decl = (TypeDeclarationSyntax)node; - return decl.Identifier.GetLocation().ExtendLocation(decl.TypeParameterList); + return decl.Identifier.GetLocation().ExtendLocation(decl.TypeParameterList!); } case SyntaxKind.EnumDeclaration: return ((EnumDeclarationSyntax)node).Identifier.GetLocation(); case SyntaxKind.MethodDeclaration: { var decl = (MethodDeclarationSyntax)node; - return decl.Identifier.GetLocation().ExtendLocation(decl.TypeParameterList); + return decl.Identifier.GetLocation().ExtendLocation(decl.TypeParameterList!); } case SyntaxKind.ConstructorDeclaration: { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Populators/TypeContainerVisitor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Populators/TypeContainerVisitor.cs index ededf2978b9..f49a57398ea 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Populators/TypeContainerVisitor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Populators/TypeContainerVisitor.cs @@ -1,7 +1,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Semmle.Extraction.Entities; +using Semmle.Extraction.CSharp.Entities; using System; using System.Collections.Generic; using System.IO; @@ -9,12 +9,12 @@ using System.Linq; namespace Semmle.Extraction.CSharp.Populators { - public class TypeContainerVisitor : CSharpSyntaxVisitor + internal class TypeContainerVisitor : CSharpSyntaxVisitor { protected Context Cx { get; } protected IEntity Parent { get; } protected TextWriter TrapFile { get; } - private readonly Lazy> attributeLookup; + private readonly Lazy> attributeLookup; public TypeContainerVisitor(Context cx, TextWriter trapFile, IEntity parent) { @@ -22,9 +22,9 @@ namespace Semmle.Extraction.CSharp.Populators Parent = parent; TrapFile = trapFile; - attributeLookup = new Lazy>(() => + attributeLookup = new Lazy>(() => { - var dict = new Dictionary(); + var dict = new Dictionary(); foreach (var attributeData in cx.Compilation.Assembly.GetAttributes().Concat(cx.Compilation.Assembly.Modules.SelectMany(m => m.GetAttributes()))) { if (attributeData.ApplicationSyntaxReference?.GetSyntax() is SyntaxNode syntax) @@ -47,7 +47,7 @@ namespace Semmle.Extraction.CSharp.Populators public override void VisitDelegateDeclaration(DelegateDeclarationSyntax node) { - Entities.NamedType.Create(Cx, Cx.GetModel(node).GetDeclaredSymbol(node)).ExtractRecursive(TrapFile, Parent); + Entities.NamedType.Create(Cx, Cx.GetModel(node).GetDeclaredSymbol(node)!).ExtractRecursive(TrapFile, Parent); } public override void VisitRecordDeclaration(RecordDeclarationSyntax node) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Populators/TypeOrNamespaceVisitor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Populators/TypeOrNamespaceVisitor.cs index a3daae08a60..adebc76a0ce 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Populators/TypeOrNamespaceVisitor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Populators/TypeOrNamespaceVisitor.cs @@ -12,7 +12,7 @@ namespace Semmle.Extraction.CSharp.Populators public override void VisitUsingDirective(UsingDirectiveSyntax usingDirective) { // Only deal with "using namespace" not "using X = Y" - if (usingDirective.Alias == null) + if (usingDirective.Alias is null) new UsingDirective(Cx, usingDirective, (NamespaceDeclaration)Parent); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Semmle.Extraction.CSharp.csproj b/csharp/extractor/Semmle.Extraction.CSharp/Semmle.Extraction.CSharp.csproj index fab4da0605b..f07ec48d80a 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Semmle.Extraction.CSharp.csproj +++ b/csharp/extractor/Semmle.Extraction.CSharp/Semmle.Extraction.CSharp.csproj @@ -1,12 +1,14 @@ - netcoreapp3.1 + net5.0 Semmle.Extraction.CSharp Semmle.Extraction.CSharp false true win-x64;linux-x64;osx-x64 + win-x64;linux-x64;osx-x64 + enable @@ -20,7 +22,7 @@ - + diff --git a/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs index 77e27a00be5..f03f1674969 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs @@ -2,6 +2,7 @@ using Microsoft.CodeAnalysis; using Semmle.Extraction.CSharp.Entities; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; @@ -15,10 +16,10 @@ namespace Semmle.Extraction.CSharp /// public struct AnnotatedTypeSymbol { - public ITypeSymbol Symbol { get; set; } + public ITypeSymbol? Symbol { get; set; } public NullableAnnotation Nullability { get; } - public AnnotatedTypeSymbol(ITypeSymbol symbol, NullableAnnotation nullability) + public AnnotatedTypeSymbol(ITypeSymbol? symbol, NullableAnnotation nullability) { Symbol = symbol; Nullability = nullability; @@ -36,7 +37,7 @@ namespace Semmle.Extraction.CSharp /// /// The type to disambiguate. /// - public static ITypeSymbol DisambiguateType(this ITypeSymbol type) + public static ITypeSymbol? DisambiguateType(this ITypeSymbol? type) { /* A type could not be determined. * Sometimes this happens due to a missing reference, @@ -49,7 +50,6 @@ namespace Semmle.Extraction.CSharp * The conservative option would be to resolve all error types as null. */ - return type is IErrorTypeSymbol errorType && errorType.CandidateSymbols.Any() ? errorType.CandidateSymbols.First() as ITypeSymbol : type; @@ -93,7 +93,7 @@ namespace Semmle.Extraction.CSharp { var seen = new HashSet(SymbolEqualityComparer.Default); - bool IdDependsOnImpl(ITypeSymbol type) + bool IdDependsOnImpl(ITypeSymbol? type) { if (SymbolEqualityComparer.Default.Equals(type, symbol)) return true; @@ -117,7 +117,7 @@ namespace Semmle.Extraction.CSharp case TypeKind.Delegate: case TypeKind.Error: var named = (INamedTypeSymbol)type; - if (named.IsTupleType && named.TupleUnderlyingType is object) + if (named.IsTupleType && named.TupleUnderlyingType is not null) named = named.TupleUnderlyingType; if (IdDependsOnImpl(named.ContainingType)) return true; @@ -207,8 +207,14 @@ namespace Semmle.Extraction.CSharp } } - private static void BuildOrWriteId(this ISymbol symbol, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType = false) + private static void BuildOrWriteId(this ISymbol? symbol, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType = false) { + if (symbol is null) + { + cx.ModelError(symbolBeingDefined, "Missing symbol. Couldn't build some part of the ID."); + return; + } + // We need to keep track of the symbol being defined in order to avoid cyclic labels. // For example, in // @@ -243,7 +249,7 @@ namespace Semmle.Extraction.CSharp /// it will generate an appropriate ID that encodes the signature of /// . /// - public static void BuildOrWriteId(this ISymbol symbol, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined) => + public static void BuildOrWriteId(this ISymbol? symbol, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined) => symbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, true); /// @@ -289,7 +295,7 @@ namespace Semmle.Extraction.CSharp trapFile.BuildList(",", named.TupleElements, (f, tb0) => { - trapFile.Write(f.Name); + trapFile.Write((f.CorrespondingTupleField ?? f).Name); trapFile.Write(":"); f.Type.BuildOrWriteId(cx, tb0, symbolBeingDefined, addBaseClass); } @@ -300,14 +306,14 @@ namespace Semmle.Extraction.CSharp void AddContaining() { - if (named.ContainingType != null) + if (named.ContainingType is not null) { named.ContainingType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass); trapFile.Write('.'); } - else if (named.ContainingNamespace != null) + else if (named.ContainingNamespace is not null) { - if (cx.ShouldAddAssemblyTrapPrefix && named.ContainingAssembly is object) + if (cx.ShouldAddAssemblyTrapPrefix && named.ContainingAssembly is not null) BuildAssembly(named.ContainingAssembly, trapFile); named.ContainingNamespace.BuildNamespace(cx, trapFile); } @@ -588,7 +594,7 @@ namespace Semmle.Extraction.CSharp /// Holds if this method is a source declaration. /// public static bool IsSourceDeclaration(this IMethodSymbol method) => - IsSourceDeclaration((ISymbol)method) && SymbolEqualityComparer.Default.Equals(method, method.ConstructedFrom) && method.ReducedFrom == null; + IsSourceDeclaration((ISymbol)method) && SymbolEqualityComparer.Default.Equals(method, method.ConstructedFrom) && method.ReducedFrom is null; /// /// Holds if this parameter is a source declaration. @@ -606,24 +612,34 @@ namespace Semmle.Extraction.CSharp /// Gets the base type of `symbol`. Unlike `symbol.BaseType`, this excludes effective base /// types of type parameters as well as `object` base types. /// - public static INamedTypeSymbol GetNonObjectBaseType(this ITypeSymbol symbol, Context cx) => + public static INamedTypeSymbol? GetNonObjectBaseType(this ITypeSymbol symbol, Context cx) => symbol is ITypeParameterSymbol || SymbolEqualityComparer.Default.Equals(symbol.BaseType, cx.Compilation.ObjectType) ? null : symbol.BaseType; - public static IEntity CreateEntity(this Context cx, ISymbol symbol) + [return: NotNullIfNotNull("symbol")] + public static IEntity? CreateEntity(this Context cx, ISymbol symbol) { - if (symbol == null) + if (symbol is null) return null; using (cx.StackGuard) { try { - return symbol.Accept(new Populators.Symbols(cx)); + var entity = symbol.Accept(new Populators.Symbols(cx)); + if (entity is null) + { + cx.ModelError(symbol, $"Symbol visitor returned null entity on symbol: {symbol}"); + } +#nullable disable warnings + return entity; +#nullable restore warnings } catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] { cx.ModelError(symbol, $"Exception processing symbol '{symbol.Kind}' of type '{ex}': {symbol}"); +#nullable disable warnings return null; +#nullable restore warnings } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Tuples.cs b/csharp/extractor/Semmle.Extraction.CSharp/Tuples.cs index df39d6a836c..72beb7b6cde 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Tuples.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Tuples.cs @@ -1,5 +1,4 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; -using Semmle.Extraction.CommentProcessing; using Semmle.Extraction.CSharp.Entities; using Semmle.Extraction.CSharp.Entities.Expressions; using Semmle.Extraction.Entities; @@ -18,6 +17,11 @@ namespace Semmle.Extraction.CSharp /// internal static class Tuples { + internal static void assemblies(this System.IO.TextWriter trapFile, Assembly assembly, Extraction.Entities.File file, string identifier, string name, string version) + { + trapFile.WriteTuple("assemblies", assembly, file, identifier, name, version); + } + internal static void accessor_location(this TextWriter trapFile, Accessor accessorKey, Location location) { trapFile.WriteTuple("accessor_location", accessorKey, location); @@ -721,5 +725,15 @@ namespace Semmle.Extraction.CSharp { trapFile.WriteTuple("directive_define_symbols", symb, name); } + + internal static void locations_mapped(this System.IO.TextWriter trapFile, NonGeneratedSourceLocation l1, Location l2) + { + trapFile.WriteTuple("locations_mapped", l1, l2); + } + + internal static void file_extraction_mode(this System.IO.TextWriter trapFile, Entities.File file, int mode) + { + trapFile.WriteTuple("file_extraction_mode", file, mode); + } } } diff --git a/csharp/extractor/Semmle.Extraction.Tests/Semmle.Extraction.Tests.csproj b/csharp/extractor/Semmle.Extraction.Tests/Semmle.Extraction.Tests.csproj index 830529d3710..5b242d984ee 100644 --- a/csharp/extractor/Semmle.Extraction.Tests/Semmle.Extraction.Tests.csproj +++ b/csharp/extractor/Semmle.Extraction.Tests/Semmle.Extraction.Tests.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net5.0 false win-x64;linux-x64;osx-x64 enable diff --git a/csharp/extractor/Semmle.Extraction/Comments.cs b/csharp/extractor/Semmle.Extraction/Comments.cs deleted file mode 100644 index 457d478ca4c..00000000000 --- a/csharp/extractor/Semmle.Extraction/Comments.cs +++ /dev/null @@ -1,105 +0,0 @@ -using Microsoft.CodeAnalysis; -using System.Collections.Generic; - -namespace Semmle.Extraction.CommentProcessing -{ - /// - /// The type of a single comment line. - /// - public enum CommentLineType - { - Singleline, // Comment starting // ... - XmlDoc, // Comment starting /// ... - Multiline, // Comment starting /* ..., even if the comment only spans one line. - MultilineContinuation // The second and subsequent lines of comment in a multiline comment. - }; - - /// - /// Describes the relationship between a comment and a program element. - /// - public enum CommentBinding - { - Parent, // The parent element of a comment - Best, // The most likely element associated with a comment - Before, // The element before the comment - After // The element after the comment - }; - - /// - /// A single line in a comment. - /// - public interface ICommentLine - { - /// - /// The location of this comment line. - /// - Location Location { get; } - - /// - /// The type of this comment line. - /// - CommentLineType Type { get; } - - /// - /// The text body of this comment line, excluding comment delimiter and leading and trailing whitespace. - /// - string Text { get; } - - /// - /// Full text of the comment including leading/trailing whitespace and comment delimiters. - /// - string RawText { get; } - } - - /// - /// A block of comment lines combined into one unit. - /// - public interface ICommentBlock - { - /// - /// The full span of this comment block. - /// - Location Location { get; } - - /// - /// The individual lines in the comment. - /// - IEnumerable CommentLines { get; } - } - - /// - /// Callback for generated comment associations. - /// - /// The label of the element - /// The duplication guard key of the element, if any - /// The comment block associated with the element - /// The relationship between the commentblock and the element - public delegate void CommentBindingCallback(Label elementLabel, Key? duplicationGuardKey, ICommentBlock commentBlock, CommentBinding binding); - - /// - /// Computes the binding information between comments and program elements. - /// - public interface ICommentGenerator - { - /// - /// Registers the location of a program element to associate comments with. - /// This can be called in any order. - /// - /// Label of the element. - /// The duplication guard key of the element, if any. - /// Location of the element. - void AddElement(Label elementLabel, Key? duplicationGuardKey, Location location); - - /// - /// Registers a line of comment. - /// - /// The comment to register. - void AddComment(ICommentLine comment); - - /// - /// Computes the binding information and calls `cb` with all of the comment binding information. - /// - /// Receiver of the binding information. - void GenerateBindings(CommentBindingCallback cb); - } -} diff --git a/csharp/extractor/Semmle.Extraction/Context.cs b/csharp/extractor/Semmle.Extraction/Context.cs index a4b84d5d047..c0580db96a7 100644 --- a/csharp/extractor/Semmle.Extraction/Context.cs +++ b/csharp/extractor/Semmle.Extraction/Context.cs @@ -1,9 +1,9 @@ using Microsoft.CodeAnalysis; -using Semmle.Extraction.CommentProcessing; using Semmle.Extraction.Entities; using Semmle.Util.Logging; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; @@ -20,22 +20,6 @@ namespace Semmle.Extraction /// public Extractor Extractor { get; } - /// - /// The program database provided by Roslyn. - /// There's one per syntax tree, which makes things awkward. - /// - public SemanticModel GetModel(SyntaxNode node) - { - if (cachedModel == null || node.SyntaxTree != cachedModel.SyntaxTree) - { - cachedModel = Compilation.GetSemanticModel(node.SyntaxTree); - } - - return cachedModel; - } - - private SemanticModel? cachedModel; - /// /// Access to the trap file. /// @@ -51,19 +35,19 @@ namespace Semmle.Extraction // A recursion guard against writing to the trap file whilst writing an id to the trap file. private bool writingLabel = false; - public void DefineLabel(IEntity entity, TextWriter trapFile, Extractor extractor) + protected void DefineLabel(IEntity entity) { if (writingLabel) { // Don't define a label whilst writing a label. - PopulateLater(() => DefineLabel(entity, trapFile, extractor)); + PopulateLater(() => DefineLabel(entity)); } else { try { writingLabel = true; - entity.DefineLabel(trapFile, extractor); + entity.DefineLabel(TrapWriter.Writer, Extractor); } finally { @@ -86,13 +70,13 @@ namespace Semmle.Extraction } #endif - public Label GetNewLabel() => new Label(GetNewId()); + protected Label GetNewLabel() => new Label(GetNewId()); - public TEntity CreateEntity(ICachedEntityFactory factory, object cacheKey, TInit init) + internal TEntity CreateEntity(CachedEntityFactory factory, object cacheKey, TInit init) where TEntity : CachedEntity => - cacheKey is ISymbol s ? CreateEntity(factory, s, init, symbolEntityCache) : CreateEntity(factory, cacheKey, init, objectEntityCache); + cacheKey is ISymbol s ? CreateEntity(factory, s, init, symbolEntityCache) : CreateEntity(factory, cacheKey, init, objectEntityCache); - public TEntity CreateEntityFromSymbol(ICachedEntityFactory factory, TSymbol init) + internal TEntity CreateEntityFromSymbol(CachedEntityFactory factory, TSymbol init) where TSymbol : ISymbol where TEntity : CachedEntity => CreateEntity(factory, init, init, symbolEntityCache); @@ -104,7 +88,7 @@ namespace Semmle.Extraction /// The initializer for the entity. /// The dictionary to use for caching. /// The new/existing entity. - private TEntity CreateEntity(ICachedEntityFactory factory, TCacheKey cacheKey, TInit init, IDictionary dictionary) + private TEntity CreateEntity(CachedEntityFactory factory, TCacheKey cacheKey, TInit init, IDictionary dictionary) where TCacheKey : notnull where TEntity : CachedEntity { @@ -119,7 +103,7 @@ namespace Semmle.Extraction dictionary[cacheKey] = entity; - DefineLabel(entity, TrapWriter.Writer, Extractor); + DefineLabel(entity); if (entity.NeedsPopulation) Populate(init as ISymbol, entity); @@ -133,32 +117,11 @@ namespace Semmle.Extraction } } - /// - /// Should the given entity be extracted? - /// A second call to this method will always return false, - /// on the assumption that it would have been extracted on the first call. - /// - /// This is used to track the extraction of generics, which cannot be extracted - /// in a top-down manner. - /// - /// The entity to extract. - /// True only on the first call for a particular entity. - public bool ExtractGenerics(CachedEntity entity) - { - if (extractedGenerics.Contains(entity.Label)) - { - return false; - } - - extractedGenerics.Add(entity.Label); - return true; - } - /// /// Creates a fresh label with ID "*", and set it on the /// supplied object. /// - public void AddFreshLabel(Entity entity) + internal void AddFreshLabel(Entity entity) { entity.Label = GetNewLabel(); entity.DefineFreshLabel(TrapWriter.Writer); @@ -170,7 +133,6 @@ namespace Semmle.Extraction private readonly IDictionary objectEntityCache = new Dictionary(); private readonly IDictionary symbolEntityCache = new Dictionary(10000, SymbolEqualityComparer.Default); - private readonly HashSet