diff --git a/.codeqlmanifest.json b/.codeqlmanifest.json index 30efc842b00..81d370e793d 100644 --- a/.codeqlmanifest.json +++ b/.codeqlmanifest.json @@ -1,5 +1,6 @@ { "provide": [ "*/ql/src/qlpack.yml", "*/ql/test/qlpack.yml", + "cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml", "*/ql/examples/qlpack.yml", "*/upgrades/qlpack.yml", "misc/legacy-support/*/qlpack.yml", diff --git a/.github/workflows/check-change-note.yml b/.github/workflows/check-change-note.yml index 0b90ffbc668..8c036c2f1b3 100644 --- a/.github/workflows/check-change-note.yml +++ b/.github/workflows/check-change-note.yml @@ -19,5 +19,5 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate | - jq 'any(.[].filename ; test("/change-notes/.*[.]md$"))' --exit-status + gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq 'any(.[].filename ; test("/change-notes/.*[.]md$"))' | + grep true -c diff --git a/.github/workflows/close-stale.yml b/.github/workflows/close-stale.yml new file mode 100644 index 00000000000..c63a6be690c --- /dev/null +++ b/.github/workflows/close-stale.yml @@ -0,0 +1,30 @@ +name: Mark stale issues + +on: + workflow_dispatch: + schedule: + - cron: "30 1 * * *" + +jobs: + stale: + if: github.repository == 'github/codeql' + + runs-on: ubuntu-latest + + steps: + - uses: actions/stale@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: 'This issue is stale because it has been open 14 days with no activity. Comment or remove the `Stale` label in order to avoid having this issue closed in 7 days.' + close-issue-message: 'This issue was closed because it has been inactive for 7 days.' + days-before-stale: 14 + days-before-close: 7 + only-labels: awaiting-response + + # do not mark PRs as stale + days-before-pr-stale: -1 + days-before-pr-close: -1 + + # Uncomment for dry-run + # debug-only: true + # operations-per-run: 1000 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b86009ef6da..87d6632d03e 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -19,13 +19,18 @@ jobs: runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + pull-requests: read + steps: - name: Checkout repository uses: actions/checkout@v2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@main # Override language selection by uncommenting this and choosing your languages with: languages: csharp @@ -34,7 +39,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@main # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -48,4 +53,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@main diff --git a/.github/workflows/csv-coverage.yml b/.github/workflows/csv-coverage.yml new file mode 100644 index 00000000000..54c172d66d3 --- /dev/null +++ b/.github/workflows/csv-coverage.yml @@ -0,0 +1,77 @@ +name: Build/check CSV flow coverage report + +on: + workflow_dispatch: + inputs: + qlModelShaOverride: + description: 'github/codeql repo SHA used for looking up the CSV models' + required: false + push: + branches: + - main + - 'rc/**' + pull_request: + paths: + - '.github/workflows/csv-coverage.yml' + - '*/ql/src/**/*.ql' + - '*/ql/src/**/*.qll' + - 'misc/scripts/library-coverage/*.py' + # input data files + - '*/documentation/library-coverage/cwe-sink.csv' + - '*/documentation/library-coverage/frameworks.csv' + # coverage report files + - '*/documentation/library-coverage/flow-model-coverage.csv' + - '*/documentation/library-coverage/flow-model-coverage.rst' + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - name: Clone self (github/codeql) + uses: actions/checkout@v2 + with: + path: script + - name: Clone self (github/codeql) at a given SHA for analysis + if: github.event.inputs.qlModelShaOverride != '' + uses: actions/checkout@v2 + with: + path: codeqlModels + ref: github.event.inputs.qlModelShaOverride + - name: Clone self (github/codeql) for analysis + if: github.event.inputs.qlModelShaOverride == '' + uses: actions/checkout@v2 + with: + path: codeqlModels + - 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: Build modeled package list + run: | + PATH="$PATH:codeql-cli/codeql" python script/misc/scripts/library-coverage/generate-report.py ci codeqlModels script + - name: Upload CSV package list + uses: actions/upload-artifact@v2 + with: + name: csv-flow-model-coverage + path: flow-model-coverage-*.csv + - name: Upload RST package list + uses: actions/upload-artifact@v2 + with: + name: rst-flow-model-coverage + path: flow-model-coverage-*.rst + # - name: Check coverage files + # if: github.event.pull_request + # run: | + # python script/misc/scripts/library-coverage/compare-files.py codeqlModels + diff --git a/config/identical-files.json b/config/identical-files.json index 6c1c0c7409d..582e4c7b6dc 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -5,6 +5,7 @@ "java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll", "java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll", "java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll", + "java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll", "cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll", "cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll", "cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll", @@ -56,6 +57,10 @@ "csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll", "python/ql/src/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll" ], + "DataFlow Java/C# Flow Summaries": [ + "java/ql/src/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll", + "csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll" + ], "SsaReadPosition Java/C#": [ "java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll", "csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll" @@ -245,6 +250,10 @@ "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll", "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll" ], + "SSA PrintAliasAnalysis": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintAliasAnalysis.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasAnalysis.qll" + ], "C++ SSA AliasAnalysisImports": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysisImports.qll" @@ -434,6 +443,10 @@ ], "CryptoAlgorithms Python/JS": [ "javascript/ql/src/semmle/javascript/security/CryptoAlgorithms.qll", - "python/ql/src/semmle/crypto/Crypto.qll" + "python/ql/src/semmle/python/concepts/CryptoAlgorithms.qll" + ], + "SensitiveDataHeuristics Python/JS": [ + "javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll", + "python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll" ] -} \ No newline at end of file +} diff --git a/cpp/change-notes/2021-03-11-overflow-abs.md b/cpp/change-notes/2021-03-11-overflow-abs.md new file mode 100644 index 00000000000..66854412f72 --- /dev/null +++ b/cpp/change-notes/2021-03-11-overflow-abs.md @@ -0,0 +1,2 @@ +lgtm +* The `cpp/tainted-arithmetic`, `cpp/arithmetic-with-extreme-values`, and `cpp/uncontrolled-arithmetic` queries now recognize more functions as returning the absolute value of their input. As a result, they produce fewer false positives. diff --git a/cpp/change-notes/2021-04-06-assign-where-compare-meant.md b/cpp/change-notes/2021-04-06-assign-where-compare-meant.md new file mode 100644 index 00000000000..e540593b15a --- /dev/null +++ b/cpp/change-notes/2021-04-06-assign-where-compare-meant.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The 'Assignment where comparison was intended' (cpp/assign-where-compare-meant) query has been improved to flag fewer benign assignments in conditionals. diff --git a/cpp/change-notes/2021-04-09-unsigned-difference-expression-compared-zero.md b/cpp/change-notes/2021-04-09-unsigned-difference-expression-compared-zero.md new file mode 100644 index 00000000000..3297dfce9a9 --- /dev/null +++ b/cpp/change-notes/2021-04-09-unsigned-difference-expression-compared-zero.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The 'Unsigned difference expression compared to zero' (cpp/unsigned-difference-expression-compared-zero) query has been improved to produce fewer false positive results. \ No newline at end of file diff --git a/cpp/change-notes/2021-04-13-arithmetic-queries.md b/cpp/change-notes/2021-04-13-arithmetic-queries.md new file mode 100644 index 00000000000..4d0f8833adc --- /dev/null +++ b/cpp/change-notes/2021-04-13-arithmetic-queries.md @@ -0,0 +1,2 @@ +lgtm +* The queries cpp/tainted-arithmetic, cpp/uncontrolled-arithmetic, and cpp/arithmetic-with-extreme-values have been improved to produce fewer false positives. diff --git a/cpp/change-notes/2021-04-21-return-stack-allocated-object.md b/cpp/change-notes/2021-04-21-return-stack-allocated-object.md new file mode 100644 index 00000000000..1876f4cf5f7 --- /dev/null +++ b/cpp/change-notes/2021-04-21-return-stack-allocated-object.md @@ -0,0 +1,2 @@ +codescanning +* The 'Pointer to stack object used as return value' (cpp/return-stack-allocated-object) query has been deprecated, and any uses should be replaced with `Returning stack-allocated memory` (cpp/return-stack-allocated-memory). \ No newline at end of file diff --git a/cpp/change-notes/2021-04-26-more-sound-expr-might-overflow.md b/cpp/change-notes/2021-04-26-more-sound-expr-might-overflow.md new file mode 100644 index 00000000000..5a7b8414fad --- /dev/null +++ b/cpp/change-notes/2021-04-26-more-sound-expr-might-overflow.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The `exprMightOverflowPositively` and `exprMightOverflowNegatively` predicates from the `SimpleRangeAnalysis` library now recognize more expressions that might overflow. diff --git a/cpp/change-notes/2021-05-10-comparison-with-wider-type.md b/cpp/change-notes/2021-05-10-comparison-with-wider-type.md new file mode 100644 index 00000000000..f06895a3c1a --- /dev/null +++ b/cpp/change-notes/2021-05-10-comparison-with-wider-type.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The 'Comparison with wider type' (cpp/comparison-with-wider-type) query has been improved to produce fewer false positives. \ No newline at end of file diff --git a/cpp/change-notes/2021-05-12-uncontrolled-arithmetic.md b/cpp/change-notes/2021-05-12-uncontrolled-arithmetic.md new file mode 100644 index 00000000000..56fbc9a44ce --- /dev/null +++ b/cpp/change-notes/2021-05-12-uncontrolled-arithmetic.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The query "Uncontrolled arithmetic" (`cpp/uncontrolled-arithmetic`) has been improved to produce fewer false positives. diff --git a/cpp/change-notes/2021-05-14-uncontrolled-allocation-size.md b/cpp/change-notes/2021-05-14-uncontrolled-allocation-size.md new file mode 100644 index 00000000000..6f0c9d6fa98 --- /dev/null +++ b/cpp/change-notes/2021-05-14-uncontrolled-allocation-size.md @@ -0,0 +1,2 @@ +lgtm +* The "Tainted allocation size" query (cpp/uncontrolled-allocation-size) has been improved to produce fewer false positives. diff --git a/cpp/change-notes/2021-05-18-static-buffer-overflow.md b/cpp/change-notes/2021-05-18-static-buffer-overflow.md new file mode 100644 index 00000000000..f56040fe8aa --- /dev/null +++ b/cpp/change-notes/2021-05-18-static-buffer-overflow.md @@ -0,0 +1,2 @@ +lgtm +* The "Static buffer overflow" query (cpp/static-buffer-overflow) has been improved to produce fewer false positives. diff --git a/cpp/change-notes/2021-05-19-weak-cryptographic-algorithm.md b/cpp/change-notes/2021-05-19-weak-cryptographic-algorithm.md new file mode 100644 index 00000000000..5e48ba166b7 --- /dev/null +++ b/cpp/change-notes/2021-05-19-weak-cryptographic-algorithm.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The "Use of a broken or risky cryptographic algorithm" (`cpp/weak-cryptographic-algorithm`) query has been enhanced to reduce false positive results, and (rarely) find more true positive results. diff --git a/cpp/change-notes/2021-05-20-incorrect-allocation-error-handling.md b/cpp/change-notes/2021-05-20-incorrect-allocation-error-handling.md new file mode 100644 index 00000000000..f00856ed711 --- /dev/null +++ b/cpp/change-notes/2021-05-20-incorrect-allocation-error-handling.md @@ -0,0 +1,2 @@ +lgtm +* A new query (`cpp/incorrect-allocation-error-handling`) has been added. The query finds incorrect error-handling of calls to `operator new`. This query was originally [submitted as an experimental query by @ihsinme](https://github.com/github/codeql/pull/5010). diff --git a/cpp/change-notes/2021-05-20-ref-qualifiers.md b/cpp/change-notes/2021-05-20-ref-qualifiers.md new file mode 100644 index 00000000000..e4b394a2173 --- /dev/null +++ b/cpp/change-notes/2021-05-20-ref-qualifiers.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* lvalue/rvalue ref qualifiers are now accessible via the new predicates on `MemberFunction`(`.isLValueRefQualified`, `.isRValueRefQualified`, and `isRefQualified`). diff --git a/cpp/change-notes/2021-05-21-unsafe-strncat.md b/cpp/change-notes/2021-05-21-unsafe-strncat.md new file mode 100644 index 00000000000..135b5278df4 --- /dev/null +++ b/cpp/change-notes/2021-05-21-unsafe-strncat.md @@ -0,0 +1,2 @@ +lgtm +* The "Potentially unsafe call to strncat" query (cpp/unsafe-strncat) query has been improved to detect more cases of unsafe calls to `strncat`. diff --git a/cpp/change-notes/2021-06-10-std-types.md b/cpp/change-notes/2021-06-10-std-types.md new file mode 100644 index 00000000000..86577ce14a0 --- /dev/null +++ b/cpp/change-notes/2021-06-10-std-types.md @@ -0,0 +1,4 @@ +lgtm,codescanning +* Added definitions for types found in `cstdint`. Added types `FixedWidthIntegralType`, `MinimumWidthIntegralType`, `FastestMinimumWidthIntegralType`, and `MaximumWidthIntegralType` to describe types such as `int8_t`, `int_least8_t`, `int_fast8_t`, and `intmax_t` respectively. +* Changed definition of `Intmax_t` and `Uintmax_t` to be part of the new type structure. +* Added a type `FixedWidthEnumType` which describes enums based on a fixed-width integer type. For instance, `enum e: uint8_t = { a, b };`. diff --git a/cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.ql b/cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.ql index 36024ddbc70..ecf739b91be 100644 --- a/cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.ql +++ b/cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.ql @@ -5,6 +5,7 @@ * @kind problem * @id cpp/offset-use-before-range-check * @problem.severity warning + * @security-severity 5.9 * @precision medium * @tags reliability * security diff --git a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.qhelp b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.qhelp index 16fc75fc7ad..aa97965996f 100644 --- a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.qhelp +++ b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.qhelp @@ -39,7 +39,7 @@ then replace all the relevant occurrences in the code.

  • Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997). - Chapter 5: Object Life Cycle, Rec 5.4 (PDF). + Chapter 5: Object Life Cycle, Rec 5.4 (PDF).
  • DCL06-C. Use meaningful symbolic constants to represent literal values diff --git a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.qhelp b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.qhelp index ca01aac69f0..19182fe5b19 100644 --- a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.qhelp +++ b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.qhelp @@ -38,7 +38,7 @@ constant.

  • Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997). - Chapter 5: Object Life Cycle, Rec 5.4 (PDF). + Chapter 5: Object Life Cycle, Rec 5.4 (PDF).
  • DCL06-C. Use meaningful symbolic constants to represent literal values diff --git a/cpp/ql/src/Best Practices/SloppyGlobal.qhelp b/cpp/ql/src/Best Practices/SloppyGlobal.qhelp index a6255cc6a9e..48b36e6d476 100644 --- a/cpp/ql/src/Best Practices/SloppyGlobal.qhelp +++ b/cpp/ql/src/Best Practices/SloppyGlobal.qhelp @@ -21,7 +21,7 @@ Review the purpose of the each global variable flagged by this rule and update e
  • Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997). - Chapter 1: Naming, Rec 1.1 (PDF). + Chapter 1: Naming, Rec 1.1 (PDF).
  • Global variables. diff --git a/cpp/ql/src/Best Practices/UseOfGoto.qhelp b/cpp/ql/src/Best Practices/UseOfGoto.qhelp index dd5702aabe4..cfceb144605 100644 --- a/cpp/ql/src/Best Practices/UseOfGoto.qhelp +++ b/cpp/ql/src/Best Practices/UseOfGoto.qhelp @@ -45,7 +45,7 @@ this rule.
  • Mats Henricson and Erik Nyquist, Industrial Strength C++, Rule 4.6. Prentice Hall PTR, 1997. - (PDF). + (PDF).
  • cplusplus.com: Control Structures. diff --git a/cpp/ql/src/Critical/DescriptorMayNotBeClosed.ql b/cpp/ql/src/Critical/DescriptorMayNotBeClosed.ql index 135b9a644d1..24cd9dc16fd 100644 --- a/cpp/ql/src/Critical/DescriptorMayNotBeClosed.ql +++ b/cpp/ql/src/Critical/DescriptorMayNotBeClosed.ql @@ -4,6 +4,7 @@ * @kind problem * @id cpp/descriptor-may-not-be-closed * @problem.severity warning + * @security-severity 5.9 * @tags efficiency * security * external/cwe/cwe-775 diff --git a/cpp/ql/src/Critical/DescriptorNeverClosed.ql b/cpp/ql/src/Critical/DescriptorNeverClosed.ql index ae50e625602..331d787be62 100644 --- a/cpp/ql/src/Critical/DescriptorNeverClosed.ql +++ b/cpp/ql/src/Critical/DescriptorNeverClosed.ql @@ -4,6 +4,7 @@ * @kind problem * @id cpp/descriptor-never-closed * @problem.severity warning + * @security-severity 5.9 * @tags efficiency * security * external/cwe/cwe-775 diff --git a/cpp/ql/src/Critical/FileMayNotBeClosed.ql b/cpp/ql/src/Critical/FileMayNotBeClosed.ql index c97e7cacca3..395bac61f0b 100644 --- a/cpp/ql/src/Critical/FileMayNotBeClosed.ql +++ b/cpp/ql/src/Critical/FileMayNotBeClosed.ql @@ -4,6 +4,7 @@ * @kind problem * @id cpp/file-may-not-be-closed * @problem.severity warning + * @security-severity 5.9 * @tags efficiency * security * external/cwe/cwe-775 diff --git a/cpp/ql/src/Critical/FileNeverClosed.ql b/cpp/ql/src/Critical/FileNeverClosed.ql index 0286c78437f..eeeed80af92 100644 --- a/cpp/ql/src/Critical/FileNeverClosed.ql +++ b/cpp/ql/src/Critical/FileNeverClosed.ql @@ -4,6 +4,7 @@ * @kind problem * @id cpp/file-never-closed * @problem.severity warning + * @security-severity 5.9 * @tags efficiency * security * external/cwe/cwe-775 diff --git a/cpp/ql/src/Critical/GlobalUseBeforeInit.ql b/cpp/ql/src/Critical/GlobalUseBeforeInit.ql index 5483a0f2fbe..7abfaeb9ebe 100644 --- a/cpp/ql/src/Critical/GlobalUseBeforeInit.ql +++ b/cpp/ql/src/Critical/GlobalUseBeforeInit.ql @@ -4,6 +4,7 @@ * @kind problem * @id cpp/global-use-before-init * @problem.severity warning + * @security-severity 6.9 * @tags reliability * security * external/cwe/cwe-457 diff --git a/cpp/ql/src/Critical/InconsistentNullnessTesting.ql b/cpp/ql/src/Critical/InconsistentNullnessTesting.ql index 86e2cb4fb29..b356c64b3fc 100644 --- a/cpp/ql/src/Critical/InconsistentNullnessTesting.ql +++ b/cpp/ql/src/Critical/InconsistentNullnessTesting.ql @@ -4,6 +4,7 @@ * @kind problem * @id cpp/inconsistent-nullness-testing * @problem.severity warning + * @security-severity 3.6 * @tags reliability * security * external/cwe/cwe-476 diff --git a/cpp/ql/src/Critical/InitialisationNotRun.ql b/cpp/ql/src/Critical/InitialisationNotRun.ql index 6295543084b..d4bb90962f7 100644 --- a/cpp/ql/src/Critical/InitialisationNotRun.ql +++ b/cpp/ql/src/Critical/InitialisationNotRun.ql @@ -4,6 +4,7 @@ * @kind problem * @id cpp/initialization-not-run * @problem.severity warning + * @security-severity 6.4 * @tags reliability * security * external/cwe/cwe-456 diff --git a/cpp/ql/src/Critical/LateNegativeTest.ql b/cpp/ql/src/Critical/LateNegativeTest.ql index 098a224c818..98d1d7cba2b 100644 --- a/cpp/ql/src/Critical/LateNegativeTest.ql +++ b/cpp/ql/src/Critical/LateNegativeTest.ql @@ -6,6 +6,7 @@ * @kind problem * @id cpp/late-negative-test * @problem.severity warning + * @security-severity 10.0 * @tags reliability * security * external/cwe/cwe-823 diff --git a/cpp/ql/src/Critical/MemoryMayNotBeFreed.ql b/cpp/ql/src/Critical/MemoryMayNotBeFreed.ql index 9c09ec4c5f3..3726117615e 100644 --- a/cpp/ql/src/Critical/MemoryMayNotBeFreed.ql +++ b/cpp/ql/src/Critical/MemoryMayNotBeFreed.ql @@ -4,6 +4,7 @@ * @kind problem * @id cpp/memory-may-not-be-freed * @problem.severity warning + * @security-severity 3.6 * @tags efficiency * security * external/cwe/cwe-401 diff --git a/cpp/ql/src/Critical/MemoryNeverFreed.ql b/cpp/ql/src/Critical/MemoryNeverFreed.ql index 59f6f1da4df..89ca2245d7f 100644 --- a/cpp/ql/src/Critical/MemoryNeverFreed.ql +++ b/cpp/ql/src/Critical/MemoryNeverFreed.ql @@ -4,6 +4,7 @@ * @kind problem * @id cpp/memory-never-freed * @problem.severity warning + * @security-severity 3.6 * @tags efficiency * security * external/cwe/cwe-401 diff --git a/cpp/ql/src/Critical/MissingNegativityTest.ql b/cpp/ql/src/Critical/MissingNegativityTest.ql index 565217578e0..937510afec6 100644 --- a/cpp/ql/src/Critical/MissingNegativityTest.ql +++ b/cpp/ql/src/Critical/MissingNegativityTest.ql @@ -5,6 +5,7 @@ * @kind problem * @id cpp/missing-negativity-test * @problem.severity warning + * @security-severity 10.0 * @tags reliability * security * external/cwe/cwe-823 diff --git a/cpp/ql/src/Critical/MissingNullTest.ql b/cpp/ql/src/Critical/MissingNullTest.ql index ea81eee8eb6..dcd45f2baf1 100644 --- a/cpp/ql/src/Critical/MissingNullTest.ql +++ b/cpp/ql/src/Critical/MissingNullTest.ql @@ -4,6 +4,7 @@ * @kind problem * @id cpp/missing-null-test * @problem.severity recommendation + * @security-severity 3.6 * @tags reliability * security * external/cwe/cwe-476 diff --git a/cpp/ql/src/Critical/NewFreeMismatch.ql b/cpp/ql/src/Critical/NewFreeMismatch.ql index 68e58b3acaf..09356762e43 100644 --- a/cpp/ql/src/Critical/NewFreeMismatch.ql +++ b/cpp/ql/src/Critical/NewFreeMismatch.ql @@ -3,6 +3,7 @@ * @description An object that was allocated with 'malloc' or 'new' is being freed using a mismatching 'free' or 'delete'. * @kind problem * @problem.severity warning + * @security-severity 3.6 * @precision high * @id cpp/new-free-mismatch * @tags reliability diff --git a/cpp/ql/src/Critical/OverflowCalculated.ql b/cpp/ql/src/Critical/OverflowCalculated.ql index a52e0d82670..01cb7b3eaa3 100644 --- a/cpp/ql/src/Critical/OverflowCalculated.ql +++ b/cpp/ql/src/Critical/OverflowCalculated.ql @@ -4,6 +4,7 @@ * @kind problem * @id cpp/overflow-calculated * @problem.severity warning + * @security-severity 5.9 * @tags reliability * security * external/cwe/cwe-131 diff --git a/cpp/ql/src/Critical/OverflowDestination.ql b/cpp/ql/src/Critical/OverflowDestination.ql index bff3cac9326..c89ec46cb42 100644 --- a/cpp/ql/src/Critical/OverflowDestination.ql +++ b/cpp/ql/src/Critical/OverflowDestination.ql @@ -5,6 +5,7 @@ * @kind problem * @id cpp/overflow-destination * @problem.severity warning + * @security-severity 10.0 * @precision low * @tags reliability * security diff --git a/cpp/ql/src/Critical/OverflowStatic.ql b/cpp/ql/src/Critical/OverflowStatic.ql index 833ee45499e..d287f43b1c8 100644 --- a/cpp/ql/src/Critical/OverflowStatic.ql +++ b/cpp/ql/src/Critical/OverflowStatic.ql @@ -4,6 +4,7 @@ * may result in a buffer overflow. * @kind problem * @problem.severity warning + * @security-severity 10.0 * @precision medium * @id cpp/static-buffer-overflow * @tags reliability @@ -14,6 +15,7 @@ import cpp import semmle.code.cpp.commons.Buffer +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis import LoopBounds private predicate staticBufferBase(VariableAccess access, Variable v) { @@ -51,6 +53,8 @@ predicate overflowOffsetInLoop(BufferAccess bufaccess, string msg) { loop.getStmt().getAChild*() = bufaccess.getEnclosingStmt() and loop.limit() >= bufaccess.bufferSize() and loop.counter().getAnAccess() = bufaccess.getArrayOffset() and + // Ensure that we don't have an upper bound on the array index that's less than the buffer size. + not upperBound(bufaccess.getArrayOffset().getFullyConverted()) < bufaccess.bufferSize() and msg = "Potential buffer-overflow: counter '" + loop.counter().toString() + "' <= " + loop.limit().toString() + " but '" + bufaccess.buffer().getName() + "' has " + @@ -94,17 +98,22 @@ class CallWithBufferSize extends FunctionCall { } int statedSizeValue() { - exists(Expr statedSizeSrc | - DataFlow::localExprFlow(statedSizeSrc, statedSizeExpr()) and - result = statedSizeSrc.getValue().toInt() - ) + // `upperBound(e)` defaults to `exprMaxVal(e)` when `e` isn't analyzable. So to get a meaningful + // result in this case we pick the minimum value obtainable from dataflow and range analysis. + result = + upperBound(statedSizeExpr()) + .minimum(min(Expr statedSizeSrc | + DataFlow::localExprFlow(statedSizeSrc, statedSizeExpr()) + | + statedSizeSrc.getValue().toInt() + )) } } predicate wrongBufferSize(Expr error, string msg) { exists(CallWithBufferSize call, int bufsize, Variable buf, int statedSize | staticBuffer(call.buffer(), buf, bufsize) and - statedSize = min(call.statedSizeValue()) and + statedSize = call.statedSizeValue() and statedSize > bufsize and error = call.statedSizeExpr() and msg = diff --git a/cpp/ql/src/Critical/ReturnStackAllocatedObject.ql b/cpp/ql/src/Critical/ReturnStackAllocatedObject.ql index 353e51daa71..72ff93e24ab 100644 --- a/cpp/ql/src/Critical/ReturnStackAllocatedObject.ql +++ b/cpp/ql/src/Critical/ReturnStackAllocatedObject.ql @@ -4,9 +4,12 @@ * @kind problem * @id cpp/return-stack-allocated-object * @problem.severity warning + * @security-severity 2.9 * @tags reliability * security * external/cwe/cwe-562 + * @deprecated This query is not suitable for production use and has been deprecated. Use + * cpp/return-stack-allocated-memory instead. */ import semmle.code.cpp.pointsto.PointsTo diff --git a/cpp/ql/src/Critical/ReturnValueIgnored.qhelp b/cpp/ql/src/Critical/ReturnValueIgnored.qhelp index 26d0f5c6e84..d82daf6d149 100644 --- a/cpp/ql/src/Critical/ReturnValueIgnored.qhelp +++ b/cpp/ql/src/Critical/ReturnValueIgnored.qhelp @@ -7,7 +7,7 @@

    This rule finds calls to a function that ignore the return value. A function call is only marked -as a violation if at least 80% of the total calls to that function check the return value. Not +as a violation if at least 90% of the total calls to that function check the return value. Not checking a return value is a common source of defects from standard library functions like malloc or fread. These functions return the status information and the return values should always be checked to see if the operation succeeded before operating on any data modified or resources allocated by these functions. @@ -32,7 +32,7 @@ Check the return value of functions that return status information.

  • - M. Henricson and E. Nyquist, Industrial Strength C++, Chapter 12: Error handling. Prentice Hall PTR, 1997 (available online). + M. Henricson and E. Nyquist, Industrial Strength C++, Chapter 12: Error handling. Prentice Hall PTR, 1997 (available online).
  • The CERT C Secure Coding Standard: EXP32-PL. Do not ignore function return values. diff --git a/cpp/ql/src/Critical/ReturnValueIgnored.ql b/cpp/ql/src/Critical/ReturnValueIgnored.ql index b9143085720..b4a4a044068 100644 --- a/cpp/ql/src/Critical/ReturnValueIgnored.ql +++ b/cpp/ql/src/Critical/ReturnValueIgnored.ql @@ -1,6 +1,6 @@ /** * @name Return value of a function is ignored - * @description A call to a function ignores its return value, but more than 80% of the total number of calls to the function check the return value. Check the return value of functions consistently, especially for functions like 'fread' or the 'scanf' functions that return the status of the operation. + * @description A call to a function ignores its return value, but at least 90% of the total number of calls to the function check the return value. Check the return value of functions consistently, especially for functions like 'fread' or the 'scanf' functions that return the status of the operation. * @kind problem * @id cpp/return-value-ignored * @problem.severity recommendation diff --git a/cpp/ql/src/Critical/SizeCheck.ql b/cpp/ql/src/Critical/SizeCheck.ql index 849b4ba5f77..7fff35cf717 100644 --- a/cpp/ql/src/Critical/SizeCheck.ql +++ b/cpp/ql/src/Critical/SizeCheck.ql @@ -4,6 +4,7 @@ * an instance of the type of the pointer may result in a buffer overflow * @kind problem * @problem.severity warning + * @security-severity 6.4 * @precision medium * @id cpp/allocation-too-small * @tags reliability diff --git a/cpp/ql/src/Critical/SizeCheck2.ql b/cpp/ql/src/Critical/SizeCheck2.ql index 31364cbfe2d..f9a09b66352 100644 --- a/cpp/ql/src/Critical/SizeCheck2.ql +++ b/cpp/ql/src/Critical/SizeCheck2.ql @@ -4,6 +4,7 @@ * multiple instances of the type of the pointer may result in a buffer overflow * @kind problem * @problem.severity warning + * @security-severity 6.4 * @precision medium * @id cpp/suspicious-allocation-size * @tags reliability diff --git a/cpp/ql/src/Critical/UseAfterFree.ql b/cpp/ql/src/Critical/UseAfterFree.ql index 8fd228ca7e4..1b714267ef1 100644 --- a/cpp/ql/src/Critical/UseAfterFree.ql +++ b/cpp/ql/src/Critical/UseAfterFree.ql @@ -4,6 +4,7 @@ * @kind problem * @id cpp/use-after-free * @problem.severity warning + * @security-severity 5.9 * @tags reliability * security * external/cwe/cwe-416 diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.ql b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.ql index c503a8f3ee2..1037e4d9063 100644 --- a/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.ql +++ b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.ql @@ -6,6 +6,7 @@ * to a larger type. * @kind problem * @problem.severity error + * @security-severity 5.9 * @precision very-high * @id cpp/bad-addition-overflow-check * @tags reliability diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql index 76ff682f7e5..941fecc453d 100644 --- a/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql +++ b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql @@ -4,6 +4,7 @@ * be a sign that the result can overflow the type converted from. * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision high * @id cpp/integer-multiplication-cast-to-long * @tags reliability diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.ql b/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.ql index 1cc7b74375d..6da994e6729 100644 --- a/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.ql +++ b/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.ql @@ -5,10 +5,13 @@ * unsigned integer values. * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision high * @id cpp/signed-overflow-check * @tags correctness * security + * external/cwe/cwe-128 + * external/cwe/cwe-190 */ import cpp diff --git a/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql b/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql index ff9afff9e7f..19e50a3f368 100644 --- a/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql +++ b/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql @@ -6,13 +6,14 @@ * use the width of the base type, leading to misaligned reads. * @kind path-problem * @problem.severity warning + * @security-severity 10.0 * @precision high + * @id cpp/upcast-array-pointer-arithmetic * @tags correctness * reliability * security * external/cwe/cwe-119 * external/cwe/cwe-843 - * @id cpp/upcast-array-pointer-arithmetic */ import cpp diff --git a/cpp/ql/src/Likely Bugs/Format/NonConstantFormat.ql b/cpp/ql/src/Likely Bugs/Format/NonConstantFormat.ql index 324c9128ba5..f480501f7ba 100644 --- a/cpp/ql/src/Likely Bugs/Format/NonConstantFormat.ql +++ b/cpp/ql/src/Likely Bugs/Format/NonConstantFormat.ql @@ -6,6 +6,7 @@ * from an untrusted source, this can be used for exploits. * @kind problem * @problem.severity recommendation + * @security-severity 6.9 * @precision high * @id cpp/non-constant-format * @tags maintainability diff --git a/cpp/ql/src/Likely Bugs/Format/SnprintfOverflow.ql b/cpp/ql/src/Likely Bugs/Format/SnprintfOverflow.ql index 029f11dd8c6..78427655c22 100644 --- a/cpp/ql/src/Likely Bugs/Format/SnprintfOverflow.ql +++ b/cpp/ql/src/Likely Bugs/Format/SnprintfOverflow.ql @@ -3,11 +3,14 @@ * @description Using the return value from snprintf without proper checks can cause overflow. * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision high * @id cpp/overflowing-snprintf * @tags reliability * correctness * security + * external/cwe/cwe-190 + * external/cwe/cwe-253 */ import cpp diff --git a/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.ql b/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.ql index 0f112e1ca6f..1147c6c66a1 100644 --- a/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.ql +++ b/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.ql @@ -4,11 +4,13 @@ * a source of security issues. * @kind problem * @problem.severity error + * @security-severity 2.9 * @precision high * @id cpp/wrong-number-format-arguments * @tags reliability * correctness * security + * external/cwe/cwe-234 * external/cwe/cwe-685 */ diff --git a/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql b/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql index 92486a030b1..d1624e484fe 100644 --- a/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql +++ b/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql @@ -4,6 +4,7 @@ * behavior. * @kind problem * @problem.severity error + * @security-severity 6.4 * @precision high * @id cpp/wrong-type-format-argument * @tags reliability diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/AssignWhereCompareMeant.ql b/cpp/ql/src/Likely Bugs/Likely Typos/AssignWhereCompareMeant.ql index ca116a8609c..e60b763bc53 100644 --- a/cpp/ql/src/Likely Bugs/Likely Typos/AssignWhereCompareMeant.ql +++ b/cpp/ql/src/Likely Bugs/Likely Typos/AssignWhereCompareMeant.ql @@ -54,7 +54,7 @@ class BooleanControllingAssignmentInExpr extends BooleanControllingAssignment { override predicate isWhitelisted() { this.getConversion().(ParenthesisExpr).isParenthesised() or - // whitelist this assignment if all comparison operations in the expression that this + // Allow this assignment if all comparison operations in the expression that this // assignment is part of, are not parenthesized. In that case it seems like programmer // is fine with unparenthesized comparison operands to binary logical operators, and // the parenthesis around this assignment was used to call it out as an assignment. @@ -62,6 +62,21 @@ class BooleanControllingAssignmentInExpr extends BooleanControllingAssignment { forex(ComparisonOperation op | op = getComparisonOperand*(this.getParent+()) | not op.isParenthesised() ) + or + // Match a pattern like: + // ``` + // if((a = b) && use_value(a)) { ... } + // ``` + // where the assignment is meant to update the value of `a` before it's used in some other boolean + // subexpression that is guarenteed to be evaluate _after_ the assignment. + this.isParenthesised() and + exists(LogicalAndExpr parent, Variable var, VariableAccess access | + var = this.getLValue().(VariableAccess).getTarget() and + access = var.getAnAccess() and + not access.isUsedAsLValue() and + parent.getRightOperand() = access.getParent*() and + parent.getLeftOperand() = this.getParent*() + ) } } diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/IncorrectNotOperatorUsage.ql b/cpp/ql/src/Likely Bugs/Likely Typos/IncorrectNotOperatorUsage.ql index bb20c220186..1b20aa1b224 100644 --- a/cpp/ql/src/Likely Bugs/Likely Typos/IncorrectNotOperatorUsage.ql +++ b/cpp/ql/src/Likely Bugs/Likely Typos/IncorrectNotOperatorUsage.ql @@ -6,6 +6,7 @@ * @kind problem * @id cpp/incorrect-not-operator-usage * @problem.severity warning + * @security-severity 3.6 * @precision medium * @tags security * external/cwe/cwe-480 diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.qhelp b/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.qhelp index 88a150a0ba2..3c9a68b97e2 100644 --- a/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.qhelp +++ b/cpp/ql/src/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.qhelp @@ -26,7 +26,7 @@ indication that there may be cases unhandled by the switch statemen MSDN Library: switch statement (C++)
  • - M. Henricson and E. Nyquist, Industrial Strength C++, Chapter 4: Control Flow, Rec 4.5. Prentice Hall PTR, 1997 (available online). + M. Henricson and E. Nyquist, Industrial Strength C++, Chapter 4: Control Flow, Rec 4.5. Prentice Hall PTR, 1997 (available online).
  • diff --git a/cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.ql b/cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.ql index e684cb525e7..1af4ba839b5 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.ql @@ -3,6 +3,7 @@ * @description Using alloca in a loop can lead to a stack overflow * @kind problem * @problem.severity warning + * @security-severity 3.6 * @precision high * @id cpp/alloca-in-loop * @tags reliability diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql b/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql index 2a4b2d16507..c72086060fd 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/ImproperNullTermination.ql @@ -5,6 +5,7 @@ * @kind problem * @id cpp/improper-null-termination * @problem.severity warning + * @security-severity 5.9 * @tags security * external/cwe/cwe-170 * external/cwe/cwe-665 diff --git a/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow.ql b/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow.ql index 6344addd547..3035d3ba2ea 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow.ql @@ -4,10 +4,12 @@ * on undefined behavior and may lead to memory corruption. * @kind problem * @problem.severity error + * @security-severity 2.9 * @precision high * @id cpp/pointer-overflow-check * @tags reliability * security + * external/cwe/cwe-758 */ import cpp diff --git a/cpp/ql/src/Likely Bugs/Memory Management/PotentialBufferOverflow.ql b/cpp/ql/src/Likely Bugs/Memory Management/PotentialBufferOverflow.ql index 9f611ae9bf6..23cf7e8364b 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/PotentialBufferOverflow.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/PotentialBufferOverflow.ql @@ -6,6 +6,7 @@ * @kind problem * @id cpp/potential-buffer-overflow * @problem.severity warning + * @security-severity 10.0 * @tags reliability * security * external/cwe/cwe-676 diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql index fc06ed0a500..f5dda53d484 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql @@ -13,6 +13,7 @@ import cpp import semmle.code.cpp.dataflow.EscapesTree +import semmle.code.cpp.models.interfaces.PointerWrapper import semmle.code.cpp.dataflow.DataFlow /** @@ -39,6 +40,10 @@ predicate hasNontrivialConversion(Expr e) { e instanceof ParenthesisExpr ) or + // A smart pointer can be stack-allocated while the data it points to is heap-allocated. + // So we exclude such "conversions" from this predicate. + e = any(PointerWrapper wrapper).getAnUnwrapperFunction().getACallToThisFunction() + or hasNontrivialConversion(e.getConversion()) } diff --git a/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.ql b/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.ql index 87120de0603..4a9fc949f89 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.ql @@ -4,6 +4,7 @@ * as the third argument may result in a buffer overflow. * @kind problem * @problem.severity warning + * @security-severity 10.0 * @precision medium * @id cpp/bad-strncpy-size * @tags reliability diff --git a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToMemset.ql b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToMemset.ql index 2ccdda23bfd..8e41b414794 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToMemset.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToMemset.ql @@ -7,6 +7,7 @@ * @kind problem * @id cpp/suspicious-call-to-memset * @problem.severity recommendation + * @security-severity 10.0 * @precision medium * @tags reliability * correctness diff --git a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.cpp b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.cpp index c5cbcd2d7f1..d15a123ce66 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.cpp +++ b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.cpp @@ -2,3 +2,7 @@ strncat(dest, src, strlen(dest)); //wrong: should use remaining size of dest strncat(dest, src, sizeof(dest)); //wrong: should use remaining size of dest. //Also fails if dest is a pointer and not an array. + +strncat(dest, source, sizeof(dest) - strlen(dest)); // wrong: writes a zero byte past the `dest` buffer. + +strncat(dest, source, sizeof(dest) - strlen(dest) - 1); // correct: reserves space for the zero byte. diff --git a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.qhelp b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.qhelp index 5424338e1d1..13c1e6d2710 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.qhelp +++ b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.qhelp @@ -4,7 +4,17 @@

    The standard library function strncat appends a source string to a target string. -The third argument defines the maximum number of characters to append and should be less than or equal to the remaining space in the destination buffer. Calls of the form strncat(dest, src, strlen(dest)) or strncat(dest, src, sizeof(dest)) set the third argument to the entire size of the destination buffer. Executing a call of this type may cause a buffer overflow unless the buffer is known to be empty. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.

    +The third argument defines the maximum number of characters to append and should be less than or equal +to the remaining space in the destination buffer.

    + +

    Calls of the form strncat(dest, src, strlen(dest)) or strncat(dest, src, sizeof(dest)) set +the third argument to the entire size of the destination buffer. +Executing a call of this type may cause a buffer overflow unless the buffer is known to be empty.

    + +

    Similarly, calls of the form strncat(dest, src, sizeof (dest) - strlen (dest)) allow one +byte to be written ouside the dest buffer.

    + +

    Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.

    @@ -25,6 +35,10 @@ The third argument defines the maximum number of characters to append and should
  • M. Donaldson, Inside the Buffer Overflow Attack: Mechanism, Method & Prevention. SANS Institute InfoSec Reading Room, 2002.
  • +
  • + CERT C Coding Standard: +STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator. +
  • diff --git a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.ql b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.ql index eae20876e35..28742629b37 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousCallToStrncat.ql @@ -1,14 +1,15 @@ /** * @name Potentially unsafe call to strncat - * @description Calling 'strncat' with the size of the destination buffer - * as the third argument may result in a buffer overflow. + * @description Calling 'strncat' with an incorrect size argument may result in a buffer overflow. * @kind problem * @problem.severity warning + * @security-severity 10.0 * @precision medium * @id cpp/unsafe-strncat * @tags reliability * correctness * security + * external/cwe/cwe-788 * external/cwe/cwe-676 * external/cwe/cwe-119 * external/cwe/cwe-251 @@ -16,11 +17,53 @@ import cpp import Buffer +import semmle.code.cpp.models.implementations.Strcat +import semmle.code.cpp.valuenumbering.GlobalValueNumbering -from FunctionCall fc, VariableAccess va1, VariableAccess va2 -where - fc.getTarget().(Function).hasName("strncat") and - va1 = fc.getArgument(0) and - va2 = fc.getArgument(2).(BufferSizeExpr).getArg() and - va1.getTarget() = va2.getTarget() +/** + * Holds if `call` is a call to `strncat` such that `sizeArg` and `destArg` are the size and + * destination arguments, respectively. + */ +predicate interestringCallWithArgs(Call call, Expr sizeArg, Expr destArg) { + exists(StrcatFunction strcat | + strcat = call.getTarget() and + sizeArg = call.getArgument(strcat.getParamSize()) and + destArg = call.getArgument(strcat.getParamDest()) + ) +} + +/** + * Holds if `fc` is a call to `strncat` with size argument `sizeArg` and destination + * argument `destArg`, and `destArg` is the size of the buffer pointed to by `destArg`. + */ +predicate case1(FunctionCall fc, Expr sizeArg, VariableAccess destArg) { + interestringCallWithArgs(fc, sizeArg, destArg) and + exists(VariableAccess va | + va = sizeArg.(BufferSizeExpr).getArg() and + destArg.getTarget() = va.getTarget() + ) +} + +/** + * Holds if `fc` is a call to `strncat` with size argument `sizeArg` and destination + * argument `destArg`, and `sizeArg` computes the value `sizeof (dest) - strlen (dest)`. + */ +predicate case2(FunctionCall fc, Expr sizeArg, VariableAccess destArg) { + interestringCallWithArgs(fc, sizeArg, destArg) and + exists(SubExpr sub, int n | + // The destination buffer is an array of size n + destArg.getUnspecifiedType().(ArrayType).getSize() = n and + // The size argument is equivalent to a subtraction + globalValueNumber(sizeArg).getAnExpr() = sub and + // ... where the left side of the subtraction is the constant n + globalValueNumber(sub.getLeftOperand()).getAnExpr().getValue().toInt() = n and + // ... and the right side of the subtraction is a call to `strlen` where the argument is the + // destination buffer. + globalValueNumber(sub.getRightOperand()).getAnExpr().(StrlenCall).getStringExpr() = + globalValueNumber(destArg).getAnExpr() + ) +} + +from FunctionCall fc, Expr sizeArg, Expr destArg +where case1(fc, sizeArg, destArg) or case2(fc, sizeArg, destArg) select fc, "Potentially unsafe call to strncat." diff --git a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousSizeof.ql b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousSizeof.ql index b887894707c..9198cd0497e 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousSizeof.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/SuspiciousSizeof.ql @@ -5,6 +5,7 @@ * the machine pointer size. * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision medium * @id cpp/suspicious-sizeof * @tags reliability diff --git a/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.ql b/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.ql index 57dac90c850..94e230e8838 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.ql @@ -5,6 +5,7 @@ * @kind problem * @id cpp/uninitialized-local * @problem.severity warning + * @security-severity 5.9 * @precision medium * @tags security * external/cwe/cwe-665 diff --git a/cpp/ql/src/Likely Bugs/Memory Management/UnsafeUseOfStrcat.ql b/cpp/ql/src/Likely Bugs/Memory Management/UnsafeUseOfStrcat.ql index 2d595c0c050..2eb8d0b4060 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/UnsafeUseOfStrcat.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/UnsafeUseOfStrcat.ql @@ -4,6 +4,7 @@ * may result in a buffer overflow * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision medium * @id cpp/unsafe-strcat * @tags reliability diff --git a/cpp/ql/src/Likely Bugs/OO/SelfAssignmentCheck.ql b/cpp/ql/src/Likely Bugs/OO/SelfAssignmentCheck.ql index c8e10709f1c..2702cbdcea7 100644 --- a/cpp/ql/src/Likely Bugs/OO/SelfAssignmentCheck.ql +++ b/cpp/ql/src/Likely Bugs/OO/SelfAssignmentCheck.ql @@ -6,6 +6,7 @@ * @kind problem * @id cpp/self-assignment-check * @problem.severity warning + * @security-severity 5.9 * @tags reliability * security * external/cwe/cwe-826 diff --git a/cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql b/cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql index 8e78dab08ab..746a2761e49 100644 --- a/cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql +++ b/cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql @@ -6,10 +6,12 @@ * @kind path-problem * @id cpp/unsafe-use-of-this * @problem.severity error + * @security-severity 3.6 * @precision very-high * @tags correctness * language-features * security + * external/cwe/cwe-670 */ import cpp diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.ql b/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.ql index fdacae48e07..3196143c5d1 100644 --- a/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.ql +++ b/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.ql @@ -7,11 +7,14 @@ * undefined data. * @kind problem * @problem.severity error + * @security-severity 2.9 * @precision very-high * @id cpp/too-few-arguments * @tags correctness * maintainability * security + * external/cwe/cwe-234 + * external/cwe/cwe-685 */ import cpp diff --git a/cpp/ql/src/Metrics/Files/FTransitiveIncludes.qhelp b/cpp/ql/src/Metrics/Files/FTransitiveIncludes.qhelp index 4e0b989b121..846e54b33c9 100644 --- a/cpp/ql/src/Metrics/Files/FTransitiveIncludes.qhelp +++ b/cpp/ql/src/Metrics/Files/FTransitiveIncludes.qhelp @@ -29,7 +29,7 @@ build time: the more included files, the longer the compilation time.

    Decoupling C Header Files
  • - C++ Best Practice - + C++ Best Practice - Designing Header Files
  • diff --git a/cpp/ql/src/Metrics/Files/FTransitiveSourceIncludes.qhelp b/cpp/ql/src/Metrics/Files/FTransitiveSourceIncludes.qhelp index a3091c68285..785ad68847d 100644 --- a/cpp/ql/src/Metrics/Files/FTransitiveSourceIncludes.qhelp +++ b/cpp/ql/src/Metrics/Files/FTransitiveSourceIncludes.qhelp @@ -35,7 +35,7 @@ they are contributing to unnecessarily long build times and creating artificial Decoupling C Header Files
  • - C++ Best Practice - + C++ Best Practice - Designing Header Files
  • diff --git a/cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted.ql b/cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted.ql index bbb77e807e7..77c4f149cac 100644 --- a/cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted.ql +++ b/cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted.ql @@ -5,6 +5,7 @@ * @kind problem * @id cpp/memset-may-be-deleted * @problem.severity warning + * @security-severity 6.4 * @precision high * @tags security * external/cwe/cwe-14 diff --git a/cpp/ql/src/Security/CWE/CWE-020/IRUntrustedDataToExternalAPI.ql b/cpp/ql/src/Security/CWE/CWE-020/IRUntrustedDataToExternalAPI.ql index e4f0cc7883d..0396d5c7bb0 100644 --- a/cpp/ql/src/Security/CWE/CWE-020/IRUntrustedDataToExternalAPI.ql +++ b/cpp/ql/src/Security/CWE/CWE-020/IRUntrustedDataToExternalAPI.ql @@ -5,6 +5,7 @@ * @kind path-problem * @precision low * @problem.severity error + * @security-severity 5.9 * @tags security external/cwe/cwe-20 */ diff --git a/cpp/ql/src/Security/CWE/CWE-020/UntrustedDataToExternalAPI.ql b/cpp/ql/src/Security/CWE/CWE-020/UntrustedDataToExternalAPI.ql index ca6d2d00e8c..196fe57f74b 100644 --- a/cpp/ql/src/Security/CWE/CWE-020/UntrustedDataToExternalAPI.ql +++ b/cpp/ql/src/Security/CWE/CWE-020/UntrustedDataToExternalAPI.ql @@ -5,6 +5,7 @@ * @kind path-problem * @precision low * @problem.severity error + * @security-severity 5.9 * @tags security external/cwe/cwe-20 */ diff --git a/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql index 496b957cca3..c96b6f6dc5b 100644 --- a/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql +++ b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql @@ -4,6 +4,7 @@ * attacker to access unexpected resources. * @kind path-problem * @problem.severity warning + * @security-severity 6.4 * @precision medium * @id cpp/path-injection * @tags security diff --git a/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql index 69d6ce9b98f..d3020406f15 100644 --- a/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql +++ b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql @@ -5,6 +5,7 @@ * to command injection. * @kind problem * @problem.severity error + * @security-severity 5.9 * @precision low * @id cpp/command-line-injection * @tags security diff --git a/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql b/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql index d1e2fa12913..0b56e972320 100644 --- a/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql +++ b/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql @@ -4,6 +4,7 @@ * allows for a cross-site scripting vulnerability. * @kind path-problem * @problem.severity error + * @security-severity 2.9 * @precision high * @id cpp/cgi-xss * @tags security diff --git a/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql b/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql index 5ed84f45250..1e4536f0942 100644 --- a/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql +++ b/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql @@ -5,6 +5,7 @@ * to SQL Injection. * @kind path-problem * @problem.severity error + * @security-severity 6.4 * @precision high * @id cpp/sql-injection * @tags security diff --git a/cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.ql b/cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.ql index 943c13f9c5d..3aba72ed741 100644 --- a/cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.ql +++ b/cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.ql @@ -5,6 +5,7 @@ * commands. * @kind path-problem * @problem.severity warning + * @security-severity 6.0 * @precision medium * @id cpp/uncontrolled-process-operation * @tags security diff --git a/cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.ql b/cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.ql index 00dc569c2f2..c61498ac2e0 100644 --- a/cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.ql +++ b/cpp/ql/src/Security/CWE/CWE-119/OverflowBuffer.ql @@ -6,6 +6,7 @@ * @kind problem * @id cpp/overflow-buffer * @problem.severity recommendation + * @security-severity 10.0 * @tags security * external/cwe/cwe-119 * external/cwe/cwe-121 diff --git a/cpp/ql/src/Security/CWE/CWE-120/BadlyBoundedWrite.ql b/cpp/ql/src/Security/CWE/CWE-120/BadlyBoundedWrite.ql index 77d514bc7b6..01d8d8db4e2 100644 --- a/cpp/ql/src/Security/CWE/CWE-120/BadlyBoundedWrite.ql +++ b/cpp/ql/src/Security/CWE/CWE-120/BadlyBoundedWrite.ql @@ -5,6 +5,7 @@ * overflow. * @kind problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id cpp/badly-bounded-write * @tags reliability diff --git a/cpp/ql/src/Security/CWE/CWE-120/OverrunWrite.ql b/cpp/ql/src/Security/CWE/CWE-120/OverrunWrite.ql index 4ee20508e45..6832561e10c 100644 --- a/cpp/ql/src/Security/CWE/CWE-120/OverrunWrite.ql +++ b/cpp/ql/src/Security/CWE/CWE-120/OverrunWrite.ql @@ -4,6 +4,7 @@ * of data written may overflow. * @kind problem * @problem.severity error + * @security-severity 5.9 * @precision medium * @id cpp/overrunning-write * @tags reliability diff --git a/cpp/ql/src/Security/CWE/CWE-120/OverrunWriteFloat.ql b/cpp/ql/src/Security/CWE/CWE-120/OverrunWriteFloat.ql index 79ba1e17df2..73ef5e62fb2 100644 --- a/cpp/ql/src/Security/CWE/CWE-120/OverrunWriteFloat.ql +++ b/cpp/ql/src/Security/CWE/CWE-120/OverrunWriteFloat.ql @@ -5,6 +5,7 @@ * take extreme values. * @kind problem * @problem.severity error + * @security-severity 5.9 * @precision medium * @id cpp/overrunning-write-with-float * @tags reliability diff --git a/cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.ql b/cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.ql index f1a8b4e8544..656b52b03bf 100644 --- a/cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.ql +++ b/cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.ql @@ -4,6 +4,7 @@ * of data written may overflow. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision medium * @id cpp/unbounded-write * @tags reliability diff --git a/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.ql b/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.ql index bfb3a2fbb81..3dacc443a74 100644 --- a/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.ql +++ b/cpp/ql/src/Security/CWE/CWE-121/UnterminatedVarargsCall.ql @@ -5,6 +5,7 @@ * a specific value to terminate the argument list. * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision medium * @id cpp/unterminated-variadic-call * @tags reliability diff --git a/cpp/ql/src/Security/CWE/CWE-129/ImproperArrayIndexValidation.ql b/cpp/ql/src/Security/CWE/CWE-129/ImproperArrayIndexValidation.ql index c073cf37af8..59498017b1f 100644 --- a/cpp/ql/src/Security/CWE/CWE-129/ImproperArrayIndexValidation.ql +++ b/cpp/ql/src/Security/CWE/CWE-129/ImproperArrayIndexValidation.ql @@ -6,6 +6,7 @@ * @kind problem * @id cpp/unclear-array-index-validation * @problem.severity warning + * @security-severity 5.9 * @tags security * external/cwe/cwe-129 */ diff --git a/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.ql b/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.ql index 7ee6acdcd59..b31213b09f3 100644 --- a/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.ql +++ b/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.ql @@ -5,6 +5,7 @@ * terminator can cause a buffer overrun. * @kind problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id cpp/no-space-for-terminator * @tags reliability diff --git a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql index b64091263e0..0593679c3f5 100644 --- a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql +++ b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql @@ -5,6 +5,7 @@ * or data representation problems. * @kind path-problem * @problem.severity warning + * @security-severity 6.9 * @precision high * @id cpp/tainted-format-string * @tags reliability diff --git a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql index d38f3eb24c2..67853b9e361 100644 --- a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql +++ b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql @@ -5,6 +5,7 @@ * or data representation problems. * @kind path-problem * @problem.severity warning + * @security-severity 6.9 * @precision high * @id cpp/tainted-format-string-through-global * @tags reliability diff --git a/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql b/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql index 511c7a1d79f..b2844c319ba 100644 --- a/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql +++ b/cpp/ql/src/Security/CWE/CWE-170/ImproperNullTerminationTainted.ql @@ -5,6 +5,7 @@ * @kind problem * @id cpp/user-controlled-null-termination-tainted * @problem.severity warning + * @security-severity 10.0 * @tags security * external/cwe/cwe-170 */ diff --git a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticTainted.ql b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticTainted.ql index 7416af15865..ad4d0389f0c 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticTainted.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticTainted.ql @@ -4,6 +4,7 @@ * not validated can cause overflows. * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision low * @id cpp/tainted-arithmetic * @tags security diff --git a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql index a4b0f131d14..359ac7a0d1a 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql @@ -4,6 +4,7 @@ * validated can cause overflows. * @kind path-problem * @problem.severity warning + * @security-severity 5.9 * @precision medium * @id cpp/uncontrolled-arithmetic * @tags security @@ -15,34 +16,107 @@ import cpp import semmle.code.cpp.security.Overflow import semmle.code.cpp.security.Security import semmle.code.cpp.security.TaintTracking +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis import TaintedWithPath -predicate isRandCall(FunctionCall fc) { fc.getTarget().getName() = "rand" } - -predicate isRandCallOrParent(Expr e) { - isRandCall(e) or - isRandCallOrParent(e.getAChild()) +predicate isUnboundedRandCall(FunctionCall fc) { + exists(Function func | func = fc.getTarget() | + func.hasGlobalOrStdOrBslName("rand") and + not bounded(fc) and + func.getNumberOfParameters() = 0 + ) } -predicate isRandValue(Expr e) { - isRandCall(e) +/** + * An operand `e` of a division expression (i.e., `e` is an operand of either a `DivExpr` or + * a `AssignDivExpr`) is bounded when `e` is the left-hand side of the division. + */ +pragma[inline] +predicate boundedDiv(Expr e, Expr left) { e = left } + +/** + * An operand `e` of a remainder expression `rem` (i.e., `rem` is either a `RemExpr` or + * an `AssignRemExpr`) with left-hand side `left` and right-ahnd side `right` is bounded + * when `e` is `left` and `right` is upper bounded by some number that is less than the maximum integer + * allowed by the result type of `rem`. + */ +pragma[inline] +predicate boundedRem(Expr e, Expr rem, Expr left, Expr right) { + e = left and + upperBound(right.getFullyConverted()) < exprMaxVal(rem.getFullyConverted()) +} + +/** + * An operand `e` of a bitwise and expression `andExpr` (i.e., `andExpr` is either an `BitwiseAndExpr` + * or an `AssignAndExpr`) with operands `operand1` and `operand2` is the operand that is not `e` is upper + * bounded by some number that is less than the maximum integer allowed by the result type of `andExpr`. + */ +pragma[inline] +predicate boundedBitwiseAnd(Expr e, Expr andExpr, Expr operand1, Expr operand2) { + operand1 != operand2 and + e = operand1 and + upperBound(operand2.getFullyConverted()) < exprMaxVal(andExpr.getFullyConverted()) +} + +/** + * Holds if `fc` is a part of the left operand of a binary operation that greatly reduces the range + * of possible values. + */ +predicate bounded(Expr e) { + // For `%` and `&` we require that `e` is bounded by a value that is strictly smaller than the + // maximum possible value of the result type of the operation. + // For example, the function call `rand()` is considered bounded in the following program: + // ``` + // int i = rand() % (UINT8_MAX + 1); + // ``` + // but not in: + // ``` + // unsigned char uc = rand() % (UINT8_MAX + 1); + // ``` + exists(RemExpr rem | boundedRem(e, rem, rem.getLeftOperand(), rem.getRightOperand())) + or + exists(AssignRemExpr rem | boundedRem(e, rem, rem.getLValue(), rem.getRValue())) + or + exists(BitwiseAndExpr andExpr | + boundedBitwiseAnd(e, andExpr, andExpr.getAnOperand(), andExpr.getAnOperand()) + ) + or + exists(AssignAndExpr andExpr | + boundedBitwiseAnd(e, andExpr, andExpr.getAnOperand(), andExpr.getAnOperand()) + ) + or + // Optimitically assume that a division always yields a much smaller value. + boundedDiv(e, any(DivExpr div).getLeftOperand()) + or + boundedDiv(e, any(AssignDivExpr div).getLValue()) + or + boundedDiv(e, any(RShiftExpr shift).getLeftOperand()) + or + boundedDiv(e, any(AssignRShiftExpr div).getLValue()) +} + +predicate isUnboundedRandCallOrParent(Expr e) { + isUnboundedRandCall(e) + or + isUnboundedRandCallOrParent(e.getAChild()) +} + +predicate isUnboundedRandValue(Expr e) { + isUnboundedRandCall(e) or exists(MacroInvocation mi | e = mi.getExpr() and - isRandCallOrParent(e) + isUnboundedRandCallOrParent(e) ) } class SecurityOptionsArith extends SecurityOptions { override predicate isUserInput(Expr expr, string cause) { - isRandValue(expr) and - cause = "rand" and - not expr.getParent*() instanceof DivExpr + isUnboundedRandValue(expr) and + cause = "rand" } } -predicate isDiv(VariableAccess va) { exists(AssignDivExpr div | div.getLValue() = va) } - predicate missingGuard(VariableAccess va, string effect) { exists(Operation op | op.getAnOperand() = va | missingGuardAgainstUnderflow(op, va) and effect = "underflow" @@ -52,29 +126,15 @@ predicate missingGuard(VariableAccess va, string effect) { } class Configuration extends TaintTrackingConfiguration { - override predicate isSink(Element e) { - isDiv(e) - or - missingGuard(e, _) - } -} + override predicate isSink(Element e) { missingGuard(e, _) } -/** - * A value that undergoes division is likely to be bounded within a safe - * range. - */ -predicate guardedByAssignDiv(Expr origin) { - exists(VariableAccess va | - taintedWithPath(origin, va, _, _) and - isDiv(va) - ) + override predicate isBarrier(Expr e) { super.isBarrier(e) or bounded(e) } } from Expr origin, VariableAccess va, string effect, PathNode sourceNode, PathNode sinkNode where taintedWithPath(origin, va, sourceNode, sinkNode) and - missingGuard(va, effect) and - not guardedByAssignDiv(origin) + missingGuard(va, effect) select va, sourceNode, sinkNode, "$@ flows to here and is used in arithmetic, potentially causing an " + effect + ".", origin, "Uncontrolled value" diff --git a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.ql b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.ql index 668b07d72af..9addbab5c1c 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.ql @@ -6,6 +6,7 @@ * @kind problem * @id cpp/arithmetic-with-extreme-values * @problem.severity warning + * @security-severity 5.9 * @precision low * @tags security * reliability diff --git a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql index 3303316cede..1ec3c6554fe 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql @@ -5,6 +5,7 @@ * @id cpp/comparison-with-wider-type * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision high * @tags reliability * security @@ -49,7 +50,9 @@ where small = rel.getLesserOperand() and large = rel.getGreaterOperand() and rel = l.getCondition().getAChild*() and - upperBound(large).log2() > getComparisonSize(small) * 8 and + forall(Expr conv | conv = large.getConversion*() | + upperBound(conv).log2() > getComparisonSize(small) * 8 + ) and // Ignore cases where the smaller type is int or larger // These are still bugs, but you should need a very large string or array to // trigger them. We will want to disable this for some applications, but it's diff --git a/cpp/ql/src/Security/CWE/CWE-190/IntegerOverflowTainted.ql b/cpp/ql/src/Security/CWE/CWE-190/IntegerOverflowTainted.ql index 0adb600dbda..7e4880ffca6 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/IntegerOverflowTainted.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/IntegerOverflowTainted.ql @@ -5,6 +5,7 @@ * @kind problem * @id cpp/integer-overflow-tainted * @problem.severity warning + * @security-severity 5.9 * @precision low * @tags security * external/cwe/cwe-190 @@ -28,6 +29,7 @@ predicate outOfBoundsExpr(Expr expr, string kind) { from Expr use, Expr origin, string kind where + not use.getUnspecifiedType() instanceof PointerType and outOfBoundsExpr(use, kind) and tainted(origin, use) and origin != use and diff --git a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql index cc2d52385c7..765a2519a38 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql @@ -4,14 +4,17 @@ * user can result in integer overflow. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision medium * @id cpp/uncontrolled-allocation-size * @tags reliability * security * external/cwe/cwe-190 + * external/cwe/cwe-789 */ import cpp +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis import semmle.code.cpp.security.TaintTracking import TaintedWithPath @@ -27,6 +30,27 @@ predicate allocSink(Expr alloc, Expr tainted) { class TaintedAllocationSizeConfiguration extends TaintTrackingConfiguration { override predicate isSink(Element tainted) { allocSink(_, tainted) } + + override predicate isBarrier(Expr e) { + super.isBarrier(e) + or + // There can be two separate reasons for `convertedExprMightOverflow` not holding: + // 1. `e` really cannot overflow. + // 2. `e` isn't analyzable. + // If we didn't rule out case 2 we would place barriers on anything that isn't analyzable. + ( + e instanceof UnaryArithmeticOperation or + e instanceof BinaryArithmeticOperation or + e instanceof AssignArithmeticOperation + ) and + not convertedExprMightOverflow(e) + or + // Subtracting two pointers is either well-defined (and the result will likely be small), or + // terribly undefined and dangerous. Here, we assume that the programmer has ensured that the + // result is well-defined (i.e., the two pointers point to the same object), and thus the result + // will likely be small. + e = any(PointerDiffExpr diff).getAnOperand() + } } predicate taintedAllocSize( diff --git a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql index 007a4fd746d..ddd7bf3430f 100644 --- a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql +++ b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql @@ -4,6 +4,7 @@ * @kind problem * @id cpp/unsigned-difference-expression-compared-zero * @problem.severity warning + * @security-severity 5.9 * @precision medium * @tags security * correctness @@ -12,29 +13,60 @@ import cpp import semmle.code.cpp.commons.Exclusions -import semmle.code.cpp.valuenumbering.GlobalValueNumbering import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils import semmle.code.cpp.controlflow.Guards +import semmle.code.cpp.dataflow.DataFlow -/** Holds if `sub` is guarded by a condition which ensures that `left >= right`. */ +/** + * Holds if `sub` is guarded by a condition which ensures that + * `left >= right`. + */ pragma[noinline] predicate isGuarded(SubExpr sub, Expr left, Expr right) { - exists(GuardCondition guard | - guard.controls(sub.getBasicBlock(), true) and - guard.ensuresLt(left, right, 0, sub.getBasicBlock(), false) + exists(GuardCondition guard, int k | + guard.controls(sub.getBasicBlock(), _) and + guard.ensuresLt(left, right, k, sub.getBasicBlock(), false) and + k >= 0 ) } -/** Holds if `sub` will never be negative. */ -predicate nonNegative(SubExpr sub) { - not exprMightOverflowNegatively(sub.getFullyConverted()) +/** + * Holds if `n` is known or suspected to be less than or equal to + * `sub.getLeftOperand()`. + */ +predicate exprIsSubLeftOrLess(SubExpr sub, DataFlow::Node n) { + n.asExpr() = sub.getLeftOperand() or - // The subtraction is guarded by a check of the form `left >= right`. - exists(GVN left, GVN right | - // This is basically a poor man's version of a directional unbind operator. - strictcount([left, globalValueNumber(sub.getLeftOperand())]) = 1 and - strictcount([right, globalValueNumber(sub.getRightOperand())]) = 1 and - isGuarded(sub, left.getAnExpr(), right.getAnExpr()) + exists(DataFlow::Node other | + // dataflow + exprIsSubLeftOrLess(sub, other) and + ( + DataFlow::localFlowStep(n, other) or + DataFlow::localFlowStep(other, n) + ) + ) + or + exists(DataFlow::Node other | + // guard constraining `sub` + exprIsSubLeftOrLess(sub, other) and + isGuarded(sub, other.asExpr(), n.asExpr()) // other >= n + ) + or + exists(DataFlow::Node other, float p, float q | + // linear access of `other` + exprIsSubLeftOrLess(sub, other) and + linearAccess(n.asExpr(), other.asExpr(), p, q) and // n = p * other + q + p <= 1 and + q <= 0 + ) + or + exists(DataFlow::Node other, float p, float q | + // linear access of `n` + exprIsSubLeftOrLess(sub, other) and + linearAccess(other.asExpr(), n.asExpr(), p, q) and // other = p * n + q + p >= 1 and + q >= 0 ) } @@ -45,5 +77,6 @@ where ro.getLesserOperand().getValue().toInt() = 0 and ro.getGreaterOperand() = sub and sub.getFullyConverted().getUnspecifiedType().(IntegralType).isUnsigned() and - not nonNegative(sub) + exprMightOverflowNegatively(sub.getFullyConverted()) and // generally catches false positives involving constants + not exprIsSubLeftOrLess(sub, DataFlow::exprNode(sub.getRightOperand())) // generally catches false positives where there's a relation between the left and right operands select ro, "Unsigned subtraction can never be negative." diff --git a/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql b/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql index 605321b51d0..bde1e265690 100644 --- a/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql +++ b/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql @@ -4,6 +4,7 @@ * @kind problem * @id cpp/hresult-boolean-conversion * @problem.severity error + * @security-severity 4.2 * @precision high * @tags security * external/cwe/cwe-253 diff --git a/cpp/ql/src/Security/CWE/CWE-290/AuthenticationBypass.ql b/cpp/ql/src/Security/CWE/CWE-290/AuthenticationBypass.ql index 80b5ee49e97..7cdd5c34b8b 100644 --- a/cpp/ql/src/Security/CWE/CWE-290/AuthenticationBypass.ql +++ b/cpp/ql/src/Security/CWE/CWE-290/AuthenticationBypass.ql @@ -5,6 +5,7 @@ * vulnerable to spoofing attacks. * @kind path-problem * @problem.severity warning + * @security-severity 5.8 * @precision medium * @id cpp/user-controlled-bypass * @tags security diff --git a/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.ql b/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.ql index 3e84c0a87d9..6785700d077 100644 --- a/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.ql +++ b/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.ql @@ -4,6 +4,7 @@ * to an attacker. * @kind path-problem * @problem.severity warning + * @security-severity 5.9 * @precision medium * @id cpp/cleartext-storage-buffer * @tags security diff --git a/cpp/ql/src/Security/CWE/CWE-311/CleartextFileWrite.ql b/cpp/ql/src/Security/CWE/CWE-311/CleartextFileWrite.ql index 8e447bafd90..fcf2a00435e 100644 --- a/cpp/ql/src/Security/CWE/CWE-311/CleartextFileWrite.ql +++ b/cpp/ql/src/Security/CWE/CWE-311/CleartextFileWrite.ql @@ -4,6 +4,7 @@ * to an attacker. * @kind problem * @problem.severity warning + * @security-severity 6.4 * @precision medium * @id cpp/cleartext-storage-file * @tags security diff --git a/cpp/ql/src/Security/CWE/CWE-313/CleartextSqliteDatabase.ql b/cpp/ql/src/Security/CWE/CWE-313/CleartextSqliteDatabase.ql index fb5454d523e..ccb67f54d3a 100644 --- a/cpp/ql/src/Security/CWE/CWE-313/CleartextSqliteDatabase.ql +++ b/cpp/ql/src/Security/CWE/CWE-313/CleartextSqliteDatabase.ql @@ -4,6 +4,7 @@ * database can expose it to an attacker. * @kind path-problem * @problem.severity warning + * @security-severity 6.4 * @precision medium * @id cpp/cleartext-storage-database * @tags security diff --git a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql index af64a1789c3..848adfd7adc 100644 --- a/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql +++ b/cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql @@ -4,6 +4,7 @@ * an attacker to compromise security. * @kind problem * @problem.severity error + * @security-severity 5.2 * @precision medium * @id cpp/weak-cryptographic-algorithm * @tags security @@ -13,39 +14,128 @@ import cpp import semmle.code.cpp.security.Encryption -abstract class InsecureCryptoSpec extends Locatable { - abstract string description(); -} - -Function getAnInsecureFunction() { - result.getName().regexpMatch(getInsecureAlgorithmRegex()) and +/** + * A function which may relate to an insecure encryption algorithm. + */ +Function getAnInsecureEncryptionFunction() { + ( + isInsecureEncryption(result.getName()) or + isInsecureEncryption(result.getAParameter().getName()) or + isInsecureEncryption(result.getDeclaringType().getName()) + ) and exists(result.getACallToThisFunction()) } -class InsecureFunctionCall extends InsecureCryptoSpec, FunctionCall { - InsecureFunctionCall() { this.getTarget() = getAnInsecureFunction() } - - override string description() { result = "function call" } - - override string toString() { result = FunctionCall.super.toString() } - - override Location getLocation() { result = FunctionCall.super.getLocation() } +/** + * A function with additional evidence it is related to encryption. + */ +Function getAnAdditionalEvidenceFunction() { + ( + isEncryptionAdditionalEvidence(result.getName()) or + isEncryptionAdditionalEvidence(result.getAParameter().getName()) + ) and + exists(result.getACallToThisFunction()) } -Macro getAnInsecureMacro() { - result.getName().regexpMatch(getInsecureAlgorithmRegex()) and +/** + * A macro which may relate to an insecure encryption algorithm. + */ +Macro getAnInsecureEncryptionMacro() { + isInsecureEncryption(result.getName()) and exists(result.getAnInvocation()) } -class InsecureMacroSpec extends InsecureCryptoSpec, MacroInvocation { - InsecureMacroSpec() { this.getMacro() = getAnInsecureMacro() } - - override string description() { result = "macro invocation" } - - override string toString() { result = MacroInvocation.super.toString() } - - override Location getLocation() { result = MacroInvocation.super.getLocation() } +/** + * A macro with additional evidence it is related to encryption. + */ +Macro getAnAdditionalEvidenceMacro() { + isEncryptionAdditionalEvidence(result.getName()) and + exists(result.getAnInvocation()) } -from InsecureCryptoSpec c -select c, "This " + c.description() + " specifies a broken or weak cryptographic algorithm." +/** + * An enum constant which may relate to an insecure encryption algorithm. + */ +EnumConstant getAnInsecureEncryptionEnumConst() { isInsecureEncryption(result.getName()) } + +/** + * An enum constant with additional evidence it is related to encryption. + */ +EnumConstant getAdditionalEvidenceEnumConst() { isEncryptionAdditionalEvidence(result.getName()) } + +/** + * A function call we have a high confidence is related to use of an insecure encryption algorithm, along + * with an associated `Element` which might be the best point to blame, and a description of that element. + */ +predicate getInsecureEncryptionEvidence(FunctionCall fc, Element blame, string description) { + // find use of an insecure algorithm name + ( + fc.getTarget() = getAnInsecureEncryptionFunction() and + blame = fc and + description = "call to " + fc.getTarget().getName() + or + exists(MacroInvocation mi | + ( + mi.getAnExpandedElement() = fc or + mi.getAnExpandedElement() = fc.getAnArgument() + ) and + mi.getMacro() = getAnInsecureEncryptionMacro() and + blame = mi and + description = "invocation of macro " + mi.getMacro().getName() + ) + or + exists(EnumConstantAccess ec | + ec = fc.getAnArgument() and + ec.getTarget() = getAnInsecureEncryptionEnumConst() and + blame = ec and + description = "access of enum constant " + ec.getTarget().getName() + ) + ) and + // find additional evidence that this function is related to encryption. + ( + fc.getTarget() = getAnAdditionalEvidenceFunction() + or + exists(MacroInvocation mi | + ( + mi.getAnExpandedElement() = fc or + mi.getAnExpandedElement() = fc.getAnArgument() + ) and + mi.getMacro() = getAnAdditionalEvidenceMacro() + ) + or + exists(EnumConstantAccess ec | + ec = fc.getAnArgument() and + ec.getTarget() = getAdditionalEvidenceEnumConst() + ) + ) +} + +/** + * An element that is the `blame` of an `InsecureFunctionCall`. + */ +class BlamedElement extends Element { + string description; + + BlamedElement() { getInsecureEncryptionEvidence(_, this, description) } + + /** + * Holds if this is the `num`-th `BlamedElement` in `f`. + */ + predicate hasFileRank(File f, int num) { + exists(int loc | + getLocation().charLoc(f, loc, _) and + loc = + rank[num](BlamedElement other, int loc2 | other.getLocation().charLoc(f, loc2, _) | loc2) + ) + } + + string getDescription() { result = description } +} + +from File f, BlamedElement firstResult, BlamedElement thisResult +where + firstResult.hasFileRank(f, 1) and + thisResult.hasFileRank(f, _) +select firstResult, + "This file makes use of a broken or weak cryptographic algorithm (specified by $@).", thisResult, + thisResult.getDescription() diff --git a/cpp/ql/src/Security/CWE/CWE-327/OpenSslHeartbleed.ql b/cpp/ql/src/Security/CWE/CWE-327/OpenSslHeartbleed.ql index a7ffadc07be..5ee196994ac 100644 --- a/cpp/ql/src/Security/CWE/CWE-327/OpenSslHeartbleed.ql +++ b/cpp/ql/src/Security/CWE/CWE-327/OpenSslHeartbleed.ql @@ -4,6 +4,7 @@ * attackers to retrieve portions of memory. * @kind problem * @problem.severity error + * @security-severity 5.2 * @precision very-high * @id cpp/openssl-heartbleed * @tags security diff --git a/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRace.ql b/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRace.ql index 052cce56198..7d19e323f2b 100644 --- a/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRace.ql +++ b/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRace.ql @@ -5,6 +5,7 @@ * the two operations. * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision medium * @id cpp/toctou-race-condition * @tags security diff --git a/cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql b/cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql index c0f44a0c3d6..73e0d8794ad 100644 --- a/cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql +++ b/cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql @@ -4,6 +4,7 @@ * @id cpp/unsafe-create-process-call * @kind problem * @problem.severity error + * @security-severity 5.9 * @precision medium * @msrc.severity important * @tags security diff --git a/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariable.ql b/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariable.ql index 3b76a5447dd..3d6c54e33ae 100644 --- a/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariable.ql +++ b/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariable.ql @@ -5,6 +5,7 @@ * state, and reading the variable may result in undefined behavior. * @kind problem * @problem.severity warning + * @security-severity 6.9 * @opaque-id SM02313 * @id cpp/conditionally-uninitialized-variable * @tags security diff --git a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScaling.ql b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScaling.ql index d49eef4202a..a582813ed5f 100644 --- a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScaling.ql +++ b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScaling.ql @@ -4,6 +4,7 @@ * can cause buffer overflow conditions. * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision medium * @id cpp/suspicious-pointer-scaling * @tags security diff --git a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingChar.ql b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingChar.ql index d333a2b37bc..c1bfb9c4ee9 100644 --- a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingChar.ql +++ b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingChar.ql @@ -5,6 +5,7 @@ * @kind problem * @id cpp/incorrect-pointer-scaling-char * @problem.severity warning + * @security-severity 5.9 * @precision low * @tags security * external/cwe/cwe-468 diff --git a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingVoid.ql b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingVoid.ql index 42bf03e8628..aa267a25f4e 100644 --- a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingVoid.ql +++ b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingVoid.ql @@ -4,6 +4,7 @@ * can cause buffer overflow conditions. * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision medium * @id cpp/suspicious-pointer-scaling-void * @tags security diff --git a/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql b/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql index d356ba7bbc4..7532f7e9fcf 100644 --- a/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql +++ b/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql @@ -5,6 +5,7 @@ * implicitly scaled. * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision high * @id cpp/suspicious-add-sizeof * @tags security diff --git a/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql index 63eca292297..ead50c9620e 100644 --- a/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql +++ b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql @@ -5,6 +5,7 @@ * attack plan. * @kind problem * @problem.severity warning + * @security-severity 3.6 * @precision medium * @id cpp/system-data-exposure * @tags security diff --git a/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.cpp b/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.cpp new file mode 100644 index 00000000000..055aadcedb6 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.cpp @@ -0,0 +1,43 @@ +// BAD: the allocation will throw an unhandled exception +// instead of returning a null pointer. +void bad1(std::size_t length) noexcept { + int* dest = new int[length]; + if(!dest) { + return; + } + std::memset(dest, 0, length); + // ... +} + +// BAD: the allocation won't throw an exception, but +// instead return a null pointer. +void bad2(std::size_t length) noexcept { + try { + int* dest = new(std::nothrow) int[length]; + std::memset(dest, 0, length); + // ... + } catch(std::bad_alloc&) { + // ... + } +} + +// GOOD: the allocation failure is handled appropriately. +void good1(std::size_t length) noexcept { + try { + int* dest = new int[length]; + std::memset(dest, 0, length); + // ... + } catch(std::bad_alloc&) { + // ... + } +} + +// GOOD: the allocation failure is handled appropriately. +void good2(std::size_t length) noexcept { + int* dest = new int[length]; + if(!dest) { + return; + } + std::memset(dest, 0, length); + // ... +} diff --git a/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.qhelp b/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.qhelp new file mode 100644 index 00000000000..9e131e75d4e --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.qhelp @@ -0,0 +1,31 @@ + + + +

    Different overloads of the new operator handle allocation failures in different ways. +If new T fails for some type T, it throws a std::bad_alloc exception, +but new(std::nothrow) T returns a null pointer. If the programmer does not use the corresponding +method of error handling, allocation failure may go unhandled and could cause the program to behave in +unexpected ways.

    + +
    + + +

    Make sure that exceptions are handled appropriately if new T is used. On the other hand, +make sure to handle the possibility of null pointers if new(std::nothrow) T is used.

    + +
    + + + + + + +
  • + CERT C++ Coding Standard: +MEM52-CPP. Detect and handle memory allocation errors. +
  • + +
    +
    diff --git a/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql b/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql new file mode 100644 index 00000000000..b06df90c860 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql @@ -0,0 +1,251 @@ +/** + * @name Incorrect allocation-error handling + * @description Mixing up the failure conditions of 'operator new' and 'operator new(std::nothrow)' can result in unexpected behavior. + * @kind problem + * @id cpp/incorrect-allocation-error-handling + * @problem.severity warning + * @precision medium + * @tags correctness + * security + * external/cwe/cwe-570 + */ + +import cpp +import semmle.code.cpp.valuenumbering.GlobalValueNumbering +import semmle.code.cpp.controlflow.Guards + +/** + * A C++ `delete` or `delete[]` expression. + */ +class DeleteOrDeleteArrayExpr extends Expr { + DeleteOrDeleteArrayExpr() { this instanceof DeleteExpr or this instanceof DeleteArrayExpr } + + DeallocationFunction getDeallocator() { + result = [this.(DeleteExpr).getDeallocator(), this.(DeleteArrayExpr).getDeallocator()] + } + + Destructor getDestructor() { + result = [this.(DeleteExpr).getDestructor(), this.(DeleteArrayExpr).getDestructor()] + } +} + +/** Gets the `Constructor` invoked when `newExpr` allocates memory. */ +Constructor getConstructorForAllocation(NewOrNewArrayExpr newExpr) { + result.getACallToThisFunction() = newExpr.getInitializer() +} + +/** Gets the `Destructor` invoked when `deleteExpr` deallocates memory. */ +Destructor getDestructorForDeallocation(DeleteOrDeleteArrayExpr deleteExpr) { + result = deleteExpr.getDestructor() +} + +/** Holds if the evaluation of `newExpr` may throw an exception. */ +predicate newMayThrow(NewOrNewArrayExpr newExpr) { + functionMayThrow(newExpr.getAllocator()) or + functionMayThrow(getConstructorForAllocation(newExpr)) +} + +/** Holds if the evaluation of `deleteExpr` may throw an exception. */ +predicate deleteMayThrow(DeleteOrDeleteArrayExpr deleteExpr) { + functionMayThrow(deleteExpr.getDeallocator()) or + functionMayThrow(getDestructorForDeallocation(deleteExpr)) +} + +/** + * Holds if the function may throw an exception when called. That is, if the body of the function looks + * like it might throw an exception, and the function does not have a `noexcept` or `throw()` specifier. + */ +predicate functionMayThrow(Function f) { + (not exists(f.getBlock()) or stmtMayThrow(f.getBlock())) and + not f.isNoExcept() and + not f.isNoThrow() +} + +/** Holds if the evaluation of `stmt` may throw an exception. */ +predicate stmtMayThrow(Stmt stmt) { + stmtMayThrow(stmt.(BlockStmt).getAStmt()) + or + convertedExprMayThrow(stmt.(ExprStmt).getExpr()) + or + convertedExprMayThrow(stmt.(DeclStmt).getADeclaration().(Variable).getInitializer().getExpr()) + or + exists(IfStmt ifStmt | ifStmt = stmt | + convertedExprMayThrow(ifStmt.getCondition()) or + stmtMayThrow([ifStmt.getThen(), ifStmt.getElse()]) + ) + or + exists(ConstexprIfStmt constIfStmt | constIfStmt = stmt | + stmtMayThrow([constIfStmt.getThen(), constIfStmt.getElse()]) + ) + or + exists(Loop loop | loop = stmt | + convertedExprMayThrow(loop.getCondition()) or + stmtMayThrow(loop.getStmt()) + ) + or + // The case for `Loop` already checked the condition and the statement. + convertedExprMayThrow(stmt.(RangeBasedForStmt).getUpdate()) + or + // The case for `Loop` already checked the condition and the statement. + exists(ForStmt forStmt | forStmt = stmt | + stmtMayThrow(forStmt.getInitialization()) + or + convertedExprMayThrow(forStmt.getUpdate()) + ) + or + exists(SwitchStmt switchStmt | switchStmt = stmt | + convertedExprMayThrow(switchStmt.getExpr()) or + stmtMayThrow(switchStmt.getStmt()) + ) + or + // NOTE: We don't include `TryStmt` as those exceptions are not "observable" outside the function. + stmtMayThrow(stmt.(Handler).getBlock()) + or + convertedExprMayThrow(stmt.(CoReturnStmt).getExpr()) + or + convertedExprMayThrow(stmt.(ReturnStmt).getExpr()) +} + +/** Holds if the evaluation of `e` (including conversions) may throw an exception. */ +predicate convertedExprMayThrow(Expr e) { + exprMayThrow(e) + or + convertedExprMayThrow(e.getConversion()) +} + +/** Holds if the evaluation of `e` may throw an exception. */ +predicate exprMayThrow(Expr e) { + e instanceof DynamicCast + or + e instanceof TypeidOperator + or + e instanceof ThrowExpr + or + newMayThrow(e) + or + deleteMayThrow(e) + or + convertedExprMayThrow(e.(UnaryOperation).getOperand()) + or + exists(BinaryOperation binOp | binOp = e | + convertedExprMayThrow([binOp.getLeftOperand(), binOp.getRightOperand()]) + ) + or + exists(Assignment assign | assign = e | + convertedExprMayThrow([assign.getLValue(), assign.getRValue()]) + ) + or + exists(CommaExpr comma | comma = e | + convertedExprMayThrow([comma.getLeftOperand(), comma.getRightOperand()]) + ) + or + exists(StmtExpr stmtExpr | stmtExpr = e | + convertedExprMayThrow(stmtExpr.getResultExpr()) or + stmtMayThrow(stmtExpr.getStmt()) + ) + or + convertedExprMayThrow(e.(Conversion).getExpr()) + or + exists(FunctionCall fc | fc = e | + not exists(fc.getTarget()) or + functionMayThrow(fc.getTarget()) or + convertedExprMayThrow(fc.getAnArgument()) + ) +} + +/** The `std::nothrow_t` class and its `bsl` variant. */ +class NoThrowType extends Struct { + NoThrowType() { this.hasGlobalOrStdOrBslName("nothrow_t") } +} + +/** An allocator that might throw an exception. */ +class ThrowingAllocator extends Function { + ThrowingAllocator() { + exists(NewOrNewArrayExpr newExpr | + newExpr.getAllocator() = this and + // Exclude custom overloads of `operator new`. + // What we really want here is to only include the functions that satisfy `functionMayThrow`, but + // there seems to be examples where `throw()` isn't extracted (which causes false positives). + // + // As noted in the QLDoc for `Function.getAllocatorCall`: + // + // "As a rule of thumb, there will be an allocator call precisely when the type + // being allocated has a custom `operator new`, or when an argument list appears + // after the `new` keyword and before the name of the type being allocated. + // + // In particular note that uses of placement-new and nothrow-new will have an + // allocator call." + // + // So we say an allocator might throw if: + // 1. It doesn't have a body + // 2. there isn't a parameter with type `nothrow_t` + // 3. the allocator isn't marked with `throw()` or `noexcept`. + not exists(this.getBlock()) and + not exists(Parameter p | p = this.getAParameter() | + p.getUnspecifiedType() instanceof NoThrowType + ) and + not this.isNoExcept() and + not this.isNoThrow() + ) + } +} + +/** The `std::bad_alloc` exception and its `bsl` variant. */ +class BadAllocType extends Class { + BadAllocType() { this.hasGlobalOrStdOrBslName("bad_alloc") } +} + +/** + * A catch block that catches a `std::bad_alloc` (or any of its superclasses), or a catch + * block that catches every exception (i.e., `catch(...)`). + */ +class BadAllocCatchBlock extends CatchBlock { + BadAllocCatchBlock() { + this.getParameter().getUnspecifiedType().stripType() = + any(BadAllocType badAlloc).getABaseClass*() + or + not exists(this.getParameter()) + } +} + +/** + * Holds if `newExpr` is embedded in a `try` statement with a catch block `catchBlock` that + * catches a `std::bad_alloc` exception, but nothing in the `try` block (including the `newExpr`) + * will throw that exception. + */ +predicate noThrowInTryBlock(NewOrNewArrayExpr newExpr, BadAllocCatchBlock catchBlock) { + exists(TryStmt try | + not stmtMayThrow(try.getStmt()) and + try.getACatchClause() = catchBlock and + newExpr.getEnclosingBlock().getEnclosingBlock*() = try.getStmt() + ) +} + +/** + * Holds if `newExpr` is handles allocation failures by throwing an exception, yet + * the guard condition `guard` compares the result of `newExpr` to a null value. + */ +predicate nullCheckInThrowingNew(NewOrNewArrayExpr newExpr, GuardCondition guard) { + newExpr.getAllocator() instanceof ThrowingAllocator and + ( + // Handles null comparisons. + guard.ensuresEq(globalValueNumber(newExpr).getAnExpr(), any(NullValue null), _, _, _) + or + // Handles `if(ptr)` and `if(!ptr)` cases. + guard = globalValueNumber(newExpr).getAnExpr() + ) +} + +from NewOrNewArrayExpr newExpr, Element element, string msg, string elementString +where + not newExpr.isFromUninstantiatedTemplate(_) and + ( + noThrowInTryBlock(newExpr, element) and + msg = "This allocation cannot throw. $@ is unnecessary." and + elementString = "This catch block" + or + nullCheckInThrowingNew(newExpr, element) and + msg = "This allocation cannot return null. $@ is unnecessary." and + elementString = "This check" + ) +select newExpr, msg, element, elementString diff --git a/cpp/ql/src/Security/CWE/CWE-676/DangerousFunctionOverflow.ql b/cpp/ql/src/Security/CWE/CWE-676/DangerousFunctionOverflow.ql index d883b2e7356..35955665a9e 100644 --- a/cpp/ql/src/Security/CWE/CWE-676/DangerousFunctionOverflow.ql +++ b/cpp/ql/src/Security/CWE/CWE-676/DangerousFunctionOverflow.ql @@ -3,6 +3,7 @@ * @description Use of a standard library function that does not guard against buffer overflow. * @kind problem * @problem.severity error + * @security-severity 10.0 * @precision very-high * @id cpp/dangerous-function-overflow * @tags reliability diff --git a/cpp/ql/src/Security/CWE/CWE-676/DangerousUseOfCin.ql b/cpp/ql/src/Security/CWE/CWE-676/DangerousUseOfCin.ql index 32bdfbaf100..07a7ef1de9b 100644 --- a/cpp/ql/src/Security/CWE/CWE-676/DangerousUseOfCin.ql +++ b/cpp/ql/src/Security/CWE/CWE-676/DangerousUseOfCin.ql @@ -4,6 +4,7 @@ * may be dangerous. * @kind problem * @problem.severity error + * @security-severity 10.0 * @precision high * @id cpp/dangerous-cin * @tags reliability diff --git a/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.ql b/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.ql index 4316fe229b2..4e281b238bc 100644 --- a/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.ql +++ b/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.ql @@ -3,6 +3,7 @@ * @description Use of a standard library function that is not thread-safe. * @kind problem * @problem.severity warning + * @security-severity 10.0 * @precision high * @id cpp/potentially-dangerous-function * @tags reliability diff --git a/cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.ql b/cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.ql index 0d866ff1196..321684dc93c 100644 --- a/cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.ql +++ b/cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.ql @@ -6,6 +6,7 @@ * @kind problem * @id cpp/incorrect-string-type-conversion * @problem.severity error + * @security-severity 5.9 * @precision high * @tags security * external/cwe/cwe-704 diff --git a/cpp/ql/src/Security/CWE/CWE-732/DoNotCreateWorldWritable.ql b/cpp/ql/src/Security/CWE/CWE-732/DoNotCreateWorldWritable.ql index 95790298347..67dce658ed8 100644 --- a/cpp/ql/src/Security/CWE/CWE-732/DoNotCreateWorldWritable.ql +++ b/cpp/ql/src/Security/CWE/CWE-732/DoNotCreateWorldWritable.ql @@ -3,6 +3,7 @@ * @description Creating a file that is world-writable can allow an attacker to write to the file. * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision medium * @id cpp/world-writable-file-creation * @tags security diff --git a/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql index 3e4ebfadac9..72399cec376 100644 --- a/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql +++ b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql @@ -7,6 +7,7 @@ * @id cpp/unsafe-dacl-security-descriptor * @kind problem * @problem.severity error + * @security-severity 5.9 * @precision high * @tags security * external/cwe/cwe-732 diff --git a/cpp/ql/src/Security/CWE/CWE-764/LockOrderCycle.ql b/cpp/ql/src/Security/CWE/CWE-764/LockOrderCycle.ql index f53f84a4fb0..2a0d765f239 100644 --- a/cpp/ql/src/Security/CWE/CWE-764/LockOrderCycle.ql +++ b/cpp/ql/src/Security/CWE/CWE-764/LockOrderCycle.ql @@ -5,6 +5,7 @@ * @kind problem * @id cpp/lock-order-cycle * @problem.severity error + * @security-severity 6.9 * @tags security * external/cwe/cwe-764 * external/cwe/cwe-833 diff --git a/cpp/ql/src/Security/CWE/CWE-764/TwiceLocked.ql b/cpp/ql/src/Security/CWE/CWE-764/TwiceLocked.ql index 0eb7e8451e5..c32e747f3e4 100644 --- a/cpp/ql/src/Security/CWE/CWE-764/TwiceLocked.ql +++ b/cpp/ql/src/Security/CWE/CWE-764/TwiceLocked.ql @@ -5,6 +5,7 @@ * @kind problem * @id cpp/twice-locked * @problem.severity error + * @security-severity 6.9 * @precision low * @tags security * external/cwe/cwe-764 diff --git a/cpp/ql/src/Security/CWE/CWE-764/UnreleasedLock.ql b/cpp/ql/src/Security/CWE/CWE-764/UnreleasedLock.ql index 1c0ee4a500f..8f3d9e92149 100644 --- a/cpp/ql/src/Security/CWE/CWE-764/UnreleasedLock.ql +++ b/cpp/ql/src/Security/CWE/CWE-764/UnreleasedLock.ql @@ -5,6 +5,7 @@ * @kind problem * @id cpp/unreleased-lock * @problem.severity error + * @security-severity 6.9 * @precision low * @tags security * external/cwe/cwe-764 diff --git a/cpp/ql/src/Security/CWE/CWE-807/TaintedCondition.ql b/cpp/ql/src/Security/CWE/CWE-807/TaintedCondition.ql index e60f592b2af..08a5ceb49db 100644 --- a/cpp/ql/src/Security/CWE/CWE-807/TaintedCondition.ql +++ b/cpp/ql/src/Security/CWE/CWE-807/TaintedCondition.ql @@ -5,6 +5,7 @@ * attack. * @kind path-problem * @problem.severity warning + * @security-severity 6.4 * @precision medium * @id cpp/tainted-permissions-check * @tags security diff --git a/cpp/ql/src/Security/CWE/CWE-835/InfiniteLoopWithUnsatisfiableExitCondition.ql b/cpp/ql/src/Security/CWE/CWE-835/InfiniteLoopWithUnsatisfiableExitCondition.ql index e088b9a666d..cd85179d14d 100644 --- a/cpp/ql/src/Security/CWE/CWE-835/InfiniteLoopWithUnsatisfiableExitCondition.ql +++ b/cpp/ql/src/Security/CWE/CWE-835/InfiniteLoopWithUnsatisfiableExitCondition.ql @@ -6,6 +6,7 @@ * @kind problem * @id cpp/infinite-loop-with-unsatisfiable-exit-condition * @problem.severity warning + * @security-severity 3.6 * @tags security * external/cwe/cwe-835 */ diff --git a/cpp/ql/src/Summary/LinesOfCode.ql b/cpp/ql/src/Summary/LinesOfCode.ql index 3b2aa2ac4c9..2d816b349e8 100644 --- a/cpp/ql/src/Summary/LinesOfCode.ql +++ b/cpp/ql/src/Summary/LinesOfCode.ql @@ -4,6 +4,7 @@ * @description The total number of lines of C/C++ code across all files, including system headers, libraries, and auto-generated files. This is a useful metric of the size of a database. For all files that were seen during the build, this query counts the lines of code, excluding whitespace or comments. * @kind metric * @tags summary + * lines-of-code */ import cpp diff --git a/cpp/ql/src/Summary/LinesOfUserCode.ql b/cpp/ql/src/Summary/LinesOfUserCode.ql new file mode 100644 index 00000000000..67d3aa6a8e0 --- /dev/null +++ b/cpp/ql/src/Summary/LinesOfUserCode.ql @@ -0,0 +1,17 @@ +/** + * @name Total lines of user written C/C++ code in the database + * @description The total number of lines of C/C++ code from the source code directory, excluding auto-generated files. This query counts the lines of code, excluding whitespace or comments. Note: If external libraries are included in the codebase either in a checked-in virtual environment or as vendored code, that will currently be counted as user written code. + * @kind metric + * @tags summary + * lines-of-code + * @id cpp/summary/lines-of-user-code + */ + +import cpp +import semmle.code.cpp.AutogeneratedFile + +select sum(File f | + f.fromSource() and exists(f.getRelativePath()) and not f instanceof AutogeneratedFile + | + f.getMetrics().getNumberOfLinesOfCode() + ) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.cpp new file mode 100644 index 00000000000..3b85835fff9 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.cpp @@ -0,0 +1,28 @@ +#include +#include +#include + +int main(int argc, char ** argv) { + + if (argc != 2) { + throw std::runtime_error("Give me a string!"); + } + + pqxx::connection c; + pqxx::work w(c); + + // BAD + char *userName = argv[1]; + char query1[1000] = {0}; + sprintf(query1, "SELECT UID FROM USERS where name = \"%s\"", userName); + pqxx::row r = w.exec1(query1); + w.commit(); + std::cout << r[0].as() << std::endl; + + // GOOD + pqxx::result r2 = w.exec("SELECT " + w.quote(argv[1])); + w.commit(); + std::cout << r2[0][0].c_str() << std::endl; + + return 0; +} diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.qhelp new file mode 100644 index 00000000000..1c01b3e4f3a --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.qhelp @@ -0,0 +1,31 @@ + + + +

    The code passes user input as part of a SQL query without escaping special elements. +It generates a SQL query to Postgres using sprintf, +with the user-supplied data directly passed as an argument +to sprintf. This leaves the code vulnerable to attack by SQL Injection.

    + +
    + + +

    Use a library routine to escape characters in the user-supplied +string before converting it to SQL. Use esc and quote pqxx library functions.

    + +
    + + + + + + +
  • MSDN Library: SQL Injection.
  • + + + + +
    +
    diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql new file mode 100644 index 00000000000..8de55953b15 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql @@ -0,0 +1,113 @@ +/** + * @name Uncontrolled data in SQL query to Postgres + * @description Including user-supplied data in a SQL query to Postgres + * without neutralizing special elements can make code + * vulnerable to SQL Injection. + * @kind path-problem + * @problem.severity error + * @precision high + * @id cpp/sql-injection-via-pqxx + * @tags security + * external/cwe/cwe-089 + */ + +import cpp +import semmle.code.cpp.security.Security +import semmle.code.cpp.dataflow.TaintTracking +import DataFlow::PathGraph + +predicate pqxxTransationClassNames(string className, string namespace) { + namespace = "pqxx" and + className in [ + "dbtransaction", "nontransaction", "basic_robusttransaction", "robusttransaction", + "subtransaction", "transaction", "basic_transaction", "transaction_base", "work" + ] +} + +predicate pqxxConnectionClassNames(string className, string namespace) { + namespace = "pqxx" and + className in ["connection_base", "basic_connection", "connection"] +} + +predicate pqxxTransactionSqlArgument(string function, int arg) { + function = "exec" and arg = 0 + or + function = "exec0" and arg = 0 + or + function = "exec1" and arg = 0 + or + function = "exec_n" and arg = 1 + or + function = "exec_params" and arg = 0 + or + function = "exec_params0" and arg = 0 + or + function = "exec_params1" and arg = 0 + or + function = "exec_params_n" and arg = 1 + or + function = "query_value" and arg = 0 + or + function = "stream" and arg = 0 +} + +predicate pqxxConnectionSqlArgument(string function, int arg) { function = "prepare" and arg = 1 } + +Expr getPqxxSqlArgument() { + exists(FunctionCall fc, Expr e, int argIndex, UserType t | + // examples: 'work' for 'work.exec(...)'; '->' for 'tx->exec()'. + e = fc.getQualifier() and + // to find ConnectionHandle/TransationHandle and similar classes which override '->' operator behavior + // and return pointer to a connection/transation object + e.getType().refersTo(t) and + // transaction exec and connection prepare variations + ( + pqxxTransationClassNames(t.getName(), t.getNamespace().getName()) and + pqxxTransactionSqlArgument(fc.getTarget().getName(), argIndex) + or + pqxxConnectionClassNames(t.getName(), t.getNamespace().getName()) and + pqxxConnectionSqlArgument(fc.getTarget().getName(), argIndex) + ) and + result = fc.getArgument(argIndex) + ) +} + +predicate pqxxEscapeArgument(string function, int arg) { + arg = 0 and + function in ["esc", "esc_raw", "quote", "quote_raw", "quote_name", "quote_table", "esc_like"] +} + +predicate isEscapedPqxxArgument(Expr argExpr) { + exists(FunctionCall fc, Expr e, int argIndex, UserType t | + // examples: 'work' for 'work.exec(...)'; '->' for 'tx->exec()'. + e = fc.getQualifier() and + // to find ConnectionHandle/TransationHandle and similar classes which override '->' operator behavior + // and return pointer to a connection/transation object + e.getType().refersTo(t) and + // transaction and connection escape functions + ( + pqxxTransationClassNames(t.getName(), t.getNamespace().getName()) or + pqxxConnectionClassNames(t.getName(), t.getNamespace().getName()) + ) and + pqxxEscapeArgument(fc.getTarget().getName(), argIndex) and + // is escaped arg == argExpr + argExpr = fc.getArgument(argIndex) + ) +} + +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "SqlPqxxTainted" } + + override predicate isSource(DataFlow::Node source) { isUserInput(source.asExpr(), _) } + + override predicate isSink(DataFlow::Node sink) { sink.asExpr() = getPqxxSqlArgument() } + + override predicate isSanitizer(DataFlow::Node node) { isEscapedPqxxArgument(node.asExpr()) } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, Configuration config, string taintCause +where + config.hasFlowPath(source, sink) and + isUserInput(source.getNode().asExpr(), taintCause) +select sink, source, sink, "This argument to a SQL query function is derived from $@", source, + "user input (" + taintCause + ")" diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c new file mode 100644 index 00000000000..53a50841977 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.c @@ -0,0 +1,14 @@ +while(intIndex > 2) +{ + ... + intIndex--; + ... +} // GOOD: correct cycle +... +while(intIndex > 2) +{ + ... + int intIndex; + intIndex--; + ... +} // BAD: the variable used in the condition does not change. diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp new file mode 100644 index 00000000000..5234212f7ca --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.qhelp @@ -0,0 +1,26 @@ + + + +

    Using variables with the same name is dangerous. However, such a situation inside the while loop can create an infinite loop exhausting resources. Requires the attention of developers.

    + +
    + +

    We recommend not to use local variables inside a loop if their names are the same as the variables in the condition of this loop.

    + +
    + +

    The following example demonstrates an erroneous and corrected use of a local variable within a loop.

    + + +
    + + +
  • + CERT C Coding Standard: + DCL01-C. Do not reuse variable names in subscopes. +
  • + +
    +
    diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql new file mode 100644 index 00000000000..e73f36145c6 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql @@ -0,0 +1,60 @@ +/** + * @name Errors When Using Variable Declaration Inside Loop + * @description Using variables with the same name is dangerous. + * However, such a situation inside the while loop can create an infinite loop exhausting resources. + * Requires the attention of developers. + * @kind problem + * @id cpp/errors-when-using-variable-declaration-inside-loop + * @problem.severity warning + * @precision medium + * @tags correctness + * security + * external/cwe/cwe-1126 + */ + +import cpp + +/** + * Errors when using a variable declaration inside a loop. + */ +class DangerousWhileLoop extends WhileStmt { + Expr exp; + Declaration dl; + + DangerousWhileLoop() { + this = dl.getParentScope().(BlockStmt).getParent*() and + exp = this.getCondition().getAChild*() and + not exp instanceof PointerFieldAccess and + not exp instanceof ValueFieldAccess and + exp.(VariableAccess).getTarget().getName() = dl.getName() and + not exp.getParent*() instanceof FunctionCall + } + + Declaration getDeclaration() { result = dl } + + /** Holds when there are changes to the variables involved in the condition. */ + predicate isUseThisVariable() { + exists(Variable v | + this.getCondition().getAChild*().(VariableAccess).getTarget() = v and + ( + exists(Assignment aexp | + this = aexp.getEnclosingStmt().getParentStmt*() and + ( + aexp.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() = v + or + aexp.getLValue().(VariableAccess).getTarget() = v + ) + ) + or + exists(CrementOperation crm | + this = crm.getEnclosingStmt().getParentStmt*() and + crm.getOperand().(VariableAccess).getTarget() = v + ) + ) + ) + } +} + +from DangerousWhileLoop lp +where not lp.isUseThisVariable() +select lp.getDeclaration(), "A variable with this name is used in the $@ condition.", lp, "loop" diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.c b/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.c new file mode 100644 index 00000000000..ce7fcc6846e --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.c @@ -0,0 +1,14 @@ +... + buf = malloc(intSize); +... + free(buf); + buf = NULL; + if(buf) free(buf); // GOOD +... + +... + buf = malloc(intSize); +... + free(buf); + if(buf) free(buf); // BAD: the cleanup function does not zero out the pointer +... diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.qhelp new file mode 100644 index 00000000000..69ef348be34 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.qhelp @@ -0,0 +1,26 @@ + + + +

    Freeing a previously allocated resource twice can lead to various vulnerabilities in the program.

    + +
    + +

    We recommend that you exclude situations of possible double release. For example, use the assignment NULL to a freed variable.

    + +
    + +

    The following example demonstrates an erroneous and corrected use of freeing a pointer.

    + + +
    + + +
  • + CERT C Coding Standard: + MEM30-C. Do not access freed memory. +
  • + +
    +
    diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.ql b/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.ql new file mode 100644 index 00000000000..0544c2aefd5 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-415/DoubleFree.ql @@ -0,0 +1,43 @@ +/** + * @name Errors When Double Free + * @description Freeing a previously allocated resource twice can lead to various vulnerabilities in the program. + * @kind problem + * @id cpp/double-free + * @problem.severity warning + * @precision medium + * @tags security + * external/cwe/cwe-415 + */ + +import cpp + +from FunctionCall fc, FunctionCall fc2, LocalScopeVariable v +where + freeCall(fc, v.getAnAccess()) and + freeCall(fc2, v.getAnAccess()) and + fc != fc2 and + fc.getASuccessor*() = fc2 and + not exists(Expr exptmp | + (exptmp = v.getAnAssignedValue() or exptmp.(AddressOfExpr).getOperand() = v.getAnAccess()) and + exptmp = fc.getASuccessor*() and + exptmp = fc2.getAPredecessor*() + ) and + not exists(FunctionCall fctmp | + not fctmp instanceof DeallocationExpr and + fctmp = fc.getASuccessor*() and + fctmp = fc2.getAPredecessor*() and + fctmp.getAnArgument().(VariableAccess).getTarget() = v + ) and + ( + fc.getTarget().hasGlobalOrStdName("realloc") and + ( + not fc.getParent*() instanceof IfStmt and + not exists(IfStmt iftmp | + iftmp.getCondition().getAChild*().(VariableAccess).getTarget().getAnAssignedValue() = fc + ) + ) + or + not fc.getTarget().hasGlobalOrStdName("realloc") + ) +select fc2.getArgument(0), + "This pointer may have already been cleared in the line " + fc.getLocation().getStartLine() + "." diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.cpp deleted file mode 100644 index df69886e97b..00000000000 --- a/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// BAD: on memory allocation error, the program terminates. -void badFunction(const int *source, std::size_t length) noexcept { - int * dest = new int[length]; - std::memset(dest, 0, length); -// .. -} -// GOOD: memory allocation error will be handled. -void goodFunction(const int *source, std::size_t length) noexcept { - try { - int * dest = new int[length]; - } catch(std::bad_alloc) { - // ... - } - std::memset(dest, 0, length); -// .. -} -// BAD: memory allocation error will not be handled. -void badFunction(const int *source, std::size_t length) noexcept { - try { - int * dest = new (std::nothrow) int[length]; - } catch(std::bad_alloc) { - // ... - } - std::memset(dest, 0, length); -// .. -} -// GOOD: memory allocation error will be handled. -void goodFunction(const int *source, std::size_t length) noexcept { - int * dest = new (std::nothrow) int[length]; - if (!dest) { - return; - } - std::memset(dest, 0, length); -// .. -} diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.qhelp deleted file mode 100644 index 9e6cb2d89ce..00000000000 --- a/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.qhelp +++ /dev/null @@ -1,27 +0,0 @@ - - - -

    When using the new operator to allocate memory, you need to pay attention to the different ways of detecting errors. ::operator new(std::size_t) throws an exception on error, whereas ::operator new(std::size_t, const std::nothrow_t &) returns zero on error. The programmer can get confused and check the error that occurs when allocating memory incorrectly. That can lead to an unhandled program termination or to a violation of the program logic.

    - -
    - - -

    Use the correct error detection method corresponding with the memory allocation.

    - -
    - -

    The following example demonstrates various approaches to detecting memory allocation errors using the new operator.

    - - -
    - - -
  • - CERT C++ Coding Standard: -MEM52-CPP. Detect and handle memory allocation errors. -
  • - -
    -
    diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.ql b/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.ql deleted file mode 100644 index 4869da7e6f3..00000000000 --- a/cpp/ql/src/experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.ql +++ /dev/null @@ -1,87 +0,0 @@ -/** - * @name Detect And Handle Memory Allocation Errors - * @description --::operator new(std::size_t) throws an exception on error, and ::operator new(std::size_t, const std::nothrow_t &) returns zero on error. - * --the programmer can get confused when check the error that occurs when allocating memory incorrectly. - * @kind problem - * @id cpp/detect-and-handle-memory-allocation-errors - * @problem.severity warning - * @precision medium - * @tags correctness - * security - * external/cwe/cwe-570 - */ - -import cpp - -/** - * Lookup if condition compare with 0 - */ -class IfCompareWithZero extends IfStmt { - IfCompareWithZero() { - this.getCondition().(EQExpr).getAChild().getValue() = "0" - or - this.getCondition().(NEExpr).getAChild().getValue() = "0" and - this.hasElse() - or - this.getCondition().(NEExpr).getAChild().getValue() = "0" and - this.getThen().getAChild*() instanceof ReturnStmt - } -} - -/** - * lookup for calls to `operator new`, with incorrect error handling. - */ -class WrongCheckErrorOperatorNew extends FunctionCall { - Expr exp; - - WrongCheckErrorOperatorNew() { - this = exp.(NewOrNewArrayExpr).getAChild().(FunctionCall) and - ( - this.getTarget().hasGlobalOrStdName("operator new") - or - this.getTarget().hasGlobalOrStdName("operator new[]") - ) - } - - /** - * Holds if handler `try ... catch` exists. - */ - predicate isExistsTryCatchBlock() { - exists(TryStmt ts | this.getEnclosingStmt() = ts.getStmt().getAChild*()) - } - - /** - * Holds if results call `operator new` check in `operator if`. - */ - predicate isExistsIfCondition() { - exists(IfCompareWithZero ifc, AssignExpr aex, Initializer it | - // call `operator new` directly from the condition of `operator if`. - this = ifc.getCondition().getAChild*() - or - // check results call `operator new` with variable appropriation - postDominates(ifc, this) and - aex.getAChild() = exp and - ifc.getCondition().getAChild().(VariableAccess).getTarget() = - aex.getLValue().(VariableAccess).getTarget() - or - // check results call `operator new` with declaration variable - postDominates(ifc, this) and - exp = it.getExpr() and - it.getDeclaration() = ifc.getCondition().getAChild().(VariableAccess).getTarget() - ) - } - - /** - * Holds if `(std::nothrow)` or `(std::noexcept)` exists in call `operator new`. - */ - predicate isExistsNothrow() { getTarget().isNoExcept() or getTarget().isNoThrow() } -} - -from WrongCheckErrorOperatorNew op -where - // use call `operator new` with `(std::nothrow)` and checking error using `try ... catch` block and not `operator if` - op.isExistsNothrow() and not op.isExistsIfCondition() and op.isExistsTryCatchBlock() - or - // use call `operator new` without `(std::nothrow)` and checking error using `operator if` and not `try ... catch` block - not op.isExistsNothrow() and not op.isExistsTryCatchBlock() and op.isExistsIfCondition() -select op, "memory allocation error check is incorrect or missing" diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.c b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.c new file mode 100644 index 00000000000..1f1f10b89cc --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.c @@ -0,0 +1,17 @@ +while(flagsLoop) +{ + ... + if(flagsIf) break; + ... +}while(flagsLoop); // BAD: when exiting through `break`, it is possible to get into an eternal loop. +... +while(flagsLoop) +{ + ... + if(flagsIf) break; + ... +} // GOOD: correct cycle +... +if(intA+intB) return 1; // BAD: possibly no comparison +... +if(intA+intB>intC) return 1; // GOOD: correct comparison diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.qhelp new file mode 100644 index 00000000000..4167ce57d65 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.qhelp @@ -0,0 +1,28 @@ + + + +

    In some situations, after code refactoring, parts of the old constructs may remain. They are correctly accepted by the compiler, but can critically affect program execution. For example, if you switch from `do {...} while ();` to `while () {...}` forgetting to remove the old construct completely, you get `while(){...}while();` which may be vulnerable. These code snippets look suspicious and require the developer's attention.

    + + +
    + + +

    We recommend that you use more explicit code transformations.

    + +
    + +

    The following example demonstrates the erroneous and corrected sections of the code.

    + + +
    + + +
  • + CWE Common Weakness Enumeration: + CWE-691: Insufficient Control Flow Management. +
  • + +
    +
    diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.ql b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.ql new file mode 100644 index 00000000000..163305dd039 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.ql @@ -0,0 +1,119 @@ +/** + * @name Errors After Refactoring + * @description --In some situations, after code refactoring, parts of the old constructs may remain. + * --They are correctly accepted by the compiler, but can critically affect program execution. + * --For example, if you switch from `do {...} while ();` to `while () {...}` with errors, you run the risk of running out of resources. + * --These code snippets look suspicious and require the developer's attention. + * @kind problem + * @id cpp/errors-after-refactoring + * @problem.severity warning + * @precision medium + * @tags correctness + * security + * external/cwe/cwe-691 + */ + +import cpp +import semmle.code.cpp.valuenumbering.HashCons +import semmle.code.cpp.valuenumbering.GlobalValueNumbering + +/** + * Using `while` directly after the body of another` while`. + */ +class UsingWhileAfterWhile extends WhileStmt { + /** + * Using a loop call after another loop has finished running can result in an eternal loop. + * For example, perhaps as a result of refactoring, the `do ... while ()` loop was incorrectly corrected. + * Even in the case of deliberate use of such an expression, it is better to correct it. + */ + UsingWhileAfterWhile() { + exists(WhileStmt wh1 | + wh1.getStmt().getAChild*().(BreakStmt).(ControlFlowNode).getASuccessor().getASuccessor() = + this and + hashCons(wh1.getCondition()) = hashCons(this.getCondition()) and + this.getStmt() instanceof EmptyStmt + ) + or + exists(ForStmt fr1 | + fr1.getStmt().getAChild*().(BreakStmt).(ControlFlowNode).getASuccessor().getASuccessor() = + this and + hashCons(fr1.getCondition()) = hashCons(this.getCondition()) and + this.getStmt() instanceof EmptyStmt + ) + } +} + +/** + * Using arithmetic in a condition. + */ +class UsingArithmeticInComparison extends BinaryArithmeticOperation { + /** + * Using arithmetic operations in a comparison operation can be dangerous. + * For example, part of the comparison may have been lost as a result of refactoring. + * Even if you deliberately use such an expression, it is better to add an explicit comparison. + */ + UsingArithmeticInComparison() { + this.getParent*() instanceof IfStmt and + not this.getAChild*().isConstant() and + not this.getParent*() instanceof Call and + not this.getParent*() instanceof AssignExpr and + not this.getParent*() instanceof ArrayExpr and + not this.getParent*() instanceof RemExpr and + not this.getParent*() instanceof AssignBitwiseOperation and + not this.getParent*() instanceof AssignArithmeticOperation and + not this.getParent*() instanceof EqualityOperation and + not this.getParent*() instanceof RelationalOperation + } + + /** Holds when the expression is inside the loop body. */ + predicate insideTheLoop() { exists(Loop lp | lp.getStmt().getAChild*() = this.getParent*()) } + + /** Holds when the expression is used in binary operations. */ + predicate workingWithValue() { + this.getParent*() instanceof BinaryBitwiseOperation or + this.getParent*() instanceof NotExpr + } + + /** Holds when the expression contains a pointer. */ + predicate workingWithPointer() { + this.getAChild*().getFullyConverted().getType() instanceof DerivedType + } + + /** Holds when a null comparison expression exists. */ + predicate compareWithZero() { + exists(Expr exp | + exp instanceof ComparisonOperation and + ( + globalValueNumber(exp.getAChild*()) = globalValueNumber(this) or + hashCons(exp.getAChild*()) = hashCons(this) + ) and + ( + exp.(ComparisonOperation).getLeftOperand().getValue() = "0" or + exp.(ComparisonOperation).getRightOperand().getValue() = "0" + ) + ) + } + + /** Holds when a comparison expression exists. */ + predicate compareWithOutZero() { + exists(Expr exp | + exp instanceof ComparisonOperation and + ( + globalValueNumber(exp.getAChild*()) = globalValueNumber(this) or + hashCons(exp.getAChild*()) = hashCons(this) + ) + ) + } +} + +from Expr exp +where + exp instanceof UsingArithmeticInComparison and + not exp.(UsingArithmeticInComparison).workingWithValue() and + not exp.(UsingArithmeticInComparison).workingWithPointer() and + not exp.(UsingArithmeticInComparison).insideTheLoop() and + not exp.(UsingArithmeticInComparison).compareWithZero() and + exp.(UsingArithmeticInComparison).compareWithOutZero() + or + exists(WhileStmt wst | wst instanceof UsingWhileAfterWhile and exp = wst.getCondition()) +select exp, "this expression needs your attention" diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementWhenUsingBitOperations.c b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementWhenUsingBitOperations.c new file mode 100644 index 00000000000..27631843118 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementWhenUsingBitOperations.c @@ -0,0 +1,4 @@ +if(len>0 & memset(buf,0,len)) return 1; // BAD: `memset` will be called regardless of the value of the `len` variable. moreover, one cannot be sure that it will happen after verification +... +if(len>0 && memset(buf,0,len)) return 1; // GOOD: `memset` will be called after the `len` variable has been checked. +... diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementWhenUsingBitOperations.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementWhenUsingBitOperations.qhelp new file mode 100644 index 00000000000..f49d5a4fe78 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementWhenUsingBitOperations.qhelp @@ -0,0 +1,28 @@ + + + +

    Using bitwise operations can be a mistake in some situations. For example, if parameters are evaluated in an expression and the function should be called only upon certain test results. These bitwise operations look suspicious and require developer attention.

    + + +
    + + +

    We recommend that you evaluate the correctness of using the specified bit operations.

    + +
    + +

    The following example demonstrates the erroneous and fixed use of bit and logical operations.

    + + +
    + + +
  • + CWE Common Weakness Enumeration: + CWE-691: Insufficient Control Flow Management. +
  • + +
    +
    diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementWhenUsingBitOperations.ql b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementWhenUsingBitOperations.ql new file mode 100644 index 00000000000..72d7625b517 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementWhenUsingBitOperations.ql @@ -0,0 +1,78 @@ +/** + * @name Errors When Using Bit Operations + * @description Unlike the binary operations `||` and `&&`, there is no sequence point after evaluating an + * operand of a bitwise operation like `|` or `&`. If left-to-right evaluation is expected this may be confusing. + * @kind problem + * @id cpp/errors-when-using-bit-operations + * @problem.severity warning + * @precision medium + * @tags correctness + * security + * external/cwe/cwe-691 + */ + +import cpp +import semmle.code.cpp.valuenumbering.GlobalValueNumbering + +/** + * Dangerous uses of bit operations. + * For example: `if(intA>0 & intA<10 & charBuf&myFunc(charBuf[intA]))`. + * In this case, the function will be called in any case, and even the sequence of the call is not guaranteed. + */ +class DangerousBitOperations extends BinaryBitwiseOperation { + FunctionCall bfc; + + /** + * The assignment indicates the conscious use of the bit operator. + * Use in comparison, conversion, or return value indicates conscious use of the bit operator. + * The use of shifts and bitwise operations on any element of an expression indicates a conscious use of the bitwise operator. + */ + DangerousBitOperations() { + bfc = this.getRightOperand() and + not this.getParent*() instanceof Assignment and + not this.getParent*() instanceof Initializer and + not this.getParent*() instanceof ReturnStmt and + not this.getParent*() instanceof EqualityOperation and + not this.getParent*() instanceof UnaryLogicalOperation and + not this.getParent*() instanceof BinaryLogicalOperation and + not this.getAChild*() instanceof BitwiseXorExpr and + not this.getAChild*() instanceof LShiftExpr and + not this.getAChild*() instanceof RShiftExpr + } + + /** Holds when part of a bit expression is used in a logical operation. */ + predicate useInLogicalOperations() { + exists(BinaryLogicalOperation blop, Expr exp | + blop.getAChild*() = exp and + exp.(FunctionCall).getTarget() = bfc.getTarget() and + not exp.getParent() instanceof ComparisonOperation and + not exp.getParent() instanceof BinaryBitwiseOperation + ) + } + + /** Holds when part of a bit expression is used as part of another supply. For example, as an argument to another function. */ + predicate useInOtherCalls() { + bfc.hasQualifier() or + bfc.getTarget() instanceof Operator or + exists(FunctionCall fc | fc.getAnArgument().getAChild*() = this) or + bfc.getTarget() instanceof BuiltInFunction + } + + /** Holds when the bit expression contains both arguments and a function call. */ + predicate dangerousArgumentChecking() { + not this.getLeftOperand() instanceof Call and + globalValueNumber(this.getLeftOperand().getAChild*()) = globalValueNumber(bfc.getAnArgument()) + } + + /** Holds when function calls are present in the bit expression. */ + predicate functionCallsInBitsExpression() { + this.getLeftOperand().getAChild*() instanceof FunctionCall + } +} + +from DangerousBitOperations dbo +where + not dbo.useInOtherCalls() and + dbo.useInLogicalOperations() and + (not dbo.functionCallsInBitsExpression() or dbo.dangerousArgumentChecking()) +select dbo, "This bitwise operation appears in a context where a Boolean operation is expected." diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.c b/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.c new file mode 100644 index 00000000000..8458d82f7ad --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.c @@ -0,0 +1,11 @@ +if(len=funcReadData()==0) return 1; // BAD: variable `len` will not equal the value returned by function `funcReadData()` +... +if((len=funcReadData())==0) return 1; // GOOD: variable `len` equal the value returned by function `funcReadData()` +... +bool a=true; +a++;// BAD: variable `a` does not change its meaning +bool b; +b=-a;// BAD: variable `b` equal `true` +... +a=false;// GOOD: variable `a` equal `false` +b=!a;// GOOD: variable `b` equal `false` diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.qhelp new file mode 100644 index 00000000000..8114da831fe --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.qhelp @@ -0,0 +1,28 @@ + + + +

    Finding places of confusing use of boolean type. For example, a unary minus does not work before a boolean type and an increment always gives true.

    + + +
    + + +

    we recommend making the code simpler.

    + +
    + +

    The following example demonstrates erroneous and fixed methods for using a boolean data type.

    + + +
    + + +
  • + CERT C Coding Standard: + EXP00-C. Use parentheses for precedence of operation. +
  • + +
    +
    diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.ql b/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.ql new file mode 100644 index 00000000000..4f30f112eb0 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.ql @@ -0,0 +1,54 @@ +/** + * @name Operator Precedence Logic Error When Use Bool Type + * @description --Finding places of confusing use of boolean type. + * --For example, a unary minus does not work before a boolean type and an increment always gives true. + * @kind problem + * @id cpp/operator-precedence-logic-error-when-use-bool-type + * @problem.severity warning + * @precision medium + * @tags correctness + * security + * external/cwe/cwe-783 + * external/cwe/cwe-480 + */ + +import cpp +import semmle.code.cpp.valuenumbering.HashCons + +/** Holds if `exp` increments a boolean value. */ +predicate incrementBoolType(IncrementOperation exp) { + exp.getOperand().getType() instanceof BoolType +} + +/** Holds if `exp` applies the unary minus operator to a boolean type. */ +predicate revertSignBoolType(UnaryMinusExpr exp) { + exp.getAnOperand().getType() instanceof BoolType and + exp.getFullyConverted().getType() instanceof BoolType +} + +/** Holds, if this is an expression, uses comparison and assignment outside of execution precedence. */ +predicate assignBoolType(Expr exp) { + exists(ComparisonOperation co | + exp.(AssignExpr).getRValue() = co and + exp.isCondition() and + not co.isParenthesised() and + not exp.(AssignExpr).getLValue().getType() instanceof BoolType and + not exists(Expr exbl | + hashCons(exbl.(AssignExpr).getLValue()) = hashCons(exp.(AssignExpr).getLValue()) and + not exbl.isCondition() and + exbl.(AssignExpr).getRValue().getType() instanceof BoolType and + exbl.(AssignExpr).getLValue().getType() = exp.(AssignExpr).getLValue().getType() + ) and + co.getLeftOperand() instanceof FunctionCall and + not co.getRightOperand().getType() instanceof BoolType and + not co.getRightOperand().getValue() = "0" and + not co.getRightOperand().getValue() = "1" + ) +} + +from Expr exp +where + incrementBoolType(exp) or + revertSignBoolType(exp) or + assignBoolType(exp) +select exp, "this expression needs attention" diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.ql b/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.ql index 012109074e9..e4577968730 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.ql @@ -3,7 +3,7 @@ * @description The expression `buffer [strlen (buffer)] = 0` is potentially dangerous, if the variable `buffer` does not have a terminal zero, then access beyond the bounds of the allocated memory is possible, which will lead to undefined behavior. * If terminal zero is present, then the specified expression is meaningless. * @kind problem - * @id cpp/access-memory-location-after-end-buffer + * @id cpp/access-memory-location-after-end-buffer-strlen * @problem.severity warning * @precision medium * @tags correctness diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.c b/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.c deleted file mode 100644 index 060a22b5c18..00000000000 --- a/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.c +++ /dev/null @@ -1,4 +0,0 @@ - -strncat(dest, source, sizeof(dest) - strlen(dest)); // BAD: writes a zero byte past the `dest` buffer. - -strncat(dest, source, sizeof(dest) - strlen(dest) -1); // GOOD: Reserves space for the zero byte. diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.qhelp deleted file mode 100644 index 5c2154097ec..00000000000 --- a/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.qhelp +++ /dev/null @@ -1,32 +0,0 @@ - - - -

    The standard library function strncat(dest, source, count) appends the source string to the dest string. count specifies the maximum number of characters to append and must be less than the remaining space in the target buffer. Calls of the form strncat (dest, source, sizeof (dest) - strlen (dest)) set the third argument to one more than possible. So when the dest is full, the expression sizeof (dest) - strlen (dest) will be equal to one, and not zero as the programmer might think. Making a call of this type may result in a zero byte being written just outside the dest buffer.

    - - -
    - - -

    We recommend subtracting one from the third argument. For example, replace strncat(dest, source, sizeof(dest)-strlen(dest)) with strncat(dest, source, sizeof(dest)-strlen(dest)-1).

    - -
    - -

    The following example demonstrates an erroneous and corrected use of the strncat function.

    - - -
    - - -
  • - CERT C Coding Standard: -STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator. -
  • -
  • - CERT C Coding Standard: - ARR30-C. Do not form or use out-of-bounds pointers or array subscripts. -
  • - -
    -
    diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.ql b/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.ql deleted file mode 100644 index 5311ffe2708..00000000000 --- a/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.ql +++ /dev/null @@ -1,64 +0,0 @@ -/** - * @name Access Of Memory Location After The End Of A Buffer Using Strncat - * @description Calls of the form `strncat(dest, source, sizeof (dest) - strlen (dest))` set the third argument to one more than possible. So when `dest` is full, the expression `sizeof(dest) - strlen (dest)` will be equal to one, and not zero as the programmer might think. Making a call of this type may result in a zero byte being written just outside the `dest` buffer. - * @kind problem - * @id cpp/access-memory-location-after-end-buffer - * @problem.severity warning - * @precision medium - * @tags correctness - * security - * external/cwe/cwe-788 - */ - -import cpp -import semmle.code.cpp.valuenumbering.GlobalValueNumbering - -/** - * A call to `strncat` of the form `strncat(buff, str, someExpr - strlen(buf))`, for some expression `someExpr` equal to `sizeof(buff)`. - */ -class WrongCallStrncat extends FunctionCall { - Expr leftsomeExpr; - - WrongCallStrncat() { - this.getTarget().hasGlobalOrStdName("strncat") and - // the expression of the first argument in `strncat` and `strnlen` is identical - globalValueNumber(this.getArgument(0)) = - globalValueNumber(this.getArgument(2).(SubExpr).getRightOperand().(StrlenCall).getStringExpr()) and - // using a string constant often speaks of manually calculating the length of the required buffer. - ( - not this.getArgument(1) instanceof StringLiteral and - not this.getArgument(1) instanceof CharLiteral - ) and - // for use in predicates - leftsomeExpr = this.getArgument(2).(SubExpr).getLeftOperand() - } - - /** - * Holds if the left side of the expression `someExpr` equal to `sizeof(buf)`. - */ - predicate isExpressionEqualSizeof() { - // the left side of the expression `someExpr` is `sizeof(buf)`. - globalValueNumber(this.getArgument(0)) = - globalValueNumber(leftsomeExpr.(SizeofExprOperator).getExprOperand()) - or - // value of the left side of the expression `someExpr` equal `sizeof(buf)` value, and `buf` is array. - leftsomeExpr.getValue().toInt() = this.getArgument(0).getType().getSize() - } - - /** - * Holds if the left side of the expression `someExpr` equal to variable containing the length of the memory allocated for the buffer. - */ - predicate isVariableEqualValueSizegBuffer() { - // the left side of expression `someExpr` is the variable that was used in the function of allocating memory for the buffer`. - exists(AllocationExpr alc | - leftsomeExpr.(VariableAccess).getTarget() = - alc.(FunctionCall).getArgument(0).(VariableAccess).getTarget() - ) - } -} - -from WrongCallStrncat sc -where - sc.isExpressionEqualSizeof() or - sc.isVariableEqualValueSizegBuffer() -select sc, "if the used buffer is full, writing out of the buffer is possible" diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp index d4edec95db9..fde62870954 100644 --- a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp @@ -33,7 +33,7 @@ the break statement only exits from one level of the loop.

  • Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997). - Chapter 4: Control Flow, Rule 4.6 (PDF). + Chapter 4: Control Flow, Rule 4.6 (PDF).
  • www.cplusplus.com Control Structures diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 201.qhelp b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 201.qhelp index c165fe59ba8..19d01d570cd 100644 --- a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 201.qhelp +++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 201.qhelp @@ -39,7 +39,7 @@ loop if the loop requires more complicated variable iteration.
  • Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997). - Chapter 4: Control Flow, Rule 4.1 (PDF). + Chapter 4: Control Flow, Rule 4.1 (PDF).
  • diff --git a/cpp/ql/src/semmle/code/cpp/Location.qll b/cpp/ql/src/semmle/code/cpp/Location.qll index dc37d4009c0..2a730ea5768 100644 --- a/cpp/ql/src/semmle/code/cpp/Location.qll +++ b/cpp/ql/src/semmle/code/cpp/Location.qll @@ -72,6 +72,7 @@ class Location extends @location { } /** Holds if `this` comes on a line strictly before `l`. */ + pragma[inline] predicate isBefore(Location l) { this.getFile() = l.getFile() and this.getEndLine() < l.getStartLine() } @@ -127,7 +128,9 @@ deprecated library class LocationExpr extends Location, @location_expr { } * Gets the length of the longest line in file `f`. */ pragma[nomagic] -private int maxCols(File f) { result = max(Location l | l.getFile() = f | l.getEndColumn()) } +private int maxCols(File f) { + result = max(Location l | l.getFile() = f | l.getStartColumn().maximum(l.getEndColumn())) +} /** * A C/C++ element that has a location in a file diff --git a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll index 9dc1bbc7346..63c1406d8a5 100644 --- a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll @@ -48,6 +48,15 @@ class MemberFunction extends Function { /** Holds if this member is public. */ predicate isPublic() { this.hasSpecifier("public") } + /** Holds if this declaration has the lvalue ref-qualifier */ + predicate isLValueRefQualified() { hasSpecifier("&") } + + /** Holds if this declaration has the rvalue ref-qualifier */ + predicate isRValueRefQualified() { hasSpecifier("&&") } + + /** Holds if this declaration has a ref-qualifier */ + predicate isRefQualified() { isLValueRefQualified() or isRValueRefQualified() } + /** Holds if this function overrides that function. */ predicate overrides(MemberFunction that) { overrides(underlyingElement(this), unresolveElement(that)) diff --git a/cpp/ql/src/semmle/code/cpp/Specifier.qll b/cpp/ql/src/semmle/code/cpp/Specifier.qll index 1c1eb0c090a..4a425b690f4 100644 --- a/cpp/ql/src/semmle/code/cpp/Specifier.qll +++ b/cpp/ql/src/semmle/code/cpp/Specifier.qll @@ -37,7 +37,7 @@ class FunctionSpecifier extends Specifier { this.hasName("explicit") } - override string getAPrimaryQlClass() { result = "FunctionSpecifier)" } + override string getAPrimaryQlClass() { result = "FunctionSpecifier" } } /** diff --git a/cpp/ql/src/semmle/code/cpp/Type.qll b/cpp/ql/src/semmle/code/cpp/Type.qll index a0d8c42df32..fd9ae4c8737 100644 --- a/cpp/ql/src/semmle/code/cpp/Type.qll +++ b/cpp/ql/src/semmle/code/cpp/Type.qll @@ -101,6 +101,7 @@ class Type extends Locatable, @type { * * For example, starting with `const i64* const` in the context of `typedef long long i64;`, this predicate will return `long long*`. */ + pragma[nomagic] Type getUnspecifiedType() { unspecifiedtype(underlyingElement(this), unresolveElement(result)) } /** diff --git a/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll b/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll index 253a2767077..5d6c64630a6 100644 --- a/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll +++ b/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll @@ -63,14 +63,278 @@ class Ptrdiff_t extends Type { override string getAPrimaryQlClass() { result = "Ptrdiff_t" } } +/** + * A parent class representing C/C++ a typedef'd `UserType` such as `int8_t`. + */ +abstract private class IntegralUnderlyingUserType extends UserType { + IntegralUnderlyingUserType() { this.getUnderlyingType() instanceof IntegralType } +} + +abstract private class TFixedWidthIntegralType extends IntegralUnderlyingUserType { } + +/** + * A C/C++ fixed-width numeric type, such as `int8_t`. + */ +class FixedWidthIntegralType extends TFixedWidthIntegralType { + FixedWidthIntegralType() { this instanceof TFixedWidthIntegralType } +} + +abstract private class TMinimumWidthIntegralType extends IntegralUnderlyingUserType { } + +/** + * A C/C++ minimum-width numeric type, such as `int_least8_t`. + */ +class MinimumWidthIntegralType extends TMinimumWidthIntegralType { + MinimumWidthIntegralType() { this instanceof TMinimumWidthIntegralType } +} + +abstract private class TFastestMinimumWidthIntegralType extends IntegralUnderlyingUserType { } + +/** + * A C/C++ minimum-width numeric type, representing the fastest integer type with a + * width of at least `N` such as `int_fast8_t`. + */ +class FastestMinimumWidthIntegralType extends TFastestMinimumWidthIntegralType { + FastestMinimumWidthIntegralType() { this instanceof TFastestMinimumWidthIntegralType } +} + +abstract private class TMaximumWidthIntegralType extends IntegralUnderlyingUserType { } + +/** + * A C/C++ maximum-width numeric type, either `intmax_t` or `uintmax_t`. + */ +class MaximumWidthIntegralType extends TMaximumWidthIntegralType { + MaximumWidthIntegralType() { this instanceof TMaximumWidthIntegralType } +} + +/** + * An enum type based on a fixed-width integer type. For instance, `enum e: uint8_t = { a, b };` + */ +class FixedWidthEnumType extends UserType { + FixedWidthEnumType() { this.(Enum).getExplicitUnderlyingType() instanceof FixedWidthIntegralType } +} + +/** + * The C/C++ `int8_t` type. + */ +class Int8_t extends TFixedWidthIntegralType { + Int8_t() { this.hasGlobalOrStdName("int8_t") } + + override string getAPrimaryQlClass() { result = "Int8_t" } +} + +/** + * The C/C++ `int16_t` type. + */ +class Int16_t extends TFixedWidthIntegralType { + Int16_t() { this.hasGlobalOrStdName("int16_t") } + + override string getAPrimaryQlClass() { result = "Int16_t" } +} + +/** + * The C/C++ `int32_t` type. + */ +class Int32_t extends TFixedWidthIntegralType { + Int32_t() { this.hasGlobalOrStdName("int32_t") } + + override string getAPrimaryQlClass() { result = "Int32_t" } +} + +/** + * The C/C++ `int64_t` type. + */ +class Int64_t extends TFixedWidthIntegralType { + Int64_t() { this.hasGlobalOrStdName("int64_t") } + + override string getAPrimaryQlClass() { result = "Int64_t" } +} + +/** + * The C/C++ `uint8_t` type. + */ +class UInt8_t extends TFixedWidthIntegralType { + UInt8_t() { this.hasGlobalOrStdName("uint8_t") } + + override string getAPrimaryQlClass() { result = "UInt8_t" } +} + +/** + * The C/C++ `uint16_t` type. + */ +class UInt16_t extends TFixedWidthIntegralType { + UInt16_t() { this.hasGlobalOrStdName("uint16_t") } + + override string getAPrimaryQlClass() { result = "UInt16_t" } +} + +/** + * The C/C++ `uint32_t` type. + */ +class UInt32_t extends TFixedWidthIntegralType { + UInt32_t() { this.hasGlobalOrStdName("uint32_t") } + + override string getAPrimaryQlClass() { result = "UInt32_t" } +} + +/** + * The C/C++ `uint64_t` type. + */ +class UInt64_t extends TFixedWidthIntegralType { + UInt64_t() { this.hasGlobalOrStdName("uint64_t") } + + override string getAPrimaryQlClass() { result = "UInt64_t" } +} + +/** + * The C/C++ `int_least8_t` type. + */ +class Int_least8_t extends TMinimumWidthIntegralType { + Int_least8_t() { this.hasGlobalOrStdName("int_least8_t") } + + override string getAPrimaryQlClass() { result = "Int_least8_t" } +} + +/** + * The C/C++ `int_least16_t` type. + */ +class Int_least16_t extends TMinimumWidthIntegralType { + Int_least16_t() { this.hasGlobalOrStdName("int_least16_t") } + + override string getAPrimaryQlClass() { result = "Int_least16_t" } +} + +/** + * The C/C++ `int_least32_t` type. + */ +class Int_least32_t extends TMinimumWidthIntegralType { + Int_least32_t() { this.hasGlobalOrStdName("int_least32_t") } + + override string getAPrimaryQlClass() { result = "Int_least32_t" } +} + +/** + * The C/C++ `int_least64_t` type. + */ +class Int_least64_t extends TMinimumWidthIntegralType { + Int_least64_t() { this.hasGlobalOrStdName("int_least64_t") } + + override string getAPrimaryQlClass() { result = "Int_least64_t" } +} + +/** + * The C/C++ `uint_least8_t` type. + */ +class UInt_least8_t extends TMinimumWidthIntegralType { + UInt_least8_t() { this.hasGlobalOrStdName("uint_least8_t") } + + override string getAPrimaryQlClass() { result = "UInt_least8_t" } +} + +/** + * The C/C++ `uint_least16_t` type. + */ +class UInt_least16_t extends TMinimumWidthIntegralType { + UInt_least16_t() { this.hasGlobalOrStdName("uint_least16_t") } + + override string getAPrimaryQlClass() { result = "UInt_least16_t" } +} + +/** + * The C/C++ `uint_least32_t` type. + */ +class UInt_least32_t extends TMinimumWidthIntegralType { + UInt_least32_t() { this.hasGlobalOrStdName("uint_least32_t") } + + override string getAPrimaryQlClass() { result = "UInt_least32_t" } +} + +/** + * The C/C++ `uint_least64_t` type. + */ +class UInt_least64_t extends TMinimumWidthIntegralType { + UInt_least64_t() { this.hasGlobalOrStdName("uint_least64_t") } + + override string getAPrimaryQlClass() { result = "UInt_least64_t" } +} + +/** + * The C/C++ `int_fast8_t` type. + */ +class Int_fast8_t extends TFastestMinimumWidthIntegralType { + Int_fast8_t() { this.hasGlobalOrStdName("int_fast8_t") } + + override string getAPrimaryQlClass() { result = "Int_fast8_t" } +} + +/** + * The C/C++ `int_fast16_t` type. + */ +class Int_fast16_t extends TFastestMinimumWidthIntegralType { + Int_fast16_t() { this.hasGlobalOrStdName("int_fast16_t") } + + override string getAPrimaryQlClass() { result = "Int_fast16_t" } +} + +/** + * The C/C++ `int_fast32_t` type. + */ +class Int_fast32_t extends TFastestMinimumWidthIntegralType { + Int_fast32_t() { this.hasGlobalOrStdName("int_fast32_t") } + + override string getAPrimaryQlClass() { result = "Int_fast32_t" } +} + +/** + * The C/C++ `int_fast64_t` type. + */ +class Int_fast64_t extends TFastestMinimumWidthIntegralType { + Int_fast64_t() { this.hasGlobalOrStdName("int_fast64_t") } + + override string getAPrimaryQlClass() { result = "Int_fast64_t" } +} + +/** + * The C/C++ `uint_fast8_t` type. + */ +class UInt_fast8_t extends TFastestMinimumWidthIntegralType { + UInt_fast8_t() { this.hasGlobalOrStdName("uint_fast8_t") } + + override string getAPrimaryQlClass() { result = "UInt_fast8_t" } +} + +/** + * The C/C++ `uint_fast16_t` type. + */ +class UInt_fast16_t extends TFastestMinimumWidthIntegralType { + UInt_fast16_t() { this.hasGlobalOrStdName("uint_fast16_t") } + + override string getAPrimaryQlClass() { result = "UInt_fast16_t" } +} + +/** + * The C/C++ `uint_fast32_t` type. + */ +class UInt_fast32_t extends TFastestMinimumWidthIntegralType { + UInt_fast32_t() { this.hasGlobalOrStdName("uint_fast32_t") } + + override string getAPrimaryQlClass() { result = "UInt_fast32_t" } +} + +/** + * The C/C++ `uint_fast64_t` type. + */ +class UInt_fast64_t extends TFastestMinimumWidthIntegralType { + UInt_fast64_t() { this.hasGlobalOrStdName("uint_fast64_t") } + + override string getAPrimaryQlClass() { result = "UInt_fast64_t" } +} + /** * The C/C++ `intmax_t` type. */ -class Intmax_t extends Type { - Intmax_t() { - this.getUnderlyingType() instanceof IntegralType and - this.hasName("intmax_t") - } +class Intmax_t extends TMaximumWidthIntegralType { + Intmax_t() { this.hasGlobalOrStdName("intmax_t") } override string getAPrimaryQlClass() { result = "Intmax_t" } } @@ -78,11 +342,8 @@ class Intmax_t extends Type { /** * The C/C++ `uintmax_t` type. */ -class Uintmax_t extends Type { - Uintmax_t() { - this.getUnderlyingType() instanceof IntegralType and - this.hasName("uintmax_t") - } +class Uintmax_t extends TMaximumWidthIntegralType { + Uintmax_t() { this.hasGlobalOrStdName("uintmax_t") } override string getAPrimaryQlClass() { result = "Uintmax_t" } } diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/Dominance.qll b/cpp/ql/src/semmle/code/cpp/controlflow/Dominance.qll index 3abe68e0442..424e615f3ea 100644 --- a/cpp/ql/src/semmle/code/cpp/controlflow/Dominance.qll +++ b/cpp/ql/src/semmle/code/cpp/controlflow/Dominance.qll @@ -14,6 +14,7 @@ import cpp * In rare cases, the same node is used in multiple control-flow scopes. This * confuses the dominance analysis, so this predicate is used to exclude them. */ +pragma[noinline] private predicate hasMultiScopeNode(Function f) { exists(ControlFlowNode node | node.getControlFlowScope() = f and diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll b/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll index 656496325af..d96fc34259c 100644 --- a/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll +++ b/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll @@ -125,17 +125,17 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition { ) } - override predicate comparesEq(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) { + override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) { exists(boolean partIsTrue, GuardCondition part | this.(BinaryLogicalOperation).impliesValue(part, partIsTrue, testIsTrue) | - part.comparesEq(left, right, k, isLessThan, partIsTrue) + part.comparesEq(left, right, k, areEqual, partIsTrue) ) } - override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) { + override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) { exists(boolean testIsTrue | - comparesEq(left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue) + comparesEq(left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue) ) } } @@ -154,20 +154,20 @@ private class GuardConditionFromShortCircuitNot extends GuardCondition, NotExpr getOperand().(GuardCondition).controls(controlled, testIsTrue.booleanNot()) } - override predicate comparesLt(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) { - getOperand().(GuardCondition).comparesLt(left, right, k, areEqual, testIsTrue.booleanNot()) + override predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) { + getOperand().(GuardCondition).comparesLt(left, right, k, isLessThan, testIsTrue.booleanNot()) } - override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean testIsTrue) { - getOperand().(GuardCondition).ensuresLt(left, right, k, block, testIsTrue.booleanNot()) + override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) { + getOperand().(GuardCondition).ensuresLt(left, right, k, block, isLessThan.booleanNot()) } override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) { getOperand().(GuardCondition).comparesEq(left, right, k, areEqual, testIsTrue.booleanNot()) } - override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean testIsTrue) { - getOperand().(GuardCondition).ensuresEq(left, right, k, block, testIsTrue.booleanNot()) + override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) { + getOperand().(GuardCondition).ensuresEq(left, right, k, block, areEqual.booleanNot()) } } diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll b/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll index b47618de2e9..31ef5570451 100644 --- a/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll +++ b/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll @@ -1307,7 +1307,8 @@ private predicate conditionJumps(Expr test, boolean truth, Node n2, Pos p2) { ) } -// Factored out for performance. See QL-796. +// Pulled out for performance. See +// https://github.com/github/codeql-coreql-team/issues/1044. private predicate normalGroupMemberBaseCase(Node memberNode, Pos memberPos, Node atNode) { memberNode = atNode and memberPos.isAt() and diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll b/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll index 77e0f05ed02..476f626e874 100644 --- a/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll +++ b/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll @@ -104,9 +104,43 @@ private predicate loopConditionAlwaysUponEntry(ControlFlowNode loop, Expr condit ) } +/** + * This relation is the same as the `el instanceof Function`, only obfuscated + * so the optimizer will not understand that any `FunctionCall.getTarget()` + * should be in this relation. + */ +pragma[noinline] +private predicate isFunction(Element el) { + el instanceof Function + or + el.(Expr).getParent() = el +} + +/** + * Holds if `fc` is a `FunctionCall` with no return value for `getTarget`. This + * can happen in case of rare database inconsistencies. + */ +pragma[noopt] +private predicate callHasNoTarget(@funbindexpr fc) { + exists(Function f | + funbind(fc, f) and + not isFunction(f) + ) +} + +// Pulled out for performance. See +// https://github.com/github/codeql-coreql-team/issues/1044. +private predicate potentiallyReturningFunctionCall_base(FunctionCall fc) { + fc.isVirtual() + or + callHasNoTarget(fc) +} + /** A function call that *may* return; if in doubt, we assume it may. */ private predicate potentiallyReturningFunctionCall(FunctionCall fc) { - potentiallyReturningFunction(fc.getTarget()) or fc.isVirtual() + potentiallyReturningFunctionCall_base(fc) + or + potentiallyReturningFunction(fc.getTarget()) } /** A function that *may* return; if in doubt, we assume it may. */ diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/AddressFlow.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/AddressFlow.qll index 7024f9d8598..f6072763e1a 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/AddressFlow.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/AddressFlow.qll @@ -15,6 +15,7 @@ */ private import cpp +private import semmle.code.cpp.models.interfaces.PointerWrapper /** * Holds if `f` is an instantiation of the `std::move` or `std::forward` @@ -94,6 +95,12 @@ private predicate pointerToPointerStep(Expr pointerIn, Expr pointerOut) { private predicate lvalueToReferenceStep(Expr lvalueIn, Expr referenceOut) { lvalueIn.getConversion() = referenceOut.(ReferenceToExpr) + or + exists(PointerWrapper wrapper, Call call | call = referenceOut | + referenceOut.getUnspecifiedType() instanceof ReferenceType and + call = wrapper.getAnUnwrapperFunction().getACallToThisFunction() and + lvalueIn = call.getQualifier().getFullyConverted() + ) } private predicate referenceToLvalueStep(Expr referenceIn, Expr lvalueOut) { @@ -106,6 +113,13 @@ private predicate referenceToPointerStep(Expr referenceIn, Expr pointerOut) { stdAddressOf(call.getTarget()) and referenceIn = call.getArgument(0).getFullyConverted() ) + or + exists(CopyConstructor copy, Call call | call = pointerOut | + copy.getDeclaringType() instanceof PointerWrapper and + call.getTarget() = copy and + // The 0'th argument is the value being copied. + referenceIn = call.getArgument(0).getFullyConverted() + ) } private predicate referenceToReferenceStep(Expr referenceIn, Expr referenceOut) { @@ -190,6 +204,19 @@ private predicate pointerToUpdate(Expr pointer, Expr outer, ControlFlowNode node // See the `lvalueToUpdate` case for an explanation of this conjunct. call.getType().isDeeplyConstBelow() ) + or + // Pointer wrappers behave as raw pointers for dataflow purposes. + outer = call.getAnArgument().getFullyConverted() and + exists(PointerWrapper wrapper | wrapper = outer.getType().stripTopLevelSpecifiers() | + not wrapper.pointsToConst() + ) + or + outer = call.getQualifier().getFullyConverted() and + outer.getUnspecifiedType() instanceof PointerWrapper and + not ( + call.getTarget().hasSpecifier("const") and + call.getType().isDeeplyConstBelow() + ) ) or exists(PointerFieldAccess fa | @@ -218,7 +245,9 @@ private predicate referenceToUpdate(Expr reference, Expr outer, ControlFlowNode not stdIdentityFunction(call.getTarget()) and not stdAddressOf(call.getTarget()) and exists(ReferenceType rt | rt = outer.getType().stripTopLevelSpecifiers() | - not rt.getBaseType().isConst() + not rt.getBaseType().isConst() or + rt.getBaseType().getUnspecifiedType() = + any(PointerWrapper wrapper | not wrapper.pointsToConst()) ) ) and reference = outer 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 8b446d28b86..9b14db7ef88 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,29 +512,29 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } /** - * Holds if an output from `call` is reached in the flow covered by `revFlow`. + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. */ pragma[nomagic] private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { @@ -597,7 +594,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -610,6 +607,15 @@ private module Stage1 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(ArgNode arg, boolean toReturn | + revFlow(arg, toReturn, config) and + revFlowInToReturn(call, arg, config) and + revFlowIsReturned(call, toReturn, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, config)) and @@ -663,7 +669,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +681,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +741,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -833,6 +838,16 @@ private module Stage2 { PrevStage::revFlow(node, _, _, apa, config) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -899,13 +914,11 @@ private module Stage2 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -944,10 +957,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -956,17 +969,14 @@ private module Stage2 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -981,7 +991,13 @@ private module Stage2 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -992,7 +1008,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1012,6 +1028,27 @@ private module Stage2 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1077,14 +1114,12 @@ private module Stage2 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1132,13 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1146,9 +1178,14 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1199,13 +1236,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1218,6 +1255,15 @@ private module Stage2 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -1245,8 +1291,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1260,8 +1305,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1320,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1366,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1395,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1429,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1488,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1465,6 +1512,16 @@ private module Stage3 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -1538,13 +1595,11 @@ private module Stage3 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -1583,10 +1638,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1595,17 +1650,14 @@ private module Stage3 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -1620,7 +1672,13 @@ private module Stage3 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1631,7 +1689,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1651,6 +1709,27 @@ private module Stage3 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1716,14 +1795,12 @@ private module Stage3 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1771,13 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1785,9 +1859,14 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1838,13 +1917,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1857,6 +1936,15 @@ private module Stage3 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2088,7 +2176,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2127,17 +2215,12 @@ private module Stage4 { bindingset[innercc, inner, call] private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) { resolveReturn(innercc, inner, call) - or - innercc.(CallContextCall).matchesCall(call) } bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -2158,8 +2241,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2185,6 +2267,16 @@ private module Stage4 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -2258,13 +2350,11 @@ private module Stage4 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -2303,10 +2393,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2315,17 +2405,14 @@ private module Stage4 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -2340,7 +2427,13 @@ private module Stage4 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2351,7 +2444,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2371,6 +2464,27 @@ private module Stage4 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -2436,14 +2550,12 @@ private module Stage4 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -2491,13 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2505,9 +2614,14 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2558,13 +2672,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2577,6 +2691,15 @@ private module Stage4 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2609,7 +2732,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2630,7 +2753,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2761,7 +2884,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2982,7 +3105,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3132,7 +3255,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, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and @@ -3151,7 +3274,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3238,7 +3361,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3251,7 +3374,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3275,7 +3398,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3571,7 +3694,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3594,7 +3717,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3614,7 +3737,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3628,9 +3751,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3786,7 +3909,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3800,7 +3923,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3816,7 +3939,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3830,7 +3953,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3927,7 +4050,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3946,7 +4069,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3983,7 +4106,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4040,7 +4163,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4118,7 +4241,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4141,7 +4264,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 8b446d28b86..9b14db7ef88 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,29 +512,29 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } /** - * Holds if an output from `call` is reached in the flow covered by `revFlow`. + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. */ pragma[nomagic] private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { @@ -597,7 +594,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -610,6 +607,15 @@ private module Stage1 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(ArgNode arg, boolean toReturn | + revFlow(arg, toReturn, config) and + revFlowInToReturn(call, arg, config) and + revFlowIsReturned(call, toReturn, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, config)) and @@ -663,7 +669,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +681,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +741,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -833,6 +838,16 @@ private module Stage2 { PrevStage::revFlow(node, _, _, apa, config) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -899,13 +914,11 @@ private module Stage2 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -944,10 +957,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -956,17 +969,14 @@ private module Stage2 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -981,7 +991,13 @@ private module Stage2 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -992,7 +1008,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1012,6 +1028,27 @@ private module Stage2 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1077,14 +1114,12 @@ private module Stage2 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1132,13 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1146,9 +1178,14 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1199,13 +1236,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1218,6 +1255,15 @@ private module Stage2 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -1245,8 +1291,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1260,8 +1305,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1320,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1366,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1395,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1429,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1488,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1465,6 +1512,16 @@ private module Stage3 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -1538,13 +1595,11 @@ private module Stage3 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -1583,10 +1638,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1595,17 +1650,14 @@ private module Stage3 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -1620,7 +1672,13 @@ private module Stage3 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1631,7 +1689,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1651,6 +1709,27 @@ private module Stage3 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1716,14 +1795,12 @@ private module Stage3 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1771,13 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1785,9 +1859,14 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1838,13 +1917,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1857,6 +1936,15 @@ private module Stage3 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2088,7 +2176,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2127,17 +2215,12 @@ private module Stage4 { bindingset[innercc, inner, call] private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) { resolveReturn(innercc, inner, call) - or - innercc.(CallContextCall).matchesCall(call) } bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -2158,8 +2241,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2185,6 +2267,16 @@ private module Stage4 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -2258,13 +2350,11 @@ private module Stage4 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -2303,10 +2393,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2315,17 +2405,14 @@ private module Stage4 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -2340,7 +2427,13 @@ private module Stage4 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2351,7 +2444,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2371,6 +2464,27 @@ private module Stage4 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -2436,14 +2550,12 @@ private module Stage4 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -2491,13 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2505,9 +2614,14 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2558,13 +2672,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2577,6 +2691,15 @@ private module Stage4 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2609,7 +2732,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2630,7 +2753,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2761,7 +2884,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2982,7 +3105,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3132,7 +3255,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, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and @@ -3151,7 +3274,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3238,7 +3361,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3251,7 +3374,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3275,7 +3398,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3571,7 +3694,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3594,7 +3717,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3614,7 +3737,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3628,9 +3751,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3786,7 +3909,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3800,7 +3923,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3816,7 +3939,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3830,7 +3953,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3927,7 +4050,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3946,7 +4069,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3983,7 +4106,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4040,7 +4163,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4118,7 +4241,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4141,7 +4264,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 8b446d28b86..9b14db7ef88 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,29 +512,29 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } /** - * Holds if an output from `call` is reached in the flow covered by `revFlow`. + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. */ pragma[nomagic] private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { @@ -597,7 +594,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -610,6 +607,15 @@ private module Stage1 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(ArgNode arg, boolean toReturn | + revFlow(arg, toReturn, config) and + revFlowInToReturn(call, arg, config) and + revFlowIsReturned(call, toReturn, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, config)) and @@ -663,7 +669,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +681,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +741,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -833,6 +838,16 @@ private module Stage2 { PrevStage::revFlow(node, _, _, apa, config) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -899,13 +914,11 @@ private module Stage2 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -944,10 +957,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -956,17 +969,14 @@ private module Stage2 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -981,7 +991,13 @@ private module Stage2 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -992,7 +1008,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1012,6 +1028,27 @@ private module Stage2 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1077,14 +1114,12 @@ private module Stage2 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1132,13 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1146,9 +1178,14 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1199,13 +1236,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1218,6 +1255,15 @@ private module Stage2 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -1245,8 +1291,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1260,8 +1305,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1320,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1366,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1395,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1429,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1488,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1465,6 +1512,16 @@ private module Stage3 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -1538,13 +1595,11 @@ private module Stage3 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -1583,10 +1638,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1595,17 +1650,14 @@ private module Stage3 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -1620,7 +1672,13 @@ private module Stage3 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1631,7 +1689,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1651,6 +1709,27 @@ private module Stage3 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1716,14 +1795,12 @@ private module Stage3 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1771,13 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1785,9 +1859,14 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1838,13 +1917,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1857,6 +1936,15 @@ private module Stage3 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2088,7 +2176,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2127,17 +2215,12 @@ private module Stage4 { bindingset[innercc, inner, call] private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) { resolveReturn(innercc, inner, call) - or - innercc.(CallContextCall).matchesCall(call) } bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -2158,8 +2241,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2185,6 +2267,16 @@ private module Stage4 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -2258,13 +2350,11 @@ private module Stage4 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -2303,10 +2393,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2315,17 +2405,14 @@ private module Stage4 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -2340,7 +2427,13 @@ private module Stage4 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2351,7 +2444,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2371,6 +2464,27 @@ private module Stage4 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -2436,14 +2550,12 @@ private module Stage4 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -2491,13 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2505,9 +2614,14 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2558,13 +2672,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2577,6 +2691,15 @@ private module Stage4 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2609,7 +2732,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2630,7 +2753,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2761,7 +2884,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2982,7 +3105,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3132,7 +3255,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, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and @@ -3151,7 +3274,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3238,7 +3361,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3251,7 +3374,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3275,7 +3398,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3571,7 +3694,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3594,7 +3717,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3614,7 +3737,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3628,9 +3751,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3786,7 +3909,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3800,7 +3923,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3816,7 +3939,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3830,7 +3953,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3927,7 +4050,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3946,7 +4069,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3983,7 +4106,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4040,7 +4163,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4118,7 +4241,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4141,7 +4264,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 8b446d28b86..9b14db7ef88 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,29 +512,29 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } /** - * Holds if an output from `call` is reached in the flow covered by `revFlow`. + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. */ pragma[nomagic] private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { @@ -597,7 +594,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -610,6 +607,15 @@ private module Stage1 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(ArgNode arg, boolean toReturn | + revFlow(arg, toReturn, config) and + revFlowInToReturn(call, arg, config) and + revFlowIsReturned(call, toReturn, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, config)) and @@ -663,7 +669,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +681,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +741,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -833,6 +838,16 @@ private module Stage2 { PrevStage::revFlow(node, _, _, apa, config) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -899,13 +914,11 @@ private module Stage2 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -944,10 +957,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -956,17 +969,14 @@ private module Stage2 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -981,7 +991,13 @@ private module Stage2 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -992,7 +1008,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1012,6 +1028,27 @@ private module Stage2 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1077,14 +1114,12 @@ private module Stage2 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1132,13 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1146,9 +1178,14 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1199,13 +1236,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1218,6 +1255,15 @@ private module Stage2 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -1245,8 +1291,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1260,8 +1305,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1320,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1366,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1395,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1429,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1488,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1465,6 +1512,16 @@ private module Stage3 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -1538,13 +1595,11 @@ private module Stage3 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -1583,10 +1638,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1595,17 +1650,14 @@ private module Stage3 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -1620,7 +1672,13 @@ private module Stage3 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1631,7 +1689,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1651,6 +1709,27 @@ private module Stage3 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1716,14 +1795,12 @@ private module Stage3 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1771,13 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1785,9 +1859,14 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1838,13 +1917,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1857,6 +1936,15 @@ private module Stage3 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2088,7 +2176,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2127,17 +2215,12 @@ private module Stage4 { bindingset[innercc, inner, call] private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) { resolveReturn(innercc, inner, call) - or - innercc.(CallContextCall).matchesCall(call) } bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -2158,8 +2241,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2185,6 +2267,16 @@ private module Stage4 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -2258,13 +2350,11 @@ private module Stage4 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -2303,10 +2393,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2315,17 +2405,14 @@ private module Stage4 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -2340,7 +2427,13 @@ private module Stage4 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2351,7 +2444,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2371,6 +2464,27 @@ private module Stage4 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -2436,14 +2550,12 @@ private module Stage4 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -2491,13 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2505,9 +2614,14 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2558,13 +2672,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2577,6 +2691,15 @@ private module Stage4 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2609,7 +2732,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2630,7 +2753,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2761,7 +2884,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2982,7 +3105,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3132,7 +3255,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, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and @@ -3151,7 +3274,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3238,7 +3361,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3251,7 +3374,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3275,7 +3398,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3571,7 +3694,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3594,7 +3717,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3614,7 +3737,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3628,9 +3751,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3786,7 +3909,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3800,7 +3923,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3816,7 +3939,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3830,7 +3953,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3927,7 +4050,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3946,7 +4069,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3983,7 +4106,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4040,7 +4163,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4118,7 +4241,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4141,7 +4264,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 a51c20c2288..462e89ac9ed 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -31,26 +31,26 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * currently excludes read-steps, store-steps, and flow-through. * * The analysis uses non-linear recursion: When computing a flow path in or out - * of a call, we use the results of the analysis recursively to resolve lamba + * of a call, we use the results of the analysis recursively to resolve lambda * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -118,8 +118,8 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeType(node)) + if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode + then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -129,7 +129,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { lambdaCall(lambdaCall, kind, node) and - t = getNodeType(node) and + t = getNodeDataFlowType(node) and toReturn = false and toJump = false and lastCall = TDataFlowCallNone() @@ -146,7 +146,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -160,7 +160,7 @@ private module LambdaFlow { toJump = true and lastCall = TDataFlowCallNone() | - jumpStep(node, mid) and + jumpStepCached(node, mid) and t = t0 or exists(boolean preservesValue | @@ -168,7 +168,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -176,7 +176,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -227,7 +227,7 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump, + DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) @@ -242,6 +242,89 @@ private DataFlowCallable viableCallableExt(DataFlowCall call) { cached private module Cached { + /** + * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to + * force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby + * collapsing the two stages. + */ + cached + predicate forceCachingInSameStage() { any() } + + cached + predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() } + + cached + predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { + c = call.getEnclosingCallable() + } + + cached + predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } + + cached + predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } + + cached + predicate clearsContentCached(Node n, Content c) { clearsContent(n, c) } + + cached + predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } + + cached + predicate outNodeExt(Node n) { + n instanceof OutNode + or + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode + } + + cached + predicate hiddenNode(Node n) { nodeIsHidden(n) } + + cached + OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { + result = getAnOutNode(call, k.(ValueReturnKind).getKind()) + or + exists(ArgNode arg | + result.(PostUpdateNode).getPreUpdateNode() = arg and + arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) + ) + } + + cached + predicate returnNodeExt(Node n, ReturnKindExt k) { + k = TValueReturn(n.(ReturnNode).getKind()) + or + exists(ParamNode p, int pos | + parameterValueFlowsToPreUpdate(p, n) and + p.isParameterOf(_, pos) and + k = TParamUpdate(pos) + ) + } + + cached + predicate castNode(Node n) { n instanceof CastNode } + + cached + predicate castingNode(Node n) { + castNode(n) or + n instanceof ParamNode or + n instanceof OutNodeExt or + // For reads, `x.f`, we want to check that the tracked type after the read (which + // is obtained by popping the head of the access path stack) is compatible with + // the type of `x.f`. + read(_, _, n) + } + + cached + predicate parameterNode(Node n, DataFlowCallable c, int i) { + n.(ParameterNode).isParameterOf(c, i) + } + + cached + predicate argumentNode(Node n, DataFlowCall call, int pos) { + n.(ArgumentNode).argumentOf(call, pos) + } + /** * Gets a viable target for the lambda call `call`. * @@ -261,7 +344,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParam(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableExt(call), i) } @@ -270,11 +353,11 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and - compatibleTypes(getNodeType(arg), getNodeType(p)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) ) } @@ -312,7 +395,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { p = node and read = false or @@ -325,30 +408,30 @@ private module Cached { // read exists(Node mid | parameterValueFlowCand(p, mid, false) and - readStep(mid, _, node) and + read(mid, _, node) and read = true ) or // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) { + private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -360,7 +443,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -369,9 +452,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read + DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -382,14 +465,14 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNode arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) ) } - predicate cand(ParameterNode p, Node n) { + predicate cand(ParamNode p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -416,21 +499,21 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(p), getNodeType(node)) + compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) or // getter - compatibleTypes(read.getContentType(), getNodeType(node)) + compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -447,7 +530,7 @@ private module Cached { readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeType(p), read.getContainerType()) + compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) ) or parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) @@ -455,34 +538,32 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) } pragma[nomagic] - private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ReadStepTypesOption read - ) { + private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -496,18 +577,18 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) | // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(arg), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) or // getter - compatibleTypes(getNodeType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) ) } @@ -516,7 +597,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNode arg, Content c, Node out) { + predicate getterStep(ArgNode arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -529,7 +610,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ReadStepTypesOption read + ParamNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -553,7 +634,7 @@ private module Cached { private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { mayBenefitFromCallContext(call, callable) or - callable = call.getEnclosingCallable() and + callEnclosingCallable(call, callable) and exists(viableCallableLambda(call, TDataFlowCallSome(_))) } @@ -611,7 +692,7 @@ private module Cached { mayBenefitFromCallContextExt(call, _) and c = viableCallableExt(call) and ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and + tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and ctxtgts < tgts ) } @@ -635,8 +716,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - cached - predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -644,9 +724,9 @@ private module Cached { Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType ) { storeStep(node1, c, node2) and - readStep(_, c, _) and - contentType = getNodeType(node1) and - containerType = getNodeType(node2) + read(_, c, _) and + contentType = getNodeDataFlowType(node1) and + containerType = getNodeDataFlowType(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and @@ -654,12 +734,15 @@ private module Cached { | argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or - readStep(n2, c, n1) and - contentType = getNodeType(n1) and - containerType = getNodeType(n2) + read(n2, c, n1) and + contentType = getNodeDataFlowType(n1) and + containerType = getNodeDataFlowType(n2) ) } + cached + predicate read(Node node1, Content c, Node node2) { readStep(node1, c, node2) } + /** * Holds if data can flow from `node1` to `node2` via a direct assignment to * `f`. @@ -678,8 +761,9 @@ private module Cached { * are aliases. A typical example is a function returning `this`, implementing a fluent * interface. */ - cached - predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) { + private predicate reverseStepThroughInputOutputAlias( + PostUpdateNode fromNode, PostUpdateNode toNode + ) { exists(Node fromPre, Node toPre | fromPre = fromNode.getPreUpdateNode() and toPre = toNode.getPreUpdateNode() @@ -688,14 +772,20 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNode), fromPre) + toPre.(ArgNode).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgNode), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) ) } + cached + predicate simpleLocalFlowStepExt(Node node1, Node node2) { + simpleLocalFlowStep(node1, node2) or + reverseStepThroughInputOutputAlias(node1, node2) + } + /** * Holds if the call context `call` either improves virtual dispatch in * `callable` or if it allows us to prune unreachable nodes in `callable`. @@ -704,7 +794,7 @@ private module Cached { predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { reducedViableImplInCallContext(_, callable, call) or - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) } cached @@ -726,12 +816,12 @@ private module Cached { cached newtype TLocalFlowCallContext = TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) } + TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -761,23 +851,15 @@ private module Cached { * A `Node` at which a cast can occur such that the type should be checked. */ class CastingNode extends Node { - CastingNode() { - this instanceof ParameterNode or - this instanceof CastNode or - this instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - readStep(_, _, this) - } + CastingNode() { castingNode(this) } } private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { - readStep(n1, c, n2) and - container = getNodeType(n1) and - content = getNodeType(n2) + read(n1, c, n2) and + container = getNodeDataFlowType(n1) and + content = getNodeDataFlowType(n2) } private newtype TReadStepTypesOption = @@ -854,7 +936,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNode p | getNodeEnclosingCallable(p) = callable) + exists(ParamNode p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -866,7 +948,7 @@ class CallContextReturn extends CallContextNoCall, TReturn { } override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable) + exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) } } @@ -899,7 +981,7 @@ class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall } private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) } /** @@ -913,26 +995,37 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) else result instanceof LocalCallContextAny } +/** + * The value of a parameter at function entry, viewed as a node in a data + * flow graph. + */ +class ParamNode extends Node { + ParamNode() { parameterNode(this, _, _) } + + /** + * Holds if this node is the parameter of callable `c` at the specified + * (zero-based) position. + */ + predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) } +} + +/** A data-flow node that represents a call argument. */ +class ArgNode extends Node { + ArgNode() { argumentNode(this, _, _) } + + /** Holds if this argument occurs at the given position in the given call. */ + final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } +} + /** * A node from which flow can return to the caller. This is either a regular * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. */ class ReturnNodeExt extends Node { - ReturnNodeExt() { - this instanceof ReturnNode or - parameterValueFlowsToPreUpdate(_, this) - } + ReturnNodeExt() { returnNodeExt(this, _) } /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { - result = TValueReturn(this.(ReturnNode).getKind()) - or - exists(ParameterNode p, int pos | - parameterValueFlowsToPreUpdate(p, this) and - p.isParameterOf(_, pos) and - result = TParamUpdate(pos) - ) - } + ReturnKindExt getKind() { returnNodeExt(this, result) } } /** @@ -940,11 +1033,7 @@ class ReturnNodeExt extends Node { * or a post-update node associated with a call argument. */ class OutNodeExt extends Node { - OutNodeExt() { - this instanceof OutNode - or - this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode - } + OutNodeExt() { outNodeExt(this) } } /** @@ -957,7 +1046,7 @@ abstract class ReturnKindExt extends TReturnKindExt { abstract string toString(); /** Gets a node corresponding to data flow out of `call`. */ - abstract OutNodeExt getAnOutNode(DataFlowCall call); + final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } } class ValueReturnKind extends ReturnKindExt, TValueReturn { @@ -968,10 +1057,6 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn { ReturnKind getKind() { result = kind } override string toString() { result = kind.toString() } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - result = getAnOutNode(call, this.getKind()) - } } class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { @@ -982,13 +1067,6 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { int getPosition() { result = pos } override string toString() { result = "param update " + pos } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - exists(ArgumentNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, this.getPosition()) - ) - } } /** A callable tagged with a relevant return kind. */ @@ -1015,10 +1093,13 @@ class ReturnPosition extends TReturnPosition0 { */ pragma[inline] DataFlowCallable getNodeEnclosingCallable(Node n) { - exists(Node n0 | - pragma[only_bind_into](n0) = n and - pragma[only_bind_into](result) = n0.getEnclosingCallable() - ) + nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) +} + +/** Gets the type of `n` used for type pruning. */ +pragma[inline] +DataFlowType getNodeDataFlowType(Node n) { + nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) } pragma[noinline] @@ -1042,7 +1123,7 @@ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall cc instanceof CallContextAny and callable = viableCallableExt(call) or exists(DataFlowCallable c0, DataFlowCall call0 | - call0.getEnclosingCallable() = callable and + callEnclosingCallable(call0, callable) and cc = TReturn(c0, call0) and c0 = prunedViableImplInCallContextReverse(call0, call) ) @@ -1063,8 +1144,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallableExt(call) and cc instanceof CallContextReturn } -predicate read = readStep/3; - /** An optional Boolean value. */ class BooleanOption extends TBooleanOption { string toString() { @@ -1116,7 +1195,7 @@ abstract class AccessPathFront extends TAccessPathFront { TypedContent getHead() { this = TFrontHead(result) } - predicate isClearedAt(Node n) { clearsContent(n, getHead().getContent()) } + predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index 8b446d28b86..9b14db7ef88 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,29 +512,29 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } /** - * Holds if an output from `call` is reached in the flow covered by `revFlow`. + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. */ pragma[nomagic] private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { @@ -597,7 +594,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -610,6 +607,15 @@ private module Stage1 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(ArgNode arg, boolean toReturn | + revFlow(arg, toReturn, config) and + revFlowInToReturn(call, arg, config) and + revFlowIsReturned(call, toReturn, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, config)) and @@ -663,7 +669,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +681,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +741,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -833,6 +838,16 @@ private module Stage2 { PrevStage::revFlow(node, _, _, apa, config) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -899,13 +914,11 @@ private module Stage2 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -944,10 +957,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -956,17 +969,14 @@ private module Stage2 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -981,7 +991,13 @@ private module Stage2 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -992,7 +1008,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1012,6 +1028,27 @@ private module Stage2 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1077,14 +1114,12 @@ private module Stage2 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1132,13 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1146,9 +1178,14 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1199,13 +1236,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1218,6 +1255,15 @@ private module Stage2 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -1245,8 +1291,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1260,8 +1305,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1320,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1366,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1395,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1429,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1488,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1465,6 +1512,16 @@ private module Stage3 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -1538,13 +1595,11 @@ private module Stage3 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -1583,10 +1638,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1595,17 +1650,14 @@ private module Stage3 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -1620,7 +1672,13 @@ private module Stage3 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1631,7 +1689,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1651,6 +1709,27 @@ private module Stage3 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1716,14 +1795,12 @@ private module Stage3 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1771,13 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1785,9 +1859,14 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1838,13 +1917,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1857,6 +1936,15 @@ private module Stage3 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2088,7 +2176,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2127,17 +2215,12 @@ private module Stage4 { bindingset[innercc, inner, call] private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) { resolveReturn(innercc, inner, call) - or - innercc.(CallContextCall).matchesCall(call) } bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -2158,8 +2241,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2185,6 +2267,16 @@ private module Stage4 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -2258,13 +2350,11 @@ private module Stage4 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -2303,10 +2393,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2315,17 +2405,14 @@ private module Stage4 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -2340,7 +2427,13 @@ private module Stage4 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2351,7 +2444,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2371,6 +2464,27 @@ private module Stage4 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -2436,14 +2550,12 @@ private module Stage4 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -2491,13 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2505,9 +2614,14 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2558,13 +2672,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2577,6 +2691,15 @@ private module Stage4 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2609,7 +2732,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2630,7 +2753,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2761,7 +2884,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2982,7 +3105,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3132,7 +3255,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, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and @@ -3151,7 +3274,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3238,7 +3361,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3251,7 +3374,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3275,7 +3398,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3571,7 +3694,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3594,7 +3717,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3614,7 +3737,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3628,9 +3751,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3786,7 +3909,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3800,7 +3923,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3816,7 +3939,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3830,7 +3953,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3927,7 +4050,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3946,7 +4069,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3983,7 +4106,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4040,7 +4163,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4118,7 +4241,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4141,7 +4264,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 835e6c46451..cb0ae4490f8 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll @@ -321,5 +321,5 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) /** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() } -/** Extra data-flow steps needed for lamba flow analysis. */ +/** Extra data-flow steps needed for lambda flow analysis. */ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll index 0a6d459ec79..5db9a84c80a 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll @@ -46,7 +46,7 @@ class Node extends TNode { /** * INTERNAL: Do not use. Alternative name for `getFunction`. */ - final Function getEnclosingCallable() { result = unique(Function f | f = this.getFunction() | f) } + final Function getEnclosingCallable() { result = this.getFunction() } /** Gets the type of this node. */ Type getType() { none() } // overridden in subclasses @@ -324,7 +324,7 @@ private class VariablePartialDefinitionNode extends PartialDefinitionNode { * A synthetic data flow node used for flow into a collection when an iterator * write occurs in a callee. */ -class IteratorPartialDefinitionNode extends PartialDefinitionNode { +private class IteratorPartialDefinitionNode extends PartialDefinitionNode { override IteratorPartialDefinition pd; override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) } @@ -526,7 +526,6 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) { * This is the local flow predicate that's used as a building block in global * data flow. It may have less flow than the `localFlowStep` predicate. */ -cached predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { // Expr -> Expr exprToExprStep_nocfg(nodeFrom.asExpr(), nodeTo.asExpr()) @@ -694,7 +693,12 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) { fromExpr = call.getQualifier() ) and call.getTarget() = f and - outModel.isReturnValue() + // AST dataflow treats a reference as if it were the referred-to object, while the dataflow + // models treat references as pointers. If the return type of the call is a reference, then + // look for data flow the the referred-to object, rather than the reference itself. + if call.getType().getUnspecifiedType() instanceof ReferenceType + then outModel.isReturnValueDeref() + else outModel.isReturnValue() ) ) } @@ -715,6 +719,7 @@ private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) { } private module FieldFlow { + private import DataFlowImplCommon private import DataFlowImplLocal private import DataFlowPrivate @@ -747,7 +752,7 @@ private module FieldFlow { exists(FieldConfiguration cfg | cfg.hasFlow(node1, node2)) and // This configuration should not be able to cross function boundaries, but // we double-check here just to be sure. - node1.getEnclosingCallable() = node2.getEnclosingCallable() + getNodeEnclosingCallable(node1) = getNodeEnclosingCallable(node2) } } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll index f3aa94a7992..e3f8b6f68fb 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll @@ -7,6 +7,7 @@ private import semmle.code.cpp.controlflow.SSA private import semmle.code.cpp.dataflow.internal.SubBasicBlocks private import semmle.code.cpp.dataflow.internal.AddressFlow private import semmle.code.cpp.models.implementations.Iterator +private import semmle.code.cpp.models.interfaces.PointerWrapper /** * A conceptual variable that is assigned only once, like an SSA variable. This @@ -158,18 +159,14 @@ private module PartialDefinitions { Expr innerDefinedExpr; IteratorPartialDefinition() { - exists(Expr convertedInner | - not this instanceof Conversion and - valueToUpdate(convertedInner, this.getFullyConverted(), node) and - innerDefinedExpr = convertedInner.getUnconverted() and - ( - innerDefinedExpr.(Call).getQualifier() = getAnIteratorAccess(collection) - or - innerDefinedExpr.(Call).getQualifier() = collection.getAnAccess() and - collection instanceof IteratorParameter - ) and - innerDefinedExpr.(Call).getTarget() instanceof IteratorPointerDereferenceMemberOperator - ) + innerDefinedExpr = getInnerDefinedExpr(this, node) and + ( + innerDefinedExpr.(Call).getQualifier() = getAnIteratorAccess(collection) + or + innerDefinedExpr.(Call).getQualifier() = collection.getAnAccess() and + collection instanceof IteratorParameter + ) and + innerDefinedExpr.(Call).getTarget() instanceof IteratorPointerDereferenceMemberOperator or // iterators passed by value without a copy constructor exists(Call call | @@ -207,16 +204,18 @@ private module PartialDefinitions { } } + private Expr getInnerDefinedExpr(Expr e, ControlFlowNode node) { + not e instanceof Conversion and + exists(Expr convertedInner | + valueToUpdate(convertedInner, e.getFullyConverted(), node) and + result = convertedInner.getUnconverted() + ) + } + class VariablePartialDefinition extends PartialDefinition { Expr innerDefinedExpr; - VariablePartialDefinition() { - not this instanceof Conversion and - exists(Expr convertedInner | - valueToUpdate(convertedInner, this.getFullyConverted(), node) and - innerDefinedExpr = convertedInner.getUnconverted() - ) - } + VariablePartialDefinition() { innerDefinedExpr = getInnerDefinedExpr(this, node) } deprecated override predicate partiallyDefines(Variable v) { innerDefinedExpr = v.getAnAccess() @@ -296,7 +295,8 @@ module FlowVar_internal { // treating them as immutable, but for data flow it gives better results in // practice to make the variable synonymous with its contents. not v.getUnspecifiedType() instanceof ReferenceType and - not v instanceof IteratorParameter + not v instanceof IteratorParameter and + not v instanceof PointerWrapperParameter } /** @@ -644,10 +644,19 @@ module FlowVar_internal { predicate parameterIsNonConstReference(Parameter p) { exists(ReferenceType refType | refType = p.getUnderlyingType() and - not refType.getBaseType().isConst() + ( + not refType.getBaseType().isConst() + or + // A field of a parameter of type `const std::shared_ptr& p` can still be changed even though + // the base type of the reference is `const`. + refType.getBaseType().getUnspecifiedType() = + any(PointerWrapper wrapper | not wrapper.pointsToConst()) + ) ) or p instanceof IteratorParameter + or + p instanceof PointerWrapperParameter } /** @@ -836,6 +845,10 @@ module FlowVar_internal { IteratorParameter() { this.getUnspecifiedType() instanceof Iterator } } + class PointerWrapperParameter extends Parameter { + PointerWrapperParameter() { this.getUnspecifiedType() instanceof PointerWrapper } + } + /** * Holds if `v` is initialized to have value `assignedExpr`. */ diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll index 1ef340c4f21..6216045db32 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll @@ -11,6 +11,7 @@ private import semmle.code.cpp.models.interfaces.DataFlow private import semmle.code.cpp.models.interfaces.Taint private import semmle.code.cpp.models.interfaces.Iterator +private import semmle.code.cpp.models.interfaces.PointerWrapper private module DataFlow { import semmle.code.cpp.dataflow.internal.DataFlowUtil @@ -44,6 +45,7 @@ predicate defaultTaintSanitizer(DataFlow::Node node) { none() } * local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent * different objects. */ +cached predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { // Taint can flow through expressions that alter the value but preserve // more than one bit of it _or_ expressions that follow data through @@ -141,7 +143,10 @@ private predicate noFlowFromChildExpr(Expr e) { or e instanceof LogicalOrExpr or - e instanceof Call + // Allow taint from `operator*` on smart pointers. + exists(Call call | e = call | + not call.getTarget() = any(PointerWrapper wrapper).getAnUnwrapperFunction() + ) or e instanceof SizeofOperator or diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll index 349efdcee10..6f6f710ac4b 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll @@ -314,6 +314,7 @@ class OverloadedPointerDereferenceFunction extends Function { * T1 operator*(const T2 &); * T1 a; T2 b; * a = *b; + * ``` */ class OverloadedPointerDereferenceExpr extends FunctionCall { OverloadedPointerDereferenceExpr() { diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll index e02a72fe680..f77518c2f56 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll @@ -850,6 +850,24 @@ class NewOrNewArrayExpr extends Expr, @any_new_expr { this.getAllocatorCall() .getArgument(this.getAllocator().(OperatorNewAllocationFunction).getPlacementArgument()) } + + /** + * For `operator new`, this gets the call or expression that initializes the allocated object, if any. + * + * As examples, for `new int(4)`, this will be `4`, and for `new std::vector(4)`, this will + * be a call to the constructor `std::vector::vector(size_t)` with `4` as an argument. + * + * For `operator new[]`, this gets the call or expression that initializes the first element of the + * array, if any. + * + * This will either be a call to the default constructor for the array's element type (as + * in `new std::string[10]`), or a literal zero for arrays of scalars which are zero-initialized + * due to extra parentheses (as in `new int[10]()`). + * + * At runtime, the constructor will be called once for each element in the array, but the + * constructor call only exists once in the AST. + */ + final Expr getInitializer() { result = this.getChild(1) } } /** @@ -871,14 +889,6 @@ class NewExpr extends NewOrNewArrayExpr, @new_expr { override Type getAllocatedType() { new_allocated_type(underlyingElement(this), unresolveElement(result)) } - - /** - * Gets the call or expression that initializes the allocated object, if any. - * - * As examples, for `new int(4)`, this will be `4`, and for `new std::vector(4)`, this will - * be a call to the constructor `std::vector::vector(size_t)` with `4` as an argument. - */ - Expr getInitializer() { result = this.getChild(1) } } /** @@ -909,18 +919,6 @@ class NewArrayExpr extends NewOrNewArrayExpr, @new_array_expr { result = getType().getUnderlyingType().(PointerType).getBaseType() } - /** - * Gets the call or expression that initializes the first element of the array, if any. - * - * This will either be a call to the default constructor for the array's element type (as - * in `new std::string[10]`), or a literal zero for arrays of scalars which are zero-initialized - * due to extra parentheses (as in `new int[10]()`). - * - * At runtime, the constructor will be called once for each element in the array, but the - * constructor call only exists once in the AST. - */ - Expr getInitializer() { result = this.getChild(1) } - /** * Gets the extent of the non-constant array dimension, if any. * @@ -1271,7 +1269,8 @@ private predicate convparents(Expr child, int idx, Element parent) { ) } -// Pulled out for performance. See QL-796. +// Pulled out for performance. See +// https://github.com/github/codeql-coreql-team/issues/1044. private predicate hasNoConversions(Expr e) { not e.hasConversion() } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll index 3092031cbc7..49d11a7e3cc 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll @@ -165,105 +165,132 @@ private predicate nodeIsBarrierEqualityCandidate( any(IRGuardCondition guard).ensuresEq(access, _, _, node.asInstruction().getBlock(), true) } -private predicate nodeIsBarrier(DataFlow::Node node) { - exists(Variable checkedVar | - readsVariable(node.asInstruction(), checkedVar) and - hasUpperBoundsCheck(checkedVar) - ) - or - exists(Variable checkedVar, Operand access | - /* - * This node is guarded by a condition that forces the accessed variable - * to equal something else. For example: - * ``` - * x = taintsource() - * if (x == 10) { - * taintsink(x); // not considered tainted - * } - * ``` - */ - - nodeIsBarrierEqualityCandidate(node, access, checkedVar) and - readsVariable(access.getDef(), checkedVar) - ) -} - -private predicate nodeIsBarrierIn(DataFlow::Node node) { - // don't use dataflow into taint sources, as this leads to duplicate results. - exists(Expr source | isUserInput(source, _) | - node = DataFlow::exprNode(source) +cached +private module Cached { + cached + predicate nodeIsBarrier(DataFlow::Node node) { + exists(Variable checkedVar | + readsVariable(node.asInstruction(), checkedVar) and + hasUpperBoundsCheck(checkedVar) + ) or - // This case goes together with the similar (but not identical) rule in - // `getNodeForSource`. - node = DataFlow::definitionByReferenceNodeFromArgument(source) - ) - or - // don't use dataflow into binary instructions if both operands are unpredictable - exists(BinaryInstruction iTo | - iTo = node.asInstruction() and - not predictableInstruction(iTo.getLeft()) and - not predictableInstruction(iTo.getRight()) and - // propagate taint from either the pointer or the offset, regardless of predictability - not iTo instanceof PointerArithmeticInstruction - ) - or - // don't use dataflow through calls to pure functions if two or more operands - // are unpredictable - exists(Instruction iFrom1, Instruction iFrom2, CallInstruction iTo | - iTo = node.asInstruction() and - isPureFunction(iTo.getStaticCallTarget().getName()) and - iFrom1 = iTo.getAnArgument() and - iFrom2 = iTo.getAnArgument() and - not predictableInstruction(iFrom1) and - not predictableInstruction(iFrom2) and - iFrom1 != iFrom2 - ) + exists(Variable checkedVar, Operand access | + /* + * This node is guarded by a condition that forces the accessed variable + * to equal something else. For example: + * ``` + * x = taintsource() + * if (x == 10) { + * taintsink(x); // not considered tainted + * } + * ``` + */ + + nodeIsBarrierEqualityCandidate(node, access, checkedVar) and + readsVariable(access.getDef(), checkedVar) + ) + } + + cached + predicate nodeIsBarrierIn(DataFlow::Node node) { + // don't use dataflow into taint sources, as this leads to duplicate results. + exists(Expr source | isUserInput(source, _) | + node = DataFlow::exprNode(source) + or + // This case goes together with the similar (but not identical) rule in + // `getNodeForSource`. + node = DataFlow::definitionByReferenceNodeFromArgument(source) + ) + or + // don't use dataflow into binary instructions if both operands are unpredictable + exists(BinaryInstruction iTo | + iTo = node.asInstruction() and + not predictableInstruction(iTo.getLeft()) and + not predictableInstruction(iTo.getRight()) and + // propagate taint from either the pointer or the offset, regardless of predictability + not iTo instanceof PointerArithmeticInstruction + ) + or + // don't use dataflow through calls to pure functions if two or more operands + // are unpredictable + exists(Instruction iFrom1, Instruction iFrom2, CallInstruction iTo | + iTo = node.asInstruction() and + isPureFunction(iTo.getStaticCallTarget().getName()) and + iFrom1 = iTo.getAnArgument() and + iFrom2 = iTo.getAnArgument() and + not predictableInstruction(iFrom1) and + not predictableInstruction(iFrom2) and + iFrom1 != iFrom2 + ) + } + + cached + Element adjustedSink(DataFlow::Node sink) { + // TODO: is it more appropriate to use asConvertedExpr here and avoid + // `getConversion*`? Or will that cause us to miss some cases where there's + // flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to + // pretend there was flow to the converted `Expr` for the sake of + // compatibility. + sink.asExpr().getConversion*() = result + or + // For compatibility, send flow from arguments to parameters, even for + // functions with no body. + exists(FunctionCall call, int i | + sink.asExpr() = call.getArgument(i) and + result = resolveCall(call).getParameter(i) + ) + or + // For compatibility, send flow into a `Variable` if there is flow to any + // Load or Store of that variable. + exists(CopyInstruction copy | + copy.getSourceValue() = sink.asInstruction() and + ( + readsVariable(copy, result) or + writesVariable(copy, result) + ) and + not hasUpperBoundsCheck(result) + ) + or + // For compatibility, send flow into a `NotExpr` even if it's part of a + // short-circuiting condition and thus might get skipped. + result.(NotExpr).getOperand() = sink.asExpr() + or + // Taint postfix and prefix crement operations when their operand is tainted. + result.(CrementOperation).getAnOperand() = sink.asExpr() + or + // Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted. + result.(AssignOperation).getAnOperand() = sink.asExpr() + or + result = + sink.asOperand() + .(SideEffectOperand) + .getUse() + .(ReadSideEffectInstruction) + .getArgumentDef() + .getUnconvertedResultExpression() + } + + /** + * Step to return value of a modeled function when an input taints the + * dereference of the return value. + */ + cached + predicate additionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) { + exists(CallInstruction call, Function func, FunctionInput modelIn, FunctionOutput modelOut | + n1.asOperand() = callInput(call, modelIn) and + ( + func.(TaintFunction).hasTaintFlow(modelIn, modelOut) + or + func.(DataFlowFunction).hasDataFlow(modelIn, modelOut) + ) and + call.getStaticCallTarget() = func and + modelOut.isReturnValueDeref() and + call = n2.asInstruction() + ) + } } -private Element adjustedSink(DataFlow::Node sink) { - // TODO: is it more appropriate to use asConvertedExpr here and avoid - // `getConversion*`? Or will that cause us to miss some cases where there's - // flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to - // pretend there was flow to the converted `Expr` for the sake of - // compatibility. - sink.asExpr().getConversion*() = result - or - // For compatibility, send flow from arguments to parameters, even for - // functions with no body. - exists(FunctionCall call, int i | - sink.asExpr() = call.getArgument(i) and - result = resolveCall(call).getParameter(i) - ) - or - // For compatibility, send flow into a `Variable` if there is flow to any - // Load or Store of that variable. - exists(CopyInstruction copy | - copy.getSourceValue() = sink.asInstruction() and - ( - readsVariable(copy, result) or - writesVariable(copy, result) - ) and - not hasUpperBoundsCheck(result) - ) - or - // For compatibility, send flow into a `NotExpr` even if it's part of a - // short-circuiting condition and thus might get skipped. - result.(NotExpr).getOperand() = sink.asExpr() - or - // Taint postfix and prefix crement operations when their operand is tainted. - result.(CrementOperation).getAnOperand() = sink.asExpr() - or - // Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted. - result.(AssignOperation).getAnOperand() = sink.asExpr() - or - result = - sink.asOperand() - .(SideEffectOperand) - .getUse() - .(ReadSideEffectInstruction) - .getArgumentDef() - .getUnconvertedResultExpression() -} +private import Cached /** * Holds if `tainted` may contain taint from `source`. @@ -402,19 +429,7 @@ module TaintedWithPath { readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable)) ) or - // Step to return value of a modeled function when an input taints the - // dereference of the return value - exists(CallInstruction call, Function func, FunctionInput modelIn, FunctionOutput modelOut | - n1.asOperand() = callInput(call, modelIn) and - ( - func.(TaintFunction).hasTaintFlow(modelIn, modelOut) - or - func.(DataFlowFunction).hasDataFlow(modelIn, modelOut) - ) and - call.getStaticCallTarget() = func and - modelOut.isReturnValueDeref() and - call = n2.asInstruction() - ) + additionalTaintStep(n1, n2) } override predicate isSanitizer(DataFlow::Node node) { diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll index e927634fec2..99d8555f8ca 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll @@ -2,11 +2,14 @@ private import cpp private import semmle.code.cpp.ir.IR private import semmle.code.cpp.ir.dataflow.DataFlow private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate +private import DataFlowImplCommon as DataFlowImplCommon /** * Gets a function that might be called by `call`. */ +cached Function viableCallable(CallInstruction call) { + DataFlowImplCommon::forceCachingInSameStage() and result = call.getStaticCallTarget() or // If the target of the call does not have a body in the snapshot, it might @@ -43,7 +46,6 @@ private module VirtualDispatch { abstract DataFlow::Node getDispatchValue(); /** Gets a candidate target for this call. */ - cached abstract Function resolve(); /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 8b446d28b86..9b14db7ef88 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 @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,29 +512,29 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } /** - * Holds if an output from `call` is reached in the flow covered by `revFlow`. + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. */ pragma[nomagic] private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { @@ -597,7 +594,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -610,6 +607,15 @@ private module Stage1 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(ArgNode arg, boolean toReturn | + revFlow(arg, toReturn, config) and + revFlowInToReturn(call, arg, config) and + revFlowIsReturned(call, toReturn, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, config)) and @@ -663,7 +669,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +681,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +741,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -833,6 +838,16 @@ private module Stage2 { PrevStage::revFlow(node, _, _, apa, config) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -899,13 +914,11 @@ private module Stage2 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -944,10 +957,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -956,17 +969,14 @@ private module Stage2 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -981,7 +991,13 @@ private module Stage2 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -992,7 +1008,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1012,6 +1028,27 @@ private module Stage2 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1077,14 +1114,12 @@ private module Stage2 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1132,13 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1146,9 +1178,14 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1199,13 +1236,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1218,6 +1255,15 @@ private module Stage2 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -1245,8 +1291,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1260,8 +1305,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1320,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1366,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1395,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1429,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1488,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1465,6 +1512,16 @@ private module Stage3 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -1538,13 +1595,11 @@ private module Stage3 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -1583,10 +1638,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1595,17 +1650,14 @@ private module Stage3 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -1620,7 +1672,13 @@ private module Stage3 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1631,7 +1689,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1651,6 +1709,27 @@ private module Stage3 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1716,14 +1795,12 @@ private module Stage3 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1771,13 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1785,9 +1859,14 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1838,13 +1917,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1857,6 +1936,15 @@ private module Stage3 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2088,7 +2176,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2127,17 +2215,12 @@ private module Stage4 { bindingset[innercc, inner, call] private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) { resolveReturn(innercc, inner, call) - or - innercc.(CallContextCall).matchesCall(call) } bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -2158,8 +2241,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2185,6 +2267,16 @@ private module Stage4 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -2258,13 +2350,11 @@ private module Stage4 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -2303,10 +2393,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2315,17 +2405,14 @@ private module Stage4 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -2340,7 +2427,13 @@ private module Stage4 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2351,7 +2444,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2371,6 +2464,27 @@ private module Stage4 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -2436,14 +2550,12 @@ private module Stage4 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -2491,13 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2505,9 +2614,14 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2558,13 +2672,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2577,6 +2691,15 @@ private module Stage4 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2609,7 +2732,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2630,7 +2753,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2761,7 +2884,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2982,7 +3105,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3132,7 +3255,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, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and @@ -3151,7 +3274,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3238,7 +3361,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3251,7 +3374,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3275,7 +3398,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3571,7 +3694,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3594,7 +3717,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3614,7 +3737,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3628,9 +3751,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3786,7 +3909,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3800,7 +3923,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3816,7 +3939,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3830,7 +3953,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3927,7 +4050,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3946,7 +4069,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3983,7 +4106,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4040,7 +4163,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4118,7 +4241,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4141,7 +4264,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 8b446d28b86..9b14db7ef88 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 @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,29 +512,29 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } /** - * Holds if an output from `call` is reached in the flow covered by `revFlow`. + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. */ pragma[nomagic] private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { @@ -597,7 +594,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -610,6 +607,15 @@ private module Stage1 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(ArgNode arg, boolean toReturn | + revFlow(arg, toReturn, config) and + revFlowInToReturn(call, arg, config) and + revFlowIsReturned(call, toReturn, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, config)) and @@ -663,7 +669,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +681,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +741,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -833,6 +838,16 @@ private module Stage2 { PrevStage::revFlow(node, _, _, apa, config) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -899,13 +914,11 @@ private module Stage2 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -944,10 +957,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -956,17 +969,14 @@ private module Stage2 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -981,7 +991,13 @@ private module Stage2 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -992,7 +1008,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1012,6 +1028,27 @@ private module Stage2 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1077,14 +1114,12 @@ private module Stage2 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1132,13 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1146,9 +1178,14 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1199,13 +1236,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1218,6 +1255,15 @@ private module Stage2 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -1245,8 +1291,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1260,8 +1305,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1320,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1366,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1395,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1429,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1488,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1465,6 +1512,16 @@ private module Stage3 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -1538,13 +1595,11 @@ private module Stage3 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -1583,10 +1638,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1595,17 +1650,14 @@ private module Stage3 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -1620,7 +1672,13 @@ private module Stage3 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1631,7 +1689,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1651,6 +1709,27 @@ private module Stage3 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1716,14 +1795,12 @@ private module Stage3 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1771,13 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1785,9 +1859,14 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1838,13 +1917,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1857,6 +1936,15 @@ private module Stage3 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2088,7 +2176,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2127,17 +2215,12 @@ private module Stage4 { bindingset[innercc, inner, call] private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) { resolveReturn(innercc, inner, call) - or - innercc.(CallContextCall).matchesCall(call) } bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -2158,8 +2241,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2185,6 +2267,16 @@ private module Stage4 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -2258,13 +2350,11 @@ private module Stage4 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -2303,10 +2393,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2315,17 +2405,14 @@ private module Stage4 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -2340,7 +2427,13 @@ private module Stage4 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2351,7 +2444,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2371,6 +2464,27 @@ private module Stage4 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -2436,14 +2550,12 @@ private module Stage4 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -2491,13 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2505,9 +2614,14 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2558,13 +2672,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2577,6 +2691,15 @@ private module Stage4 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2609,7 +2732,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2630,7 +2753,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2761,7 +2884,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2982,7 +3105,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3132,7 +3255,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, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and @@ -3151,7 +3274,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3238,7 +3361,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3251,7 +3374,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3275,7 +3398,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3571,7 +3694,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3594,7 +3717,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3614,7 +3737,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3628,9 +3751,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3786,7 +3909,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3800,7 +3923,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3816,7 +3939,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3830,7 +3953,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3927,7 +4050,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3946,7 +4069,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3983,7 +4106,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4040,7 +4163,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4118,7 +4241,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4141,7 +4264,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 8b446d28b86..9b14db7ef88 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 @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,29 +512,29 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } /** - * Holds if an output from `call` is reached in the flow covered by `revFlow`. + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. */ pragma[nomagic] private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { @@ -597,7 +594,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -610,6 +607,15 @@ private module Stage1 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(ArgNode arg, boolean toReturn | + revFlow(arg, toReturn, config) and + revFlowInToReturn(call, arg, config) and + revFlowIsReturned(call, toReturn, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, config)) and @@ -663,7 +669,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +681,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +741,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -833,6 +838,16 @@ private module Stage2 { PrevStage::revFlow(node, _, _, apa, config) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -899,13 +914,11 @@ private module Stage2 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -944,10 +957,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -956,17 +969,14 @@ private module Stage2 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -981,7 +991,13 @@ private module Stage2 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -992,7 +1008,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1012,6 +1028,27 @@ private module Stage2 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1077,14 +1114,12 @@ private module Stage2 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1132,13 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1146,9 +1178,14 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1199,13 +1236,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1218,6 +1255,15 @@ private module Stage2 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -1245,8 +1291,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1260,8 +1305,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1320,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1366,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1395,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1429,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1488,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1465,6 +1512,16 @@ private module Stage3 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -1538,13 +1595,11 @@ private module Stage3 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -1583,10 +1638,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1595,17 +1650,14 @@ private module Stage3 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -1620,7 +1672,13 @@ private module Stage3 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1631,7 +1689,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1651,6 +1709,27 @@ private module Stage3 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1716,14 +1795,12 @@ private module Stage3 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1771,13 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1785,9 +1859,14 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1838,13 +1917,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1857,6 +1936,15 @@ private module Stage3 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2088,7 +2176,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2127,17 +2215,12 @@ private module Stage4 { bindingset[innercc, inner, call] private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) { resolveReturn(innercc, inner, call) - or - innercc.(CallContextCall).matchesCall(call) } bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -2158,8 +2241,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2185,6 +2267,16 @@ private module Stage4 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -2258,13 +2350,11 @@ private module Stage4 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -2303,10 +2393,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2315,17 +2405,14 @@ private module Stage4 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -2340,7 +2427,13 @@ private module Stage4 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2351,7 +2444,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2371,6 +2464,27 @@ private module Stage4 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -2436,14 +2550,12 @@ private module Stage4 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -2491,13 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2505,9 +2614,14 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2558,13 +2672,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2577,6 +2691,15 @@ private module Stage4 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2609,7 +2732,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2630,7 +2753,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2761,7 +2884,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2982,7 +3105,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3132,7 +3255,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, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and @@ -3151,7 +3274,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3238,7 +3361,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3251,7 +3374,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3275,7 +3398,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3571,7 +3694,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3594,7 +3717,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3614,7 +3737,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3628,9 +3751,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3786,7 +3909,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3800,7 +3923,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3816,7 +3939,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3830,7 +3953,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3927,7 +4050,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3946,7 +4069,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3983,7 +4106,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4040,7 +4163,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4118,7 +4241,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4141,7 +4264,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 8b446d28b86..9b14db7ef88 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 @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,29 +512,29 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } /** - * Holds if an output from `call` is reached in the flow covered by `revFlow`. + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. */ pragma[nomagic] private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { @@ -597,7 +594,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -610,6 +607,15 @@ private module Stage1 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(ArgNode arg, boolean toReturn | + revFlow(arg, toReturn, config) and + revFlowInToReturn(call, arg, config) and + revFlowIsReturned(call, toReturn, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, config)) and @@ -663,7 +669,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +681,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +741,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -833,6 +838,16 @@ private module Stage2 { PrevStage::revFlow(node, _, _, apa, config) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -899,13 +914,11 @@ private module Stage2 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -944,10 +957,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -956,17 +969,14 @@ private module Stage2 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -981,7 +991,13 @@ private module Stage2 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -992,7 +1008,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1012,6 +1028,27 @@ private module Stage2 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1077,14 +1114,12 @@ private module Stage2 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1132,13 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1146,9 +1178,14 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1199,13 +1236,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1218,6 +1255,15 @@ private module Stage2 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -1245,8 +1291,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1260,8 +1305,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1320,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1366,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1395,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1429,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1488,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1465,6 +1512,16 @@ private module Stage3 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -1538,13 +1595,11 @@ private module Stage3 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -1583,10 +1638,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1595,17 +1650,14 @@ private module Stage3 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -1620,7 +1672,13 @@ private module Stage3 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1631,7 +1689,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1651,6 +1709,27 @@ private module Stage3 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1716,14 +1795,12 @@ private module Stage3 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1771,13 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1785,9 +1859,14 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1838,13 +1917,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1857,6 +1936,15 @@ private module Stage3 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2088,7 +2176,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2127,17 +2215,12 @@ private module Stage4 { bindingset[innercc, inner, call] private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) { resolveReturn(innercc, inner, call) - or - innercc.(CallContextCall).matchesCall(call) } bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -2158,8 +2241,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2185,6 +2267,16 @@ private module Stage4 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -2258,13 +2350,11 @@ private module Stage4 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -2303,10 +2393,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2315,17 +2405,14 @@ private module Stage4 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -2340,7 +2427,13 @@ private module Stage4 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2351,7 +2444,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2371,6 +2464,27 @@ private module Stage4 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -2436,14 +2550,12 @@ private module Stage4 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -2491,13 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2505,9 +2614,14 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2558,13 +2672,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2577,6 +2691,15 @@ private module Stage4 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2609,7 +2732,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2630,7 +2753,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2761,7 +2884,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2982,7 +3105,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3132,7 +3255,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, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and @@ -3151,7 +3274,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3238,7 +3361,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3251,7 +3374,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3275,7 +3398,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3571,7 +3694,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3594,7 +3717,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3614,7 +3737,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3628,9 +3751,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3786,7 +3909,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3800,7 +3923,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3816,7 +3939,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3830,7 +3953,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3927,7 +4050,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3946,7 +4069,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3983,7 +4106,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4040,7 +4163,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4118,7 +4241,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4141,7 +4264,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and 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 a51c20c2288..462e89ac9ed 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll @@ -31,26 +31,26 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * currently excludes read-steps, store-steps, and flow-through. * * The analysis uses non-linear recursion: When computing a flow path in or out - * of a call, we use the results of the analysis recursively to resolve lamba + * of a call, we use the results of the analysis recursively to resolve lambda * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -118,8 +118,8 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeType(node)) + if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode + then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -129,7 +129,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { lambdaCall(lambdaCall, kind, node) and - t = getNodeType(node) and + t = getNodeDataFlowType(node) and toReturn = false and toJump = false and lastCall = TDataFlowCallNone() @@ -146,7 +146,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -160,7 +160,7 @@ private module LambdaFlow { toJump = true and lastCall = TDataFlowCallNone() | - jumpStep(node, mid) and + jumpStepCached(node, mid) and t = t0 or exists(boolean preservesValue | @@ -168,7 +168,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -176,7 +176,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -227,7 +227,7 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump, + DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) @@ -242,6 +242,89 @@ private DataFlowCallable viableCallableExt(DataFlowCall call) { cached private module Cached { + /** + * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to + * force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby + * collapsing the two stages. + */ + cached + predicate forceCachingInSameStage() { any() } + + cached + predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() } + + cached + predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { + c = call.getEnclosingCallable() + } + + cached + predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } + + cached + predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } + + cached + predicate clearsContentCached(Node n, Content c) { clearsContent(n, c) } + + cached + predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } + + cached + predicate outNodeExt(Node n) { + n instanceof OutNode + or + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode + } + + cached + predicate hiddenNode(Node n) { nodeIsHidden(n) } + + cached + OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { + result = getAnOutNode(call, k.(ValueReturnKind).getKind()) + or + exists(ArgNode arg | + result.(PostUpdateNode).getPreUpdateNode() = arg and + arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) + ) + } + + cached + predicate returnNodeExt(Node n, ReturnKindExt k) { + k = TValueReturn(n.(ReturnNode).getKind()) + or + exists(ParamNode p, int pos | + parameterValueFlowsToPreUpdate(p, n) and + p.isParameterOf(_, pos) and + k = TParamUpdate(pos) + ) + } + + cached + predicate castNode(Node n) { n instanceof CastNode } + + cached + predicate castingNode(Node n) { + castNode(n) or + n instanceof ParamNode or + n instanceof OutNodeExt or + // For reads, `x.f`, we want to check that the tracked type after the read (which + // is obtained by popping the head of the access path stack) is compatible with + // the type of `x.f`. + read(_, _, n) + } + + cached + predicate parameterNode(Node n, DataFlowCallable c, int i) { + n.(ParameterNode).isParameterOf(c, i) + } + + cached + predicate argumentNode(Node n, DataFlowCall call, int pos) { + n.(ArgumentNode).argumentOf(call, pos) + } + /** * Gets a viable target for the lambda call `call`. * @@ -261,7 +344,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParam(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableExt(call), i) } @@ -270,11 +353,11 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and - compatibleTypes(getNodeType(arg), getNodeType(p)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) ) } @@ -312,7 +395,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { p = node and read = false or @@ -325,30 +408,30 @@ private module Cached { // read exists(Node mid | parameterValueFlowCand(p, mid, false) and - readStep(mid, _, node) and + read(mid, _, node) and read = true ) or // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) { + private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -360,7 +443,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -369,9 +452,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read + DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -382,14 +465,14 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNode arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) ) } - predicate cand(ParameterNode p, Node n) { + predicate cand(ParamNode p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -416,21 +499,21 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(p), getNodeType(node)) + compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) or // getter - compatibleTypes(read.getContentType(), getNodeType(node)) + compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -447,7 +530,7 @@ private module Cached { readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeType(p), read.getContainerType()) + compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) ) or parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) @@ -455,34 +538,32 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) } pragma[nomagic] - private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ReadStepTypesOption read - ) { + private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -496,18 +577,18 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) | // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(arg), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) or // getter - compatibleTypes(getNodeType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) ) } @@ -516,7 +597,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNode arg, Content c, Node out) { + predicate getterStep(ArgNode arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -529,7 +610,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ReadStepTypesOption read + ParamNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -553,7 +634,7 @@ private module Cached { private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { mayBenefitFromCallContext(call, callable) or - callable = call.getEnclosingCallable() and + callEnclosingCallable(call, callable) and exists(viableCallableLambda(call, TDataFlowCallSome(_))) } @@ -611,7 +692,7 @@ private module Cached { mayBenefitFromCallContextExt(call, _) and c = viableCallableExt(call) and ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and + tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and ctxtgts < tgts ) } @@ -635,8 +716,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - cached - predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -644,9 +724,9 @@ private module Cached { Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType ) { storeStep(node1, c, node2) and - readStep(_, c, _) and - contentType = getNodeType(node1) and - containerType = getNodeType(node2) + read(_, c, _) and + contentType = getNodeDataFlowType(node1) and + containerType = getNodeDataFlowType(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and @@ -654,12 +734,15 @@ private module Cached { | argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or - readStep(n2, c, n1) and - contentType = getNodeType(n1) and - containerType = getNodeType(n2) + read(n2, c, n1) and + contentType = getNodeDataFlowType(n1) and + containerType = getNodeDataFlowType(n2) ) } + cached + predicate read(Node node1, Content c, Node node2) { readStep(node1, c, node2) } + /** * Holds if data can flow from `node1` to `node2` via a direct assignment to * `f`. @@ -678,8 +761,9 @@ private module Cached { * are aliases. A typical example is a function returning `this`, implementing a fluent * interface. */ - cached - predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) { + private predicate reverseStepThroughInputOutputAlias( + PostUpdateNode fromNode, PostUpdateNode toNode + ) { exists(Node fromPre, Node toPre | fromPre = fromNode.getPreUpdateNode() and toPre = toNode.getPreUpdateNode() @@ -688,14 +772,20 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNode), fromPre) + toPre.(ArgNode).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgNode), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) ) } + cached + predicate simpleLocalFlowStepExt(Node node1, Node node2) { + simpleLocalFlowStep(node1, node2) or + reverseStepThroughInputOutputAlias(node1, node2) + } + /** * Holds if the call context `call` either improves virtual dispatch in * `callable` or if it allows us to prune unreachable nodes in `callable`. @@ -704,7 +794,7 @@ private module Cached { predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { reducedViableImplInCallContext(_, callable, call) or - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) } cached @@ -726,12 +816,12 @@ private module Cached { cached newtype TLocalFlowCallContext = TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) } + TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -761,23 +851,15 @@ private module Cached { * A `Node` at which a cast can occur such that the type should be checked. */ class CastingNode extends Node { - CastingNode() { - this instanceof ParameterNode or - this instanceof CastNode or - this instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - readStep(_, _, this) - } + CastingNode() { castingNode(this) } } private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { - readStep(n1, c, n2) and - container = getNodeType(n1) and - content = getNodeType(n2) + read(n1, c, n2) and + container = getNodeDataFlowType(n1) and + content = getNodeDataFlowType(n2) } private newtype TReadStepTypesOption = @@ -854,7 +936,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNode p | getNodeEnclosingCallable(p) = callable) + exists(ParamNode p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -866,7 +948,7 @@ class CallContextReturn extends CallContextNoCall, TReturn { } override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable) + exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) } } @@ -899,7 +981,7 @@ class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall } private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) } /** @@ -913,26 +995,37 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) else result instanceof LocalCallContextAny } +/** + * The value of a parameter at function entry, viewed as a node in a data + * flow graph. + */ +class ParamNode extends Node { + ParamNode() { parameterNode(this, _, _) } + + /** + * Holds if this node is the parameter of callable `c` at the specified + * (zero-based) position. + */ + predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) } +} + +/** A data-flow node that represents a call argument. */ +class ArgNode extends Node { + ArgNode() { argumentNode(this, _, _) } + + /** Holds if this argument occurs at the given position in the given call. */ + final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } +} + /** * A node from which flow can return to the caller. This is either a regular * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. */ class ReturnNodeExt extends Node { - ReturnNodeExt() { - this instanceof ReturnNode or - parameterValueFlowsToPreUpdate(_, this) - } + ReturnNodeExt() { returnNodeExt(this, _) } /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { - result = TValueReturn(this.(ReturnNode).getKind()) - or - exists(ParameterNode p, int pos | - parameterValueFlowsToPreUpdate(p, this) and - p.isParameterOf(_, pos) and - result = TParamUpdate(pos) - ) - } + ReturnKindExt getKind() { returnNodeExt(this, result) } } /** @@ -940,11 +1033,7 @@ class ReturnNodeExt extends Node { * or a post-update node associated with a call argument. */ class OutNodeExt extends Node { - OutNodeExt() { - this instanceof OutNode - or - this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode - } + OutNodeExt() { outNodeExt(this) } } /** @@ -957,7 +1046,7 @@ abstract class ReturnKindExt extends TReturnKindExt { abstract string toString(); /** Gets a node corresponding to data flow out of `call`. */ - abstract OutNodeExt getAnOutNode(DataFlowCall call); + final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } } class ValueReturnKind extends ReturnKindExt, TValueReturn { @@ -968,10 +1057,6 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn { ReturnKind getKind() { result = kind } override string toString() { result = kind.toString() } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - result = getAnOutNode(call, this.getKind()) - } } class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { @@ -982,13 +1067,6 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { int getPosition() { result = pos } override string toString() { result = "param update " + pos } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - exists(ArgumentNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, this.getPosition()) - ) - } } /** A callable tagged with a relevant return kind. */ @@ -1015,10 +1093,13 @@ class ReturnPosition extends TReturnPosition0 { */ pragma[inline] DataFlowCallable getNodeEnclosingCallable(Node n) { - exists(Node n0 | - pragma[only_bind_into](n0) = n and - pragma[only_bind_into](result) = n0.getEnclosingCallable() - ) + nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) +} + +/** Gets the type of `n` used for type pruning. */ +pragma[inline] +DataFlowType getNodeDataFlowType(Node n) { + nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) } pragma[noinline] @@ -1042,7 +1123,7 @@ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall cc instanceof CallContextAny and callable = viableCallableExt(call) or exists(DataFlowCallable c0, DataFlowCall call0 | - call0.getEnclosingCallable() = callable and + callEnclosingCallable(call0, callable) and cc = TReturn(c0, call0) and c0 = prunedViableImplInCallContextReverse(call0, call) ) @@ -1063,8 +1144,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallableExt(call) and cc instanceof CallContextReturn } -predicate read = readStep/3; - /** An optional Boolean value. */ class BooleanOption extends TBooleanOption { string toString() { @@ -1116,7 +1195,7 @@ abstract class AccessPathFront extends TAccessPathFront { TypedContent getHead() { this = TFrontHead(result) } - predicate isClearedAt(Node n) { clearsContent(n, getHead().getContent()) } + predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index 3b94e574de0..43b82f4d517 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 @@ -557,5 +557,5 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) /** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() } -/** Extra data-flow steps needed for lamba flow analysis. */ +/** Extra data-flow steps needed for lambda flow analysis. */ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll index 76ca7b215dc..915f51b4576 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll @@ -12,10 +12,20 @@ private import semmle.code.cpp.controlflow.IRGuards private import semmle.code.cpp.models.interfaces.DataFlow cached -private newtype TIRDataFlowNode = - TInstructionNode(Instruction i) or - TOperandNode(Operand op) or - TVariableNode(Variable var) +private module Cached { + cached + newtype TIRDataFlowNode = + TInstructionNode(Instruction i) or + TOperandNode(Operand op) or + TVariableNode(Variable var) + + cached + predicate localFlowStepCached(Node nodeFrom, Node nodeTo) { + simpleLocalFlowStep(nodeFrom, nodeTo) + } +} + +private import Cached /** * A node in a data flow graph. @@ -362,15 +372,22 @@ private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode { /** * Not every store instruction generates a chi instruction that we can attach a PostUpdateNode to. - * For instance, an update to a field of a struct containing only one field. For these cases we - * attach the PostUpdateNode to the store instruction. There's no obvious pre update node for this case - * (as the entire memory is updated), so `getPreUpdateNode` is implemented as `none()`. + * For instance, an update to a field of a struct containing only one field. Even if the store does + * have a chi instruction, a subsequent use of the result of the store may be linked directly to the + * result of the store as an inexact definition if the store totally overlaps the use. For these + * cases we attach the PostUpdateNode to the store instruction. There's no obvious pre update node + * for this case (as the entire memory is updated), so `getPreUpdateNode` is implemented as + * `none()`. */ private class ExplicitSingleFieldStoreQualifierNode extends PartialDefinitionNode { override StoreInstruction instr; ExplicitSingleFieldStoreQualifierNode() { - not exists(ChiInstruction chi | chi.getPartial() = instr) and + ( + instr.getAUse().isDefinitionInexact() + or + not exists(ChiInstruction chi | chi.getPartial() = instr) + ) and // Without this condition any store would create a `PostUpdateNode`. instr.getDestinationAddress() instanceof FieldAddressInstruction } @@ -583,7 +600,7 @@ Node uninitializedNode(LocalVariable v) { none() } * Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local * (intra-procedural) step. */ -predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFrom, nodeTo) } +predicate localFlowStep = localFlowStepCached/2; /** * INTERNAL: do not use. @@ -591,7 +608,6 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFr * This is the local flow predicate that's used as a building block in global * data flow. It may have less flow than the `localFlowStep` predicate. */ -cached predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { // Operand -> Instruction flow simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction()) @@ -649,7 +665,7 @@ private predicate simpleOperandLocalFlowStep(Instruction iFrom, Operand opTo) { exists(LoadInstruction load | load.getSourceValueOperand() = opTo and opTo.getAnyDef() = iFrom and - isSingleFieldClass(iFrom.getResultType(), opTo) + isSingleFieldClass(pragma[only_bind_out](pragma[only_bind_out](iFrom).getResultType()), opTo) ) } @@ -732,16 +748,10 @@ private predicate modelFlow(Operand opFrom, Instruction iTo) { ) or exists(int index, ReadSideEffectInstruction read | - modelIn.isParameterDeref(index) and + modelIn.isParameterDerefOrQualifierObject(index) and read = getSideEffectFor(call, index) and opFrom = read.getSideEffectOperand() ) - or - exists(ReadSideEffectInstruction read | - modelIn.isQualifierObject() and - read = getSideEffectFor(call, -1) and - opFrom = read.getSideEffectOperand() - ) ) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll index 337dc71a3ca..16182296e40 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll @@ -6,34 +6,7 @@ private import semmle.code.cpp.ir.ValueNumbering private import semmle.code.cpp.ir.IR private import semmle.code.cpp.ir.dataflow.DataFlow private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil - -/** - * Gets a short ID for an IR dataflow node. - * - For `Instruction`s, this is just the result ID of the instruction (e.g. `m128`). - * - For `Operand`s, this is the label of the operand, prefixed with the result ID of the - * instruction and a dot (e.g. `m128.left`). - * - For `Variable`s, this is the qualified name of the variable. - */ -private string nodeId(DataFlow::Node node, int order1, int order2) { - exists(Instruction instruction | instruction = node.asInstruction() | - result = instruction.getResultId() and - order1 = instruction.getBlock().getDisplayIndex() and - order2 = instruction.getDisplayIndexInBlock() - ) - or - exists(Operand operand, Instruction instruction | - operand = node.asOperand() and - instruction = operand.getUse() - | - result = instruction.getResultId() + "." + operand.getDumpId() and - order1 = instruction.getBlock().getDisplayIndex() and - order2 = instruction.getDisplayIndexInBlock() - ) - or - result = "var(" + node.asVariable().getQualifiedName() + ")" and - order1 = 1000000 and - order2 = 0 -} +private import PrintIRUtilities /** * Gets the local dataflow from other nodes in the same function to this node. diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRStoreSteps.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRStoreSteps.qll new file mode 100644 index 00000000000..8c318216217 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRStoreSteps.qll @@ -0,0 +1,33 @@ +/** + * Print the dataflow local store steps in IR dumps. + */ + +private import cpp +// The `ValueNumbering` library has to be imported right after `cpp` to ensure +// that the cached IR gets the same checksum here as it does in queries that use +// `ValueNumbering` without `DataFlow`. +private import semmle.code.cpp.ir.ValueNumbering +private import semmle.code.cpp.ir.IR +private import semmle.code.cpp.ir.dataflow.DataFlow +private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil +private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate +private import PrintIRUtilities + +/** + * Property provider for local IR dataflow store steps. + */ +class LocalFlowPropertyProvider extends IRPropertyProvider { + override string getInstructionProperty(Instruction instruction, string key) { + exists(DataFlow::Node objectNode, Content content | + key = "content[" + content.toString() + "]" and + instruction = objectNode.asInstruction() and + result = + strictconcat(string element, DataFlow::Node fieldNode | + storeStep(fieldNode, content, objectNode) and + element = nodeId(fieldNode, _, _) + | + element, ", " + ) + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRUtilities.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRUtilities.qll new file mode 100644 index 00000000000..5fc15cf986c --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRUtilities.qll @@ -0,0 +1,39 @@ +/** + * Shared utilities used when printing dataflow annotations in IR dumps. + */ + +private import cpp +// The `ValueNumbering` library has to be imported right after `cpp` to ensure +// that the cached IR gets the same checksum here as it does in queries that use +// `ValueNumbering` without `DataFlow`. +private import semmle.code.cpp.ir.ValueNumbering +private import semmle.code.cpp.ir.IR +private import semmle.code.cpp.ir.dataflow.DataFlow + +/** + * Gets a short ID for an IR dataflow node. + * - For `Instruction`s, this is just the result ID of the instruction (e.g. `m128`). + * - For `Operand`s, this is the label of the operand, prefixed with the result ID of the + * instruction and a dot (e.g. `m128.left`). + * - For `Variable`s, this is the qualified name of the variable. + */ +string nodeId(DataFlow::Node node, int order1, int order2) { + exists(Instruction instruction | instruction = node.asInstruction() | + result = instruction.getResultId() and + order1 = instruction.getBlock().getDisplayIndex() and + order2 = instruction.getDisplayIndexInBlock() + ) + or + exists(Operand operand, Instruction instruction | + operand = node.asOperand() and + instruction = operand.getUse() + | + result = instruction.getResultId() + "." + operand.getDumpId() and + order1 = instruction.getBlock().getDisplayIndex() and + order2 = instruction.getDisplayIndexInBlock() + ) + or + result = "var(" + node.asVariable().getQualifiedName() + ")" and + order1 = 1000000 and + order2 = 0 +} 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 af6f3d81982..453838215ff 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -297,7 +297,8 @@ class Instruction extends Construction::TStageInstruction { /** * Gets the opcode that specifies the operation performed by this instruction. */ - final Opcode getOpcode() { result = Construction::getInstructionOpcode(this) } + pragma[inline] + final Opcode getOpcode() { Construction::getInstructionOpcode(result, this) } /** * Gets all direct uses of the result of this instruction. The result can be @@ -1645,6 +1646,19 @@ class CallInstruction extends Instruction { * Gets the number of arguments of the call, including the `this` pointer, if any. */ final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) } + + /** + * Holds if the result is a side effect for the argument at the specified index, or `this` if + * `index` is `-1`. + * + * This helper predicate makes it easy to join on both of these columns at once, avoiding + * pathological join orders in case the argument index should get joined first. + */ + pragma[noinline] + final SideEffectInstruction getAParameterSideEffect(int index) { + this = result.getPrimaryInstruction() and + index = result.(IndexedInstruction).getIndex() + } } /** @@ -1980,6 +1994,14 @@ class PhiInstruction extends Instruction { */ pragma[noinline] final Instruction getAnInput() { result = this.getAnInputOperand().getDef() } + + /** + * Gets the input operand representing the value that flows from the specified predecessor block. + */ + final PhiInputOperand getInputOperand(IRBlock predecessorBlock) { + result = this.getAnOperand() and + result.getPredecessorBlock() = predecessorBlock + } } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll index a2ce0662dc2..d7cf89ca9aa 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll @@ -28,11 +28,15 @@ class Operand extends TStageOperand { cached Operand() { // Ensure that the operand does not refer to instructions from earlier stages that are unreachable here - exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or - exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or + exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) + or + exists(Instruction use | this = nonSSAMemoryOperand(use, _)) + or exists(Instruction use, Instruction def, IRBlock predecessorBlock | - this = phiOperand(use, def, predecessorBlock, _) - ) or + this = phiOperand(use, def, predecessorBlock, _) or + this = reusedPhiOperand(use, def, predecessorBlock, _) + ) + or exists(Instruction use | this = chiOperand(use, _)) } @@ -431,7 +435,11 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand { Overlap overlap; cached - PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) } + PhiInputOperand() { + this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) + or + this = reusedPhiOperand(useInstr, defInstr, predecessorBlock, overlap) + } override string toString() { result = "Phi" } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll index 19fb0490f80..9997b5b49a7 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll @@ -4,6 +4,71 @@ private import AliasAnalysisImports private class IntValue = Ints::IntValue; +/** + * If `instr` is a `SideEffectInstruction`, gets the primary `CallInstruction` that caused the side + * effect. If `instr` is a `CallInstruction`, gets that same `CallInstruction`. + */ +private CallInstruction getPrimaryCall(Instruction instr) { + result = instr + or + result = instr.(SideEffectInstruction).getPrimaryInstruction() +} + +/** + * Holds if `operand` serves as an input argument (or indirection) to `call`, in the position + * specified by `input`. + */ +private predicate isCallInput( + CallInstruction call, Operand operand, AliasModels::FunctionInput input +) { + call = getPrimaryCall(operand.getUse()) and + ( + exists(int index | + input.isParameterOrQualifierAddress(index) and + operand = call.getArgumentOperand(index) + ) + or + exists(int index, ReadSideEffectInstruction read | + input.isParameterDerefOrQualifierObject(index) and + read = call.getAParameterSideEffect(index) and + operand = read.getSideEffectOperand() + ) + ) +} + +/** + * Holds if `instr` serves as a return value or output argument indirection for `call`, in the + * position specified by `output`. + */ +private predicate isCallOutput( + CallInstruction call, Instruction instr, AliasModels::FunctionOutput output +) { + call = getPrimaryCall(instr) and + ( + output.isReturnValue() and instr = call + or + exists(int index, WriteSideEffectInstruction write | + output.isParameterDerefOrQualifierObject(index) and + write = call.getAParameterSideEffect(index) and + instr = write + ) + ) +} + +/** + * Holds if the address in `operand` flows directly to the result of `resultInstr` due to modeled + * address flow through a function call. + */ +private predicate hasAddressFlowThroughCall(Operand operand, Instruction resultInstr) { + exists( + CallInstruction call, AliasModels::FunctionInput input, AliasModels::FunctionOutput output + | + call.getStaticCallTarget().(AliasModels::AliasFunction).hasAddressFlow(input, output) and + isCallInput(call, operand, input) and + isCallOutput(call, resultInstr, output) + ) +} + /** * Holds if the operand `tag` of instruction `instr` is used in a way that does * not result in any address held in that operand from escaping beyond the @@ -25,6 +90,9 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) { or // Converting an address to a `bool` does not escape the address. instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType + or + instr instanceof CallInstruction and + not exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis()) ) ) or @@ -34,7 +102,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) { private predicate operandEscapesDomain(Operand operand) { not operandIsConsumedWithoutEscaping(operand) and - not operandIsPropagated(operand, _) and + not operandIsPropagated(operand, _, _) and not isArgumentForParameter(_, operand, _) and not isOnlyEscapesViaReturnArgument(operand) and not operand.getUse() instanceof ReturnValueInstruction and @@ -69,67 +137,67 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) { } /** - * Holds if any address held in operand `tag` of instruction `instr` is - * propagated to the result of `instr`, offset by the number of bits in - * `bitOffset`. If the address is propagated, but the offset is not known to be - * a constant, then `bitOffset` is unknown. + * Holds if any address held in operand `operand` is propagated to the result of `instr`, offset by + * the number of bits in `bitOffset`. If the address is propagated, but the offset is not known to + * be a constant, then `bitOffset` is `unknown()`. */ -private predicate operandIsPropagated(Operand operand, IntValue bitOffset) { - exists(Instruction instr | - instr = operand.getUse() and - ( - // Converting to a non-virtual base class adds the offset of the base class. - exists(ConvertToNonVirtualBaseInstruction convert | - convert = instr and - bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8) - ) - or - // Conversion using dynamic_cast results in an unknown offset - instr instanceof CheckedConvertOrNullInstruction and - bitOffset = Ints::unknown() - or - // Converting to a derived class subtracts the offset of the base class. - exists(ConvertToDerivedInstruction convert | - convert = instr and - bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8)) - ) - or - // Converting to a virtual base class adds an unknown offset. - instr instanceof ConvertToVirtualBaseInstruction and - bitOffset = Ints::unknown() - or - // Conversion to another pointer type propagates the source address. - exists(ConvertInstruction convert, IRType resultType | - convert = instr and - resultType = convert.getResultIRType() and - resultType instanceof IRAddressType and - bitOffset = 0 - ) - or - // Adding an integer to or subtracting an integer from a pointer propagates - // the address with an offset. - exists(PointerOffsetInstruction ptrOffset | - ptrOffset = instr and - operand = ptrOffset.getLeftOperand() and - bitOffset = getPointerBitOffset(ptrOffset) - ) - or - // Computing a field address from a pointer propagates the address plus the - // offset of the field. - bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField()) - or - // A copy propagates the source value. - operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 - or - // Some functions are known to propagate an argument - isAlwaysReturnedArgument(operand) and bitOffset = 0 +private predicate operandIsPropagated(Operand operand, IntValue bitOffset, Instruction instr) { + // Some functions are known to propagate an argument + hasAddressFlowThroughCall(operand, instr) and + bitOffset = 0 + or + instr = operand.getUse() and + ( + // Converting to a non-virtual base class adds the offset of the base class. + exists(ConvertToNonVirtualBaseInstruction convert | + convert = instr and + bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8) ) + or + // Conversion using dynamic_cast results in an unknown offset + instr instanceof CheckedConvertOrNullInstruction and + bitOffset = Ints::unknown() + or + // Converting to a derived class subtracts the offset of the base class. + exists(ConvertToDerivedInstruction convert | + convert = instr and + bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8)) + ) + or + // Converting to a virtual base class adds an unknown offset. + instr instanceof ConvertToVirtualBaseInstruction and + bitOffset = Ints::unknown() + or + // Conversion to another pointer type propagates the source address. + exists(ConvertInstruction convert, IRType resultType | + convert = instr and + resultType = convert.getResultIRType() and + resultType instanceof IRAddressType and + bitOffset = 0 + ) + or + // Adding an integer to or subtracting an integer from a pointer propagates + // the address with an offset. + exists(PointerOffsetInstruction ptrOffset | + ptrOffset = instr and + operand = ptrOffset.getLeftOperand() and + bitOffset = getPointerBitOffset(ptrOffset) + ) + or + // Computing a field address from a pointer propagates the address plus the + // offset of the field. + bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField()) + or + // A copy propagates the source value. + operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 ) } private predicate operandEscapesNonReturn(Operand operand) { - // The address is propagated to the result of the instruction, and that result itself is returned - operandIsPropagated(operand, _) and resultEscapesNonReturn(operand.getUse()) + exists(Instruction instr | + // The address is propagated to the result of the instruction, and that result itself is returned + operandIsPropagated(operand, _, instr) and resultEscapesNonReturn(instr) + ) or // The operand is used in a function call which returns it, and the return value is then returned exists(CallInstruction ci, Instruction init | @@ -151,9 +219,11 @@ private predicate operandEscapesNonReturn(Operand operand) { } private predicate operandMayReachReturn(Operand operand) { - // The address is propagated to the result of the instruction, and that result itself is returned - operandIsPropagated(operand, _) and - resultMayReachReturn(operand.getUse()) + exists(Instruction instr | + // The address is propagated to the result of the instruction, and that result itself is returned + operandIsPropagated(operand, _, instr) and + resultMayReachReturn(instr) + ) or // The operand is used in a function call which returns it, and the return value is then returned exists(CallInstruction ci, Instruction init | @@ -173,9 +243,9 @@ private predicate operandMayReachReturn(Operand operand) { private predicate operandReturned(Operand operand, IntValue bitOffset) { // The address is propagated to the result of the instruction, and that result itself is returned - exists(IntValue bitOffset1, IntValue bitOffset2 | - operandIsPropagated(operand, bitOffset1) and - resultReturned(operand.getUse(), bitOffset2) and + exists(Instruction instr, IntValue bitOffset1, IntValue bitOffset2 | + operandIsPropagated(operand, bitOffset1, instr) and + resultReturned(instr, bitOffset2) and bitOffset = Ints::add(bitOffset1, bitOffset2) ) or @@ -214,24 +284,27 @@ private predicate isArgumentForParameter( ) } -private predicate isAlwaysReturnedArgument(Operand operand) { - exists(AliasModels::AliasFunction f | - f = operand.getUse().(CallInstruction).getStaticCallTarget() and - f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex()) - ) -} - private predicate isOnlyEscapesViaReturnArgument(Operand operand) { exists(AliasModels::AliasFunction f | f = operand.getUse().(CallInstruction).getStaticCallTarget() and - f.parameterEscapesOnlyViaReturn(operand.(PositionalArgumentOperand).getIndex()) + ( + f.parameterEscapesOnlyViaReturn(operand.(PositionalArgumentOperand).getIndex()) + or + f.parameterEscapesOnlyViaReturn(-1) and + operand instanceof ThisArgumentOperand + ) ) } private predicate isNeverEscapesArgument(Operand operand) { exists(AliasModels::AliasFunction f | f = operand.getUse().(CallInstruction).getStaticCallTarget() and - f.parameterNeverEscapes(operand.(PositionalArgumentOperand).getIndex()) + ( + f.parameterNeverEscapes(operand.(PositionalArgumentOperand).getIndex()) + or + f.parameterNeverEscapes(-1) and + operand instanceof ThisArgumentOperand + ) ) } @@ -265,17 +338,23 @@ predicate allocationEscapes(Configuration::Allocation allocation) { exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) ) + or + Configuration::phaseNeedsSoundEscapeAnalysis() and + resultEscapesNonReturn(allocation.getABaseInstruction()) } /** * Equivalent to `operandIsPropagated()`, but includes interprocedural propagation. */ -private predicate operandIsPropagatedIncludingByCall(Operand operand, IntValue bitOffset) { - operandIsPropagated(operand, bitOffset) +private predicate operandIsPropagatedIncludingByCall( + Operand operand, IntValue bitOffset, Instruction instr +) { + operandIsPropagated(operand, bitOffset, instr) or exists(CallInstruction call, Instruction init | isArgumentForParameter(call, operand, init) and - resultReturned(init, bitOffset) + resultReturned(init, bitOffset) and + instr = call ) } @@ -292,8 +371,7 @@ private predicate hasBaseAndOffset(AddressOperand addrOperand, Instruction base, // We already have an offset from `middle`. hasBaseAndOffset(addrOperand, middle, previousBitOffset) and // `middle` is propagated from `base`. - middleOperand = middle.getAnOperand() and - operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset) and + operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset, middle) and base = middleOperand.getDef() and bitOffset = Ints::add(previousBitOffset, additionalBitOffset) ) @@ -333,8 +411,51 @@ predicate addressOperandAllocationAndOffset( allocation.getABaseInstruction() = base and hasBaseAndOffset(addrOperand, base, bitOffset) and not exists(Instruction previousBase | - hasBaseAndOffset(addrOperand, previousBase, _) and + hasBaseAndOffset(addrOperand, pragma[only_bind_out](previousBase), _) and previousBase = base.getAnOperand().getDef() ) ) } + +/** + * Predicates used only for printing annotated IR dumps. These should not be used in production + * queries. + */ +module Print { + string getOperandProperty(Operand operand, string key) { + key = "alloc" and + result = + strictconcat(Configuration::Allocation allocation, IntValue bitOffset | + addressOperandAllocationAndOffset(operand, allocation, bitOffset) + | + allocation.toString() + Ints::getBitOffsetString(bitOffset), ", " + ) + or + key = "prop" and + result = + strictconcat(Instruction destInstr, IntValue bitOffset, string value | + operandIsPropagatedIncludingByCall(operand, bitOffset, destInstr) and + if destInstr = operand.getUse() + then value = "@" + Ints::getBitOffsetString(bitOffset) + "->result" + else value = "@" + Ints::getBitOffsetString(bitOffset) + "->" + destInstr.getResultId() + | + value, ", " + ) + } + + string getInstructionProperty(Instruction instr, string key) { + key = "prop" and + result = + strictconcat(IntValue bitOffset, Operand sourceOperand, string value | + operandIsPropagatedIncludingByCall(sourceOperand, bitOffset, instr) and + if instr = sourceOperand.getUse() + then value = sourceOperand.getDumpId() + Ints::getBitOffsetString(bitOffset) + "->@" + else + value = + sourceOperand.getUse().getResultId() + "." + sourceOperand.getDumpId() + + Ints::getBitOffsetString(bitOffset) + "->@" + | + value, ", " + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll index 69cd6e6dc29..8ba91d70087 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll @@ -2,9 +2,13 @@ private import AliasConfigurationInternal private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR private import cpp private import AliasAnalysis +private import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SimpleSSA as UnaliasedSSA private newtype TAllocation = - TVariableAllocation(IRVariable var) or + TVariableAllocation(IRVariable var) { + // Only model variables that were not already handled in unaliased SSA. + not UnaliasedSSA::canReuseSSAForVariable(var) + } or TIndirectParameterAllocation(IRAutomaticVariable var) { exists(InitializeIndirectionInstruction instr | instr.getIRVariable() = var) } or @@ -105,7 +109,21 @@ class DynamicAllocation extends Allocation, TDynamicAllocation { DynamicAllocation() { this = TDynamicAllocation(call) } final override string toString() { - result = call.toString() + " at " + call.getLocation() // This isn't performant, but it's only used in test/dump code right now. + // This isn't performant, but it's only used in test/dump code right now. + // Dynamic allocations within a function are numbered in the order by start + // line number. This keeps them stable when the function moves within the + // file, or when non-allocating lines are added and removed within the + // function. + exists(int i | + result = "dynamic{" + i.toString() + "}" and + call = + rank[i](CallInstruction rangeCall | + exists(TDynamicAllocation(rangeCall)) and + rangeCall.getEnclosingIRFunction() = call.getEnclosingIRFunction() + | + rangeCall order by rangeCall.getLocation().getStartLine() + ) + ) } final override CallInstruction getABaseInstruction() { result = call } @@ -124,3 +142,5 @@ class DynamicAllocation extends Allocation, TDynamicAllocation { final override predicate alwaysEscapes() { none() } } + +predicate phaseNeedsSoundEscapeAnalysis() { none() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll index fdabee2affe..acdae2b758a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll @@ -3,6 +3,7 @@ import semmle.code.cpp.ir.internal.Overlap private import semmle.code.cpp.ir.internal.IRCppLanguage as Language private import semmle.code.cpp.Print private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR +private import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction as OldSSA private import semmle.code.cpp.ir.internal.IntegerConstant as Ints private import semmle.code.cpp.ir.internal.IntegerInterval as Interval private import semmle.code.cpp.ir.implementation.internal.OperandTag @@ -131,6 +132,8 @@ abstract class MemoryLocation extends TMemoryLocation { * with automatic storage duration). */ predicate isAlwaysAllocatedOnStack() { none() } + + final predicate canReuseSSA() { none() } } /** @@ -562,10 +565,17 @@ private Overlap getVariableMemoryLocationOverlap( use.getEndBitOffset()) } +/** + * Holds if the def/use information for the result of `instr` can be reused from the previous + * iteration of the IR. + */ +predicate canReuseSSAForOldResult(Instruction instr) { OldSSA::canReuseSSAForMemoryResult(instr) } + bindingset[result, b] private boolean unbindBool(boolean b) { result != b.booleanNot() } MemoryLocation getResultMemoryLocation(Instruction instr) { + not canReuseSSAForOldResult(instr) and exists(MemoryAccessKind kind, boolean isMayAccess | kind = instr.getResultMemoryAccess() and (if instr.hasResultMayMemoryAccess() then isMayAccess = true else isMayAccess = false) and @@ -598,6 +608,7 @@ MemoryLocation getResultMemoryLocation(Instruction instr) { } MemoryLocation getOperandMemoryLocation(MemoryOperand operand) { + not canReuseSSAForOldResult(operand.getAnyDef()) and exists(MemoryAccessKind kind, boolean isMayAccess | kind = operand.getMemoryAccess() and (if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasAnalysis.qll new file mode 100644 index 00000000000..262088245e8 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintAliasAnalysis.qll @@ -0,0 +1,19 @@ +/** + * Include this module to annotate IR dumps with information computed by `AliasAnalysis.qll`. + */ + +private import AliasAnalysisInternal +private import InputIR +private import AliasAnalysisImports +private import AliasAnalysis +private import semmle.code.cpp.ir.internal.IntegerConstant + +private class AliasPropertyProvider extends IRPropertyProvider { + override string getOperandProperty(Operand operand, string key) { + result = Print::getOperandProperty(operand, key) + } + + override string getInstructionProperty(Instruction instr, string key) { + result = Print::getInstructionProperty(instr, key) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index 340f524fce8..5092e921cb3 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -43,24 +43,81 @@ private module Cached { class TStageInstruction = TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction; + /** + * If `oldInstruction` is a `Phi` instruction that has exactly one reachable predecessor block, + * this predicate returns the `PhiInputOperand` corresponding to that predecessor block. + * Otherwise, this predicate does not hold. + */ + private OldIR::PhiInputOperand getDegeneratePhiOperand(OldInstruction oldInstruction) { + result = + unique(OldIR::PhiInputOperand operand | + operand = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and + operand.getPredecessorBlock() instanceof OldBlock + ) + } + cached predicate hasInstruction(TStageInstruction instr) { instr instanceof TRawInstruction and instr instanceof OldInstruction or - instr instanceof TPhiInstruction + instr = phiInstruction(_, _) + or + instr = reusedPhiInstruction(_) and + // Check that the phi instruction is *not* degenerate, but we can't use + // getDegeneratePhiOperand in the first stage with phi instyructions + not exists( + unique(OldIR::PhiInputOperand operand | + operand = instr.(OldIR::PhiInstruction).getAnInputOperand() and + operand.getPredecessorBlock() instanceof OldBlock + ) + ) or instr instanceof TChiInstruction or instr instanceof TUnreachedInstruction } - private IRBlock getNewBlock(OldBlock oldBlock) { - result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction()) + cached + IRBlock getNewBlock(OldBlock oldBlock) { + exists(Instruction newEnd, OldIR::Instruction oldEnd | + ( + result.getLastInstruction() = newEnd and + not newEnd instanceof ChiInstruction + or + newEnd = result.getLastInstruction().(ChiInstruction).getAPredecessor() // does this work? + ) and + ( + oldBlock.getLastInstruction() = oldEnd and + not oldEnd instanceof OldIR::ChiInstruction + or + oldEnd = oldBlock.getLastInstruction().(OldIR::ChiInstruction).getAPredecessor() // does this work? + ) and + oldEnd = getNewInstruction(newEnd) + ) + } + + /** + * Gets the block from the old IR that corresponds to `newBlock`. + */ + private OldBlock getOldBlock(IRBlock newBlock) { getNewBlock(result) = newBlock } + + /** + * Holds if this iteration of SSA can model the def/use information for the result of + * `oldInstruction`, either because alias analysis has determined a memory location for that + * result, or because a previous iteration of the IR already computed that def/use information + * completely. + */ + private predicate canModelResultForOldInstruction(OldInstruction oldInstruction) { + // We're modeling the result's memory location ourselves. + exists(Alias::getResultMemoryLocation(oldInstruction)) + or + // This result was already modeled by a previous iteration of SSA. + Alias::canReuseSSAForOldResult(oldInstruction) } cached predicate hasModeledMemoryResult(Instruction instruction) { - exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or + canModelResultForOldInstruction(getOldInstruction(instruction)) or instruction instanceof PhiInstruction or // Phis always have modeled results instruction instanceof ChiInstruction // Chis always have modeled results } @@ -117,6 +174,32 @@ private module Cached { ) } + /** + * Gets the new definition instruction for `oldOperand` based on `oldOperand`'s definition in the + * old IR. Usually, this will just get the old definition of `oldOperand` and map it to the + * corresponding new instruction. However, if the old definition of `oldOperand` is a `Phi` + * instruction that is now degenerate due all but one of its predecessor branches being + * unreachable, this predicate will recurse through any degenerate `Phi` instructions to find the + * true definition. + */ + private Instruction getNewDefinitionFromOldSSA(OldIR::MemoryOperand oldOperand, Overlap overlap) { + exists(Overlap originalOverlap | + originalOverlap = oldOperand.getDefinitionOverlap() and + ( + result = getNewInstruction(oldOperand.getAnyDef()) and + overlap = originalOverlap + or + exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap | + phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and + result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and + overlap = + combineOverlap(pragma[only_bind_out](phiOperandOverlap), + pragma[only_bind_out](originalOverlap)) + ) + ) + ) + } + cached private Instruction getMemoryOperandDefinition0( Instruction instruction, MemoryOperandTag tag, Overlap overlap @@ -148,6 +231,12 @@ private module Cached { overlap instanceof MustExactlyOverlap and exists(MustTotallyOverlap o | exists(getMemoryOperandDefinition0(instruction, tag, o))) ) + or + exists(OldIR::NonPhiMemoryOperand oldOperand | + result = getNewDefinitionFromOldSSA(oldOperand, overlap) and + oldOperand.getUse() = instruction and + tag = oldOperand.getOperandTag() + ) } /** @@ -214,10 +303,24 @@ private module Cached { ) } + /** + * Gets the new definition instruction for the operand of `instr` that flows from the block + * `newPredecessorBlock`, based on that operand's definition in the old IR. + */ + private Instruction getNewPhiOperandDefinitionFromOldSSA( + Instruction instr, IRBlock newPredecessorBlock, Overlap overlap + ) { + exists(OldIR::PhiInstruction oldPhi, OldIR::PhiInputOperand oldOperand | + oldPhi = getOldInstruction(instr) and + oldOperand = oldPhi.getInputOperand(getOldBlock(newPredecessorBlock)) and + result = getNewDefinitionFromOldSSA(oldOperand, overlap) + ) + } + pragma[noopt] cached Instruction getPhiOperandDefinition( - PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap + Instruction instr, IRBlock newPredecessorBlock, Overlap overlap ) { exists( Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock, @@ -229,6 +332,8 @@ private module Cached { result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and overlap = Alias::getOverlap(actualDefLocation, useLocation) ) + or + result = getNewPhiOperandDefinitionFromOldSSA(instr, newPredecessorBlock, overlap) } cached @@ -249,7 +354,12 @@ private module Cached { cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) { exists(OldBlock oldBlock | - instr = getPhi(oldBlock, _) and + ( + instr = getPhi(oldBlock, _) + or + // Any `Phi` that we propagated from the previous iteration stays in the same block. + getOldInstruction(instr).getBlock() = oldBlock + ) and result = getNewInstruction(oldBlock.getFirstInstruction()) ) } @@ -335,18 +445,27 @@ private module Cached { result = vvar.getType() ) or + instr = reusedPhiInstruction(_) and + result = instr.(OldInstruction).getResultLanguageType() + or instr = unreachedInstruction(_) and result = Language::getVoidType() } + /** + * Holds if `opcode` is the opcode that specifies the operation performed by `instr`. + * + * The parameters are ordered such that they produce a clean join (with no need for reordering) + * in the characteristic predicates of the `Instruction` subclasses. + */ cached - Opcode getInstructionOpcode(Instruction instr) { - result = getOldInstruction(instr).getOpcode() + predicate getInstructionOpcode(Opcode opcode, Instruction instr) { + opcode = getOldInstruction(instr).getOpcode() or - instr = phiInstruction(_, _) and result instanceof Opcode::Phi + instr = phiInstruction(_, _) and opcode instanceof Opcode::Phi or - instr = chiInstruction(_) and result instanceof Opcode::Chi + instr = chiInstruction(_) and opcode instanceof Opcode::Chi or - instr = unreachedInstruction(_) and result instanceof Opcode::Unreached + instr = unreachedInstruction(_) and opcode instanceof Opcode::Unreached } cached @@ -856,6 +975,26 @@ module DefUse { } } +predicate canReuseSSAForMemoryResult(Instruction instruction) { + exists(OldInstruction oldInstruction | + oldInstruction = getOldInstruction(instruction) and + ( + // The previous iteration said it was reusable, so we should mark it as reusable as well. + Alias::canReuseSSAForOldResult(oldInstruction) + or + // The current alias analysis says it is reusable. + Alias::getResultMemoryLocation(oldInstruction).canReuseSSA() + ) + ) + or + exists(Alias::MemoryLocation defLocation | + // This is a `Phi` for a reusable location, so the result of the `Phi` is reusable as well. + instruction = phiInstruction(_, defLocation) and + defLocation.canReuseSSA() + ) + // We don't support reusing SSA for any location that could create a `Chi` instruction. +} + /** * Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the * `DebugSSA` module, which is then imported by PrintSSA. @@ -934,7 +1073,10 @@ module SSAConsistency { locationCount > 1 and func = operand.getEnclosingIRFunction() and funcText = Language::getIdentityString(func.getFunction()) and - message = "Operand has " + locationCount.toString() + " memory accesses in function '$@'." + message = + operand.getUse().toString() + " " + "Operand has " + locationCount.toString() + + " memory accesses in function '$@': " + + strictconcat(Alias::getOperandMemoryLocation(operand).toString(), ", ") ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll index e16b71733b5..4b3f19cbdde 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll @@ -55,6 +55,8 @@ module UnaliasedSSAInstructions { result = TUnaliasedSSAPhiInstruction(blockStartInstr, memoryLocation) } + TRawInstruction reusedPhiInstruction(TRawInstruction blockStartInstr) { none() } + class TChiInstruction = TUnaliasedSSAChiInstruction; TChiInstruction chiInstruction(TRawInstruction primaryInstruction) { @@ -75,7 +77,7 @@ module UnaliasedSSAInstructions { * a class alias. */ module AliasedSSAInstructions { - class TPhiInstruction = TAliasedSSAPhiInstruction; + class TPhiInstruction = TAliasedSSAPhiInstruction or TUnaliasedSSAPhiInstruction; TPhiInstruction phiInstruction( TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation @@ -83,6 +85,10 @@ module AliasedSSAInstructions { result = TAliasedSSAPhiInstruction(blockStartInstr, memoryLocation) } + TPhiInstruction reusedPhiInstruction(TRawInstruction blockStartInstr) { + result = TUnaliasedSSAPhiInstruction(blockStartInstr, _) + } + class TChiInstruction = TAliasedSSAChiInstruction; TChiInstruction chiInstruction(TRawInstruction primaryInstruction) { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll index 1e132463cdd..e86494af03a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TOperand.qll @@ -36,10 +36,9 @@ private module Internal { useInstr.getOpcode().hasOperand(tag) } or TUnaliasedPhiOperand( - Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr, - Unaliased::IRBlock predecessorBlock, Overlap overlap + Unaliased::PhiInstruction useInstr, Unaliased::IRBlock predecessorBlock, Overlap overlap ) { - defInstr = UnaliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap) + exists(UnaliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)) } or //// ALIASED //// @@ -50,10 +49,9 @@ private module Internal { // important that we use the same definition of "is variable aliased" across // the phases. TAliasedPhiOperand( - TAliasedSSAPhiInstruction useInstr, Aliased::Instruction defInstr, - Aliased::IRBlock predecessorBlock, Overlap overlap + TAliasedSSAPhiInstruction useInstr, Aliased::IRBlock predecessorBlock, Overlap overlap ) { - defInstr = AliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap) + exists(AliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)) } or TAliasedChiOperand(TAliasedSSAChiInstruction useInstr, ChiOperandTag tag) { any() } } @@ -109,6 +107,13 @@ module RawOperands { none() } + TPhiOperand reusedPhiOperand( + Raw::PhiInstruction useInstr, Raw::Instruction defInstr, Raw::IRBlock predecessorBlock, + Overlap overlap + ) { + none() + } + /** * Returns the Chi operand with the specified parameters. */ @@ -137,7 +142,15 @@ module UnaliasedSSAOperands { Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr, Unaliased::IRBlock predecessorBlock, Overlap overlap ) { - result = Internal::TUnaliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap) + defInstr = UnaliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap) and + result = Internal::TUnaliasedPhiOperand(useInstr, predecessorBlock, overlap) + } + + TPhiOperand reusedPhiOperand( + Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr, + Unaliased::IRBlock predecessorBlock, Overlap overlap + ) { + none() } /** @@ -155,7 +168,7 @@ module UnaliasedSSAOperands { module AliasedSSAOperands { import Shared - class TPhiOperand = Internal::TAliasedPhiOperand; + class TPhiOperand = Internal::TAliasedPhiOperand or Internal::TUnaliasedPhiOperand; class TChiOperand = Internal::TAliasedChiOperand; @@ -165,10 +178,25 @@ module AliasedSSAOperands { * Returns the Phi operand with the specified parameters. */ TPhiOperand phiOperand( - TAliasedSSAPhiInstruction useInstr, Aliased::Instruction defInstr, + Aliased::PhiInstruction useInstr, Aliased::Instruction defInstr, Aliased::IRBlock predecessorBlock, Overlap overlap ) { - result = Internal::TAliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap) + defInstr = AliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap) and + result = Internal::TAliasedPhiOperand(useInstr, predecessorBlock, overlap) + } + + /** + * Returns the Phi operand with the specified parameters. + */ + TPhiOperand reusedPhiOperand( + Aliased::PhiInstruction useInstr, Aliased::Instruction defInstr, + Aliased::IRBlock predecessorBlock, Overlap overlap + ) { + exists(Unaliased::IRBlock oldBlock | + predecessorBlock = AliasedConstruction::getNewBlock(oldBlock) and + result = Internal::TUnaliasedPhiOperand(useInstr, oldBlock, _) and + defInstr = AliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap) + ) } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index af6f3d81982..453838215ff 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -297,7 +297,8 @@ class Instruction extends Construction::TStageInstruction { /** * Gets the opcode that specifies the operation performed by this instruction. */ - final Opcode getOpcode() { result = Construction::getInstructionOpcode(this) } + pragma[inline] + final Opcode getOpcode() { Construction::getInstructionOpcode(result, this) } /** * Gets all direct uses of the result of this instruction. The result can be @@ -1645,6 +1646,19 @@ class CallInstruction extends Instruction { * Gets the number of arguments of the call, including the `this` pointer, if any. */ final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) } + + /** + * Holds if the result is a side effect for the argument at the specified index, or `this` if + * `index` is `-1`. + * + * This helper predicate makes it easy to join on both of these columns at once, avoiding + * pathological join orders in case the argument index should get joined first. + */ + pragma[noinline] + final SideEffectInstruction getAParameterSideEffect(int index) { + this = result.getPrimaryInstruction() and + index = result.(IndexedInstruction).getIndex() + } } /** @@ -1980,6 +1994,14 @@ class PhiInstruction extends Instruction { */ pragma[noinline] final Instruction getAnInput() { result = this.getAnInputOperand().getDef() } + + /** + * Gets the input operand representing the value that flows from the specified predecessor block. + */ + final PhiInputOperand getInputOperand(IRBlock predecessorBlock) { + result = this.getAnOperand() and + result.getPredecessorBlock() = predecessorBlock + } } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll index a2ce0662dc2..d7cf89ca9aa 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll @@ -28,11 +28,15 @@ class Operand extends TStageOperand { cached Operand() { // Ensure that the operand does not refer to instructions from earlier stages that are unreachable here - exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or - exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or + exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) + or + exists(Instruction use | this = nonSSAMemoryOperand(use, _)) + or exists(Instruction use, Instruction def, IRBlock predecessorBlock | - this = phiOperand(use, def, predecessorBlock, _) - ) or + this = phiOperand(use, def, predecessorBlock, _) or + this = reusedPhiOperand(use, def, predecessorBlock, _) + ) + or exists(Instruction use | this = chiOperand(use, _)) } @@ -431,7 +435,11 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand { Overlap overlap; cached - PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) } + PhiInputOperand() { + this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) + or + this = reusedPhiOperand(useInstr, defInstr, predecessorBlock, overlap) + } override string toString() { result = "Phi" } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll index b0faec547f3..e8fcf3fcdf3 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll @@ -360,8 +360,8 @@ CppType getInstructionResultType(TStageInstruction instr) { getInstructionTranslatedElement(instr).hasInstruction(_, getInstructionTag(instr), result) } -Opcode getInstructionOpcode(TStageInstruction instr) { - getInstructionTranslatedElement(instr).hasInstruction(result, getInstructionTag(instr), _) +predicate getInstructionOpcode(Opcode opcode, TStageInstruction instr) { + getInstructionTranslatedElement(instr).hasInstruction(opcode, getInstructionTag(instr), _) } IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll new file mode 100644 index 00000000000..50245fafde2 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll @@ -0,0 +1,169 @@ +/** + * Predicates to compute the modeled side effects of calls during IR construction. + * + * These are used in `TranslatedElement.qll` to generate the `TTranslatedSideEffect` instances, and + * also in `TranslatedCall.qll` to inject the actual side effect instructions. + */ + +private import cpp +private import semmle.code.cpp.ir.implementation.Opcode +private import semmle.code.cpp.models.interfaces.PointerWrapper +private import semmle.code.cpp.models.interfaces.SideEffect + +private predicate isDeeplyConst(Type t) { + t.isConst() and + isDeeplyConstBelow(t) + or + isDeeplyConst(t.(Decltype).getBaseType()) + or + isDeeplyConst(t.(ReferenceType).getBaseType()) + or + exists(SpecifiedType specType | specType = t | + specType.getASpecifier().getName() = "const" and + isDeeplyConstBelow(specType.getBaseType()) + ) + or + isDeeplyConst(t.(ArrayType).getBaseType()) +} + +private predicate isDeeplyConstBelow(Type t) { + t instanceof BuiltInType + or + not t instanceof PointerWrapper and + t instanceof Class + or + t instanceof Enum + or + isDeeplyConstBelow(t.(Decltype).getBaseType()) + or + isDeeplyConst(t.(PointerType).getBaseType()) + or + isDeeplyConst(t.(ReferenceType).getBaseType()) + or + isDeeplyConstBelow(t.(SpecifiedType).getBaseType()) + or + isDeeplyConst(t.(ArrayType).getBaseType()) + or + isDeeplyConst(t.(GNUVectorType).getBaseType()) + or + isDeeplyConst(t.(FunctionPointerIshType).getBaseType()) + or + isDeeplyConst(t.(PointerWrapper).getTemplateArgument(0)) + or + isDeeplyConst(t.(PointerToMemberType).getBaseType()) + or + isDeeplyConstBelow(t.(TypedefType).getBaseType()) +} + +private predicate isConstPointerLike(Type t) { + ( + t instanceof PointerWrapper + or + t instanceof PointerType + or + t instanceof ArrayType + or + t instanceof ReferenceType + ) and + isDeeplyConstBelow(t) +} + +/** + * Holds if the specified call has a side effect that does not come from a `SideEffectFunction` + * model. + */ +private predicate hasDefaultSideEffect(Call call, ParameterIndex i, boolean buffer, boolean isWrite) { + not call.getTarget() instanceof SideEffectFunction and + ( + exists(MemberFunction mfunc | + // A non-static member function, including a constructor or destructor, may write to `*this`, + // and may also read from `*this` if it is not a constructor. + i = -1 and + mfunc = call.getTarget() and + not mfunc.isStatic() and + buffer = false and + ( + isWrite = false and not mfunc instanceof Constructor + or + isWrite = true and not mfunc instanceof ConstMemberFunction + ) + ) + or + exists(Expr expr | + // A pointer-like argument is assumed to read from the pointed-to buffer, and may write to the + // buffer as well unless the pointer points to a `const` value. + i >= 0 and + buffer = true and + expr = call.getArgument(i).getFullyConverted() and + exists(Type t | t = expr.getUnspecifiedType() | + t instanceof ArrayType or + t instanceof PointerType or + t instanceof ReferenceType or + t instanceof PointerWrapper + ) and + ( + isWrite = true and + not isConstPointerLike(call.getTarget().getParameter(i).getUnderlyingType()) + or + isWrite = false + ) + ) + ) +} + +/** + * Returns a side effect opcode for parameter index `i` of the specified call. + * + * This predicate will return at most two results: one read side effect, and one write side effect. + */ +Opcode getASideEffectOpcode(Call call, ParameterIndex i) { + exists(boolean buffer | + ( + call.getTarget().(SideEffectFunction).hasSpecificReadSideEffect(i, buffer) + or + not call.getTarget() instanceof SideEffectFunction and + hasDefaultSideEffect(call, i, buffer, false) + ) and + if exists(call.getTarget().(SideEffectFunction).getParameterSizeIndex(i)) + then ( + buffer = true and + result instanceof Opcode::SizedBufferReadSideEffect + ) else ( + buffer = false and result instanceof Opcode::IndirectReadSideEffect + or + buffer = true and result instanceof Opcode::BufferReadSideEffect + ) + ) + or + exists(boolean buffer, boolean mustWrite | + ( + call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(i, buffer, mustWrite) + or + not call.getTarget() instanceof SideEffectFunction and + hasDefaultSideEffect(call, i, buffer, true) and + mustWrite = false + ) and + if exists(call.getTarget().(SideEffectFunction).getParameterSizeIndex(i)) + then ( + buffer = true and + mustWrite = false and + result instanceof Opcode::SizedBufferMayWriteSideEffect + or + buffer = true and + mustWrite = true and + result instanceof Opcode::SizedBufferMustWriteSideEffect + ) else ( + buffer = false and + mustWrite = false and + result instanceof Opcode::IndirectMayWriteSideEffect + or + buffer = false and + mustWrite = true and + result instanceof Opcode::IndirectMustWriteSideEffect + or + buffer = true and mustWrite = false and result instanceof Opcode::BufferMayWriteSideEffect + or + buffer = true and mustWrite = true and result instanceof Opcode::BufferMustWriteSideEffect + ) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll index 7ad1dd2c01e..56d4c807ac8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll @@ -4,6 +4,7 @@ private import semmle.code.cpp.ir.implementation.internal.OperandTag private import semmle.code.cpp.ir.internal.CppType private import semmle.code.cpp.models.interfaces.SideEffect private import InstructionTag +private import SideEffects private import TranslatedElement private import TranslatedExpr private import TranslatedFunction @@ -424,12 +425,15 @@ class TranslatedCallSideEffects extends TranslatedSideEffects, TTranslatedCallSi } class TranslatedStructorCallSideEffects extends TranslatedCallSideEffects { - TranslatedStructorCallSideEffects() { getParent().(TranslatedStructorCall).hasQualifier() } + TranslatedStructorCallSideEffects() { + getParent().(TranslatedStructorCall).hasQualifier() and + getASideEffectOpcode(expr, -1) instanceof WriteSideEffectOpcode + } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType t) { - opcode instanceof Opcode::IndirectMayWriteSideEffect and tag instanceof OnlyInstructionTag and - t = getTypeForPRValue(expr.getTarget().getDeclaringType()) + t = getTypeForPRValue(expr.getTarget().getDeclaringType()) and + opcode = getASideEffectOpcode(expr, -1).(WriteSideEffectOpcode) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -460,9 +464,11 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff Call call; Expr arg; int index; - boolean write; + SideEffectOpcode sideEffectOpcode; - TranslatedSideEffect() { this = TTranslatedArgumentSideEffect(call, arg, index, write) } + TranslatedSideEffect() { + this = TTranslatedArgumentSideEffect(call, arg, index, sideEffectOpcode) + } override Locatable getAST() { result = arg } @@ -472,13 +478,13 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff int getArgumentIndex() { result = index } - predicate isWrite() { write = true } + predicate isWrite() { sideEffectOpcode instanceof WriteSideEffectOpcode } override string toString() { - write = true and + isWrite() and result = "(write side effect for " + arg.toString() + ")" or - write = false and + not isWrite() and result = "(read side effect for " + arg.toString() + ")" } @@ -489,29 +495,29 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) } override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) { - isWrite() and - hasSpecificWriteSideEffect(opcode) and tag = OnlyInstructionTag() and + opcode = sideEffectOpcode and ( - opcode instanceof BufferAccessOpcode and - type = getUnknownType() - or - not opcode instanceof BufferAccessOpcode and - exists(Type baseType | baseType = arg.getUnspecifiedType().(DerivedType).getBaseType() | - if baseType instanceof VoidType - then type = getUnknownType() - else type = getTypeForPRValueOrUnknown(baseType) + isWrite() and + ( + opcode instanceof BufferAccessOpcode and + type = getUnknownType() + or + not opcode instanceof BufferAccessOpcode and + exists(Type baseType | baseType = arg.getUnspecifiedType().(DerivedType).getBaseType() | + if baseType instanceof VoidType + then type = getUnknownType() + else type = getTypeForPRValueOrUnknown(baseType) + ) + or + index = -1 and + not arg.getUnspecifiedType() instanceof DerivedType and + type = getTypeForPRValueOrUnknown(arg.getUnspecifiedType()) ) or - index = -1 and - not arg.getUnspecifiedType() instanceof DerivedType and - type = getTypeForPRValueOrUnknown(arg.getUnspecifiedType()) + not isWrite() and + type = getVoidType() ) - or - not isWrite() and - hasSpecificReadSideEffect(opcode) and - tag = OnlyInstructionTag() and - type = getVoidType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -535,7 +541,7 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff override CppType getInstructionMemoryOperandType(InstructionTag tag, TypedOperandTag operandTag) { not isWrite() and - if hasSpecificReadSideEffect(any(BufferAccessOpcode op)) + if sideEffectOpcode instanceof BufferAccessOpcode then result = getUnknownType() and tag instanceof OnlyInstructionTag and @@ -557,56 +563,6 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff ) } - predicate hasSpecificWriteSideEffect(Opcode op) { - exists(boolean buffer, boolean mustWrite | - if exists(call.getTarget().(SideEffectFunction).getParameterSizeIndex(index)) - then - call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(index, true, mustWrite) and - buffer = true and - ( - mustWrite = false and op instanceof Opcode::SizedBufferMayWriteSideEffect - or - mustWrite = true and op instanceof Opcode::SizedBufferMustWriteSideEffect - ) - else ( - call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(index, buffer, mustWrite) and - ( - buffer = true and mustWrite = false and op instanceof Opcode::BufferMayWriteSideEffect - or - buffer = false and mustWrite = false and op instanceof Opcode::IndirectMayWriteSideEffect - or - buffer = true and mustWrite = true and op instanceof Opcode::BufferMustWriteSideEffect - or - buffer = false and mustWrite = true and op instanceof Opcode::IndirectMustWriteSideEffect - ) - ) - ) - or - not call.getTarget() instanceof SideEffectFunction and - getArgumentIndex() != -1 and - op instanceof Opcode::BufferMayWriteSideEffect - or - not call.getTarget() instanceof SideEffectFunction and - getArgumentIndex() = -1 and - op instanceof Opcode::IndirectMayWriteSideEffect - } - - predicate hasSpecificReadSideEffect(Opcode op) { - exists(boolean buffer | - call.getTarget().(SideEffectFunction).hasSpecificReadSideEffect(index, buffer) and - if exists(call.getTarget().(SideEffectFunction).getParameterSizeIndex(index)) - then buffer = true and op instanceof Opcode::SizedBufferReadSideEffect - else ( - buffer = true and op instanceof Opcode::BufferReadSideEffect - or - buffer = false and op instanceof Opcode::IndirectReadSideEffect - ) - ) - or - not call.getTarget() instanceof SideEffectFunction and - op instanceof Opcode::BufferReadSideEffect - } - override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) { tag = OnlyInstructionTag() and result = getTranslatedCallInstruction(call) 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 1de9936ae1f..81c69cf0ea2 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 @@ -12,6 +12,7 @@ private import TranslatedStmt private import TranslatedExpr private import IRConstruction private import semmle.code.cpp.models.interfaces.SideEffect +private import SideEffects /** * Gets the "real" parent of `expr`. This predicate treats conversions as if @@ -41,7 +42,8 @@ IRTempVariable getIRTempVariable(Locatable ast, TempVariableTag tag) { */ predicate isIRConstant(Expr expr) { exists(expr.getValue()) } -// Pulled out to work around QL-796 +// Pulled out for performance. See +// https://github.com/github/codeql-coreql-team/issues/1044. private predicate isOrphan(Expr expr) { not exists(getRealParent(expr)) } /** @@ -635,46 +637,15 @@ newtype TTranslatedElement = // The side effects of an allocation, i.e. `new`, `new[]` or `malloc` TTranslatedAllocationSideEffects(AllocationExpr expr) { not ignoreExpr(expr) } or // A precise side effect of an argument to a `Call` - TTranslatedArgumentSideEffect(Call call, Expr expr, int n, boolean isWrite) { - ( - expr = call.getArgument(n).getFullyConverted() - or - expr = call.getQualifier().getFullyConverted() and - n = -1 and - // Exclude calls to static member functions. They don't modify the qualifier - not exists(MemberFunction func | func = call.getTarget() and func.isStatic()) - ) and - ( - call.getTarget().(SideEffectFunction).hasSpecificReadSideEffect(n, _) and - isWrite = false - or - call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(n, _, _) and - isWrite = true - or - not call.getTarget() instanceof SideEffectFunction and - exists(Type t | t = expr.getUnspecifiedType() | - t instanceof ArrayType or - t instanceof PointerType or - t instanceof ReferenceType - ) and - ( - 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 and - not call.getTarget() instanceof ConstMemberFunction - or - isWrite = false - ) - ) and + TTranslatedArgumentSideEffect(Call call, Expr expr, int n, SideEffectOpcode opcode) { not ignoreExpr(expr) and - not ignoreExpr(call) + not ignoreExpr(call) and + ( + n >= 0 and expr = call.getArgument(n).getFullyConverted() + or + n = -1 and expr = call.getQualifier().getFullyConverted() + ) and + opcode = getASideEffectOpcode(call, n) } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll index f55d661b202..2a0b58ce96a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll @@ -725,9 +725,9 @@ abstract class TranslatedReadEffect extends TranslatedElement { override Instruction getChildSuccessor(TranslatedElement child) { none() } - override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind edge) { + override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = OnlyInstructionTag() and - edge = EdgeKind::gotoEdge() and + kind = EdgeKind::gotoEdge() and result = getParent().getChildSuccessor(this) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index af6f3d81982..453838215ff 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -297,7 +297,8 @@ class Instruction extends Construction::TStageInstruction { /** * Gets the opcode that specifies the operation performed by this instruction. */ - final Opcode getOpcode() { result = Construction::getInstructionOpcode(this) } + pragma[inline] + final Opcode getOpcode() { Construction::getInstructionOpcode(result, this) } /** * Gets all direct uses of the result of this instruction. The result can be @@ -1645,6 +1646,19 @@ class CallInstruction extends Instruction { * Gets the number of arguments of the call, including the `this` pointer, if any. */ final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) } + + /** + * Holds if the result is a side effect for the argument at the specified index, or `this` if + * `index` is `-1`. + * + * This helper predicate makes it easy to join on both of these columns at once, avoiding + * pathological join orders in case the argument index should get joined first. + */ + pragma[noinline] + final SideEffectInstruction getAParameterSideEffect(int index) { + this = result.getPrimaryInstruction() and + index = result.(IndexedInstruction).getIndex() + } } /** @@ -1980,6 +1994,14 @@ class PhiInstruction extends Instruction { */ pragma[noinline] final Instruction getAnInput() { result = this.getAnInputOperand().getDef() } + + /** + * Gets the input operand representing the value that flows from the specified predecessor block. + */ + final PhiInputOperand getInputOperand(IRBlock predecessorBlock) { + result = this.getAnOperand() and + result.getPredecessorBlock() = predecessorBlock + } } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll index a2ce0662dc2..d7cf89ca9aa 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll @@ -28,11 +28,15 @@ class Operand extends TStageOperand { cached Operand() { // Ensure that the operand does not refer to instructions from earlier stages that are unreachable here - exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or - exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or + exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) + or + exists(Instruction use | this = nonSSAMemoryOperand(use, _)) + or exists(Instruction use, Instruction def, IRBlock predecessorBlock | - this = phiOperand(use, def, predecessorBlock, _) - ) or + this = phiOperand(use, def, predecessorBlock, _) or + this = reusedPhiOperand(use, def, predecessorBlock, _) + ) + or exists(Instruction use | this = chiOperand(use, _)) } @@ -431,7 +435,11 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand { Overlap overlap; cached - PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) } + PhiInputOperand() { + this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) + or + this = reusedPhiOperand(useInstr, defInstr, predecessorBlock, overlap) + } override string toString() { result = "Phi" } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index 19fb0490f80..9997b5b49a7 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -4,6 +4,71 @@ private import AliasAnalysisImports private class IntValue = Ints::IntValue; +/** + * If `instr` is a `SideEffectInstruction`, gets the primary `CallInstruction` that caused the side + * effect. If `instr` is a `CallInstruction`, gets that same `CallInstruction`. + */ +private CallInstruction getPrimaryCall(Instruction instr) { + result = instr + or + result = instr.(SideEffectInstruction).getPrimaryInstruction() +} + +/** + * Holds if `operand` serves as an input argument (or indirection) to `call`, in the position + * specified by `input`. + */ +private predicate isCallInput( + CallInstruction call, Operand operand, AliasModels::FunctionInput input +) { + call = getPrimaryCall(operand.getUse()) and + ( + exists(int index | + input.isParameterOrQualifierAddress(index) and + operand = call.getArgumentOperand(index) + ) + or + exists(int index, ReadSideEffectInstruction read | + input.isParameterDerefOrQualifierObject(index) and + read = call.getAParameterSideEffect(index) and + operand = read.getSideEffectOperand() + ) + ) +} + +/** + * Holds if `instr` serves as a return value or output argument indirection for `call`, in the + * position specified by `output`. + */ +private predicate isCallOutput( + CallInstruction call, Instruction instr, AliasModels::FunctionOutput output +) { + call = getPrimaryCall(instr) and + ( + output.isReturnValue() and instr = call + or + exists(int index, WriteSideEffectInstruction write | + output.isParameterDerefOrQualifierObject(index) and + write = call.getAParameterSideEffect(index) and + instr = write + ) + ) +} + +/** + * Holds if the address in `operand` flows directly to the result of `resultInstr` due to modeled + * address flow through a function call. + */ +private predicate hasAddressFlowThroughCall(Operand operand, Instruction resultInstr) { + exists( + CallInstruction call, AliasModels::FunctionInput input, AliasModels::FunctionOutput output + | + call.getStaticCallTarget().(AliasModels::AliasFunction).hasAddressFlow(input, output) and + isCallInput(call, operand, input) and + isCallOutput(call, resultInstr, output) + ) +} + /** * Holds if the operand `tag` of instruction `instr` is used in a way that does * not result in any address held in that operand from escaping beyond the @@ -25,6 +90,9 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) { or // Converting an address to a `bool` does not escape the address. instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType + or + instr instanceof CallInstruction and + not exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis()) ) ) or @@ -34,7 +102,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) { private predicate operandEscapesDomain(Operand operand) { not operandIsConsumedWithoutEscaping(operand) and - not operandIsPropagated(operand, _) and + not operandIsPropagated(operand, _, _) and not isArgumentForParameter(_, operand, _) and not isOnlyEscapesViaReturnArgument(operand) and not operand.getUse() instanceof ReturnValueInstruction and @@ -69,67 +137,67 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) { } /** - * Holds if any address held in operand `tag` of instruction `instr` is - * propagated to the result of `instr`, offset by the number of bits in - * `bitOffset`. If the address is propagated, but the offset is not known to be - * a constant, then `bitOffset` is unknown. + * Holds if any address held in operand `operand` is propagated to the result of `instr`, offset by + * the number of bits in `bitOffset`. If the address is propagated, but the offset is not known to + * be a constant, then `bitOffset` is `unknown()`. */ -private predicate operandIsPropagated(Operand operand, IntValue bitOffset) { - exists(Instruction instr | - instr = operand.getUse() and - ( - // Converting to a non-virtual base class adds the offset of the base class. - exists(ConvertToNonVirtualBaseInstruction convert | - convert = instr and - bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8) - ) - or - // Conversion using dynamic_cast results in an unknown offset - instr instanceof CheckedConvertOrNullInstruction and - bitOffset = Ints::unknown() - or - // Converting to a derived class subtracts the offset of the base class. - exists(ConvertToDerivedInstruction convert | - convert = instr and - bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8)) - ) - or - // Converting to a virtual base class adds an unknown offset. - instr instanceof ConvertToVirtualBaseInstruction and - bitOffset = Ints::unknown() - or - // Conversion to another pointer type propagates the source address. - exists(ConvertInstruction convert, IRType resultType | - convert = instr and - resultType = convert.getResultIRType() and - resultType instanceof IRAddressType and - bitOffset = 0 - ) - or - // Adding an integer to or subtracting an integer from a pointer propagates - // the address with an offset. - exists(PointerOffsetInstruction ptrOffset | - ptrOffset = instr and - operand = ptrOffset.getLeftOperand() and - bitOffset = getPointerBitOffset(ptrOffset) - ) - or - // Computing a field address from a pointer propagates the address plus the - // offset of the field. - bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField()) - or - // A copy propagates the source value. - operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 - or - // Some functions are known to propagate an argument - isAlwaysReturnedArgument(operand) and bitOffset = 0 +private predicate operandIsPropagated(Operand operand, IntValue bitOffset, Instruction instr) { + // Some functions are known to propagate an argument + hasAddressFlowThroughCall(operand, instr) and + bitOffset = 0 + or + instr = operand.getUse() and + ( + // Converting to a non-virtual base class adds the offset of the base class. + exists(ConvertToNonVirtualBaseInstruction convert | + convert = instr and + bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8) ) + or + // Conversion using dynamic_cast results in an unknown offset + instr instanceof CheckedConvertOrNullInstruction and + bitOffset = Ints::unknown() + or + // Converting to a derived class subtracts the offset of the base class. + exists(ConvertToDerivedInstruction convert | + convert = instr and + bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8)) + ) + or + // Converting to a virtual base class adds an unknown offset. + instr instanceof ConvertToVirtualBaseInstruction and + bitOffset = Ints::unknown() + or + // Conversion to another pointer type propagates the source address. + exists(ConvertInstruction convert, IRType resultType | + convert = instr and + resultType = convert.getResultIRType() and + resultType instanceof IRAddressType and + bitOffset = 0 + ) + or + // Adding an integer to or subtracting an integer from a pointer propagates + // the address with an offset. + exists(PointerOffsetInstruction ptrOffset | + ptrOffset = instr and + operand = ptrOffset.getLeftOperand() and + bitOffset = getPointerBitOffset(ptrOffset) + ) + or + // Computing a field address from a pointer propagates the address plus the + // offset of the field. + bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField()) + or + // A copy propagates the source value. + operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 ) } private predicate operandEscapesNonReturn(Operand operand) { - // The address is propagated to the result of the instruction, and that result itself is returned - operandIsPropagated(operand, _) and resultEscapesNonReturn(operand.getUse()) + exists(Instruction instr | + // The address is propagated to the result of the instruction, and that result itself is returned + operandIsPropagated(operand, _, instr) and resultEscapesNonReturn(instr) + ) or // The operand is used in a function call which returns it, and the return value is then returned exists(CallInstruction ci, Instruction init | @@ -151,9 +219,11 @@ private predicate operandEscapesNonReturn(Operand operand) { } private predicate operandMayReachReturn(Operand operand) { - // The address is propagated to the result of the instruction, and that result itself is returned - operandIsPropagated(operand, _) and - resultMayReachReturn(operand.getUse()) + exists(Instruction instr | + // The address is propagated to the result of the instruction, and that result itself is returned + operandIsPropagated(operand, _, instr) and + resultMayReachReturn(instr) + ) or // The operand is used in a function call which returns it, and the return value is then returned exists(CallInstruction ci, Instruction init | @@ -173,9 +243,9 @@ private predicate operandMayReachReturn(Operand operand) { private predicate operandReturned(Operand operand, IntValue bitOffset) { // The address is propagated to the result of the instruction, and that result itself is returned - exists(IntValue bitOffset1, IntValue bitOffset2 | - operandIsPropagated(operand, bitOffset1) and - resultReturned(operand.getUse(), bitOffset2) and + exists(Instruction instr, IntValue bitOffset1, IntValue bitOffset2 | + operandIsPropagated(operand, bitOffset1, instr) and + resultReturned(instr, bitOffset2) and bitOffset = Ints::add(bitOffset1, bitOffset2) ) or @@ -214,24 +284,27 @@ private predicate isArgumentForParameter( ) } -private predicate isAlwaysReturnedArgument(Operand operand) { - exists(AliasModels::AliasFunction f | - f = operand.getUse().(CallInstruction).getStaticCallTarget() and - f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex()) - ) -} - private predicate isOnlyEscapesViaReturnArgument(Operand operand) { exists(AliasModels::AliasFunction f | f = operand.getUse().(CallInstruction).getStaticCallTarget() and - f.parameterEscapesOnlyViaReturn(operand.(PositionalArgumentOperand).getIndex()) + ( + f.parameterEscapesOnlyViaReturn(operand.(PositionalArgumentOperand).getIndex()) + or + f.parameterEscapesOnlyViaReturn(-1) and + operand instanceof ThisArgumentOperand + ) ) } private predicate isNeverEscapesArgument(Operand operand) { exists(AliasModels::AliasFunction f | f = operand.getUse().(CallInstruction).getStaticCallTarget() and - f.parameterNeverEscapes(operand.(PositionalArgumentOperand).getIndex()) + ( + f.parameterNeverEscapes(operand.(PositionalArgumentOperand).getIndex()) + or + f.parameterNeverEscapes(-1) and + operand instanceof ThisArgumentOperand + ) ) } @@ -265,17 +338,23 @@ predicate allocationEscapes(Configuration::Allocation allocation) { exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) ) + or + Configuration::phaseNeedsSoundEscapeAnalysis() and + resultEscapesNonReturn(allocation.getABaseInstruction()) } /** * Equivalent to `operandIsPropagated()`, but includes interprocedural propagation. */ -private predicate operandIsPropagatedIncludingByCall(Operand operand, IntValue bitOffset) { - operandIsPropagated(operand, bitOffset) +private predicate operandIsPropagatedIncludingByCall( + Operand operand, IntValue bitOffset, Instruction instr +) { + operandIsPropagated(operand, bitOffset, instr) or exists(CallInstruction call, Instruction init | isArgumentForParameter(call, operand, init) and - resultReturned(init, bitOffset) + resultReturned(init, bitOffset) and + instr = call ) } @@ -292,8 +371,7 @@ private predicate hasBaseAndOffset(AddressOperand addrOperand, Instruction base, // We already have an offset from `middle`. hasBaseAndOffset(addrOperand, middle, previousBitOffset) and // `middle` is propagated from `base`. - middleOperand = middle.getAnOperand() and - operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset) and + operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset, middle) and base = middleOperand.getDef() and bitOffset = Ints::add(previousBitOffset, additionalBitOffset) ) @@ -333,8 +411,51 @@ predicate addressOperandAllocationAndOffset( allocation.getABaseInstruction() = base and hasBaseAndOffset(addrOperand, base, bitOffset) and not exists(Instruction previousBase | - hasBaseAndOffset(addrOperand, previousBase, _) and + hasBaseAndOffset(addrOperand, pragma[only_bind_out](previousBase), _) and previousBase = base.getAnOperand().getDef() ) ) } + +/** + * Predicates used only for printing annotated IR dumps. These should not be used in production + * queries. + */ +module Print { + string getOperandProperty(Operand operand, string key) { + key = "alloc" and + result = + strictconcat(Configuration::Allocation allocation, IntValue bitOffset | + addressOperandAllocationAndOffset(operand, allocation, bitOffset) + | + allocation.toString() + Ints::getBitOffsetString(bitOffset), ", " + ) + or + key = "prop" and + result = + strictconcat(Instruction destInstr, IntValue bitOffset, string value | + operandIsPropagatedIncludingByCall(operand, bitOffset, destInstr) and + if destInstr = operand.getUse() + then value = "@" + Ints::getBitOffsetString(bitOffset) + "->result" + else value = "@" + Ints::getBitOffsetString(bitOffset) + "->" + destInstr.getResultId() + | + value, ", " + ) + } + + string getInstructionProperty(Instruction instr, string key) { + key = "prop" and + result = + strictconcat(IntValue bitOffset, Operand sourceOperand, string value | + operandIsPropagatedIncludingByCall(sourceOperand, bitOffset, instr) and + if instr = sourceOperand.getUse() + then value = sourceOperand.getDumpId() + Ints::getBitOffsetString(bitOffset) + "->@" + else + value = + sourceOperand.getUse().getResultId() + "." + sourceOperand.getDumpId() + + Ints::getBitOffsetString(bitOffset) + "->@" + | + value, ", " + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll index 5be476e12ee..dbdd3c14c85 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll @@ -14,3 +14,5 @@ class Allocation extends IRAutomaticVariable { none() } } + +predicate phaseNeedsSoundEscapeAnalysis() { any() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintAliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintAliasAnalysis.qll new file mode 100644 index 00000000000..262088245e8 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintAliasAnalysis.qll @@ -0,0 +1,19 @@ +/** + * Include this module to annotate IR dumps with information computed by `AliasAnalysis.qll`. + */ + +private import AliasAnalysisInternal +private import InputIR +private import AliasAnalysisImports +private import AliasAnalysis +private import semmle.code.cpp.ir.internal.IntegerConstant + +private class AliasPropertyProvider extends IRPropertyProvider { + override string getOperandProperty(Operand operand, string key) { + result = Print::getOperandProperty(operand, key) + } + + override string getInstructionProperty(Instruction instr, string key) { + result = Print::getInstructionProperty(instr, key) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 340f524fce8..5092e921cb3 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -43,24 +43,81 @@ private module Cached { class TStageInstruction = TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction; + /** + * If `oldInstruction` is a `Phi` instruction that has exactly one reachable predecessor block, + * this predicate returns the `PhiInputOperand` corresponding to that predecessor block. + * Otherwise, this predicate does not hold. + */ + private OldIR::PhiInputOperand getDegeneratePhiOperand(OldInstruction oldInstruction) { + result = + unique(OldIR::PhiInputOperand operand | + operand = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and + operand.getPredecessorBlock() instanceof OldBlock + ) + } + cached predicate hasInstruction(TStageInstruction instr) { instr instanceof TRawInstruction and instr instanceof OldInstruction or - instr instanceof TPhiInstruction + instr = phiInstruction(_, _) + or + instr = reusedPhiInstruction(_) and + // Check that the phi instruction is *not* degenerate, but we can't use + // getDegeneratePhiOperand in the first stage with phi instyructions + not exists( + unique(OldIR::PhiInputOperand operand | + operand = instr.(OldIR::PhiInstruction).getAnInputOperand() and + operand.getPredecessorBlock() instanceof OldBlock + ) + ) or instr instanceof TChiInstruction or instr instanceof TUnreachedInstruction } - private IRBlock getNewBlock(OldBlock oldBlock) { - result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction()) + cached + IRBlock getNewBlock(OldBlock oldBlock) { + exists(Instruction newEnd, OldIR::Instruction oldEnd | + ( + result.getLastInstruction() = newEnd and + not newEnd instanceof ChiInstruction + or + newEnd = result.getLastInstruction().(ChiInstruction).getAPredecessor() // does this work? + ) and + ( + oldBlock.getLastInstruction() = oldEnd and + not oldEnd instanceof OldIR::ChiInstruction + or + oldEnd = oldBlock.getLastInstruction().(OldIR::ChiInstruction).getAPredecessor() // does this work? + ) and + oldEnd = getNewInstruction(newEnd) + ) + } + + /** + * Gets the block from the old IR that corresponds to `newBlock`. + */ + private OldBlock getOldBlock(IRBlock newBlock) { getNewBlock(result) = newBlock } + + /** + * Holds if this iteration of SSA can model the def/use information for the result of + * `oldInstruction`, either because alias analysis has determined a memory location for that + * result, or because a previous iteration of the IR already computed that def/use information + * completely. + */ + private predicate canModelResultForOldInstruction(OldInstruction oldInstruction) { + // We're modeling the result's memory location ourselves. + exists(Alias::getResultMemoryLocation(oldInstruction)) + or + // This result was already modeled by a previous iteration of SSA. + Alias::canReuseSSAForOldResult(oldInstruction) } cached predicate hasModeledMemoryResult(Instruction instruction) { - exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or + canModelResultForOldInstruction(getOldInstruction(instruction)) or instruction instanceof PhiInstruction or // Phis always have modeled results instruction instanceof ChiInstruction // Chis always have modeled results } @@ -117,6 +174,32 @@ private module Cached { ) } + /** + * Gets the new definition instruction for `oldOperand` based on `oldOperand`'s definition in the + * old IR. Usually, this will just get the old definition of `oldOperand` and map it to the + * corresponding new instruction. However, if the old definition of `oldOperand` is a `Phi` + * instruction that is now degenerate due all but one of its predecessor branches being + * unreachable, this predicate will recurse through any degenerate `Phi` instructions to find the + * true definition. + */ + private Instruction getNewDefinitionFromOldSSA(OldIR::MemoryOperand oldOperand, Overlap overlap) { + exists(Overlap originalOverlap | + originalOverlap = oldOperand.getDefinitionOverlap() and + ( + result = getNewInstruction(oldOperand.getAnyDef()) and + overlap = originalOverlap + or + exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap | + phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and + result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and + overlap = + combineOverlap(pragma[only_bind_out](phiOperandOverlap), + pragma[only_bind_out](originalOverlap)) + ) + ) + ) + } + cached private Instruction getMemoryOperandDefinition0( Instruction instruction, MemoryOperandTag tag, Overlap overlap @@ -148,6 +231,12 @@ private module Cached { overlap instanceof MustExactlyOverlap and exists(MustTotallyOverlap o | exists(getMemoryOperandDefinition0(instruction, tag, o))) ) + or + exists(OldIR::NonPhiMemoryOperand oldOperand | + result = getNewDefinitionFromOldSSA(oldOperand, overlap) and + oldOperand.getUse() = instruction and + tag = oldOperand.getOperandTag() + ) } /** @@ -214,10 +303,24 @@ private module Cached { ) } + /** + * Gets the new definition instruction for the operand of `instr` that flows from the block + * `newPredecessorBlock`, based on that operand's definition in the old IR. + */ + private Instruction getNewPhiOperandDefinitionFromOldSSA( + Instruction instr, IRBlock newPredecessorBlock, Overlap overlap + ) { + exists(OldIR::PhiInstruction oldPhi, OldIR::PhiInputOperand oldOperand | + oldPhi = getOldInstruction(instr) and + oldOperand = oldPhi.getInputOperand(getOldBlock(newPredecessorBlock)) and + result = getNewDefinitionFromOldSSA(oldOperand, overlap) + ) + } + pragma[noopt] cached Instruction getPhiOperandDefinition( - PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap + Instruction instr, IRBlock newPredecessorBlock, Overlap overlap ) { exists( Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock, @@ -229,6 +332,8 @@ private module Cached { result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and overlap = Alias::getOverlap(actualDefLocation, useLocation) ) + or + result = getNewPhiOperandDefinitionFromOldSSA(instr, newPredecessorBlock, overlap) } cached @@ -249,7 +354,12 @@ private module Cached { cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) { exists(OldBlock oldBlock | - instr = getPhi(oldBlock, _) and + ( + instr = getPhi(oldBlock, _) + or + // Any `Phi` that we propagated from the previous iteration stays in the same block. + getOldInstruction(instr).getBlock() = oldBlock + ) and result = getNewInstruction(oldBlock.getFirstInstruction()) ) } @@ -335,18 +445,27 @@ private module Cached { result = vvar.getType() ) or + instr = reusedPhiInstruction(_) and + result = instr.(OldInstruction).getResultLanguageType() + or instr = unreachedInstruction(_) and result = Language::getVoidType() } + /** + * Holds if `opcode` is the opcode that specifies the operation performed by `instr`. + * + * The parameters are ordered such that they produce a clean join (with no need for reordering) + * in the characteristic predicates of the `Instruction` subclasses. + */ cached - Opcode getInstructionOpcode(Instruction instr) { - result = getOldInstruction(instr).getOpcode() + predicate getInstructionOpcode(Opcode opcode, Instruction instr) { + opcode = getOldInstruction(instr).getOpcode() or - instr = phiInstruction(_, _) and result instanceof Opcode::Phi + instr = phiInstruction(_, _) and opcode instanceof Opcode::Phi or - instr = chiInstruction(_) and result instanceof Opcode::Chi + instr = chiInstruction(_) and opcode instanceof Opcode::Chi or - instr = unreachedInstruction(_) and result instanceof Opcode::Unreached + instr = unreachedInstruction(_) and opcode instanceof Opcode::Unreached } cached @@ -856,6 +975,26 @@ module DefUse { } } +predicate canReuseSSAForMemoryResult(Instruction instruction) { + exists(OldInstruction oldInstruction | + oldInstruction = getOldInstruction(instruction) and + ( + // The previous iteration said it was reusable, so we should mark it as reusable as well. + Alias::canReuseSSAForOldResult(oldInstruction) + or + // The current alias analysis says it is reusable. + Alias::getResultMemoryLocation(oldInstruction).canReuseSSA() + ) + ) + or + exists(Alias::MemoryLocation defLocation | + // This is a `Phi` for a reusable location, so the result of the `Phi` is reusable as well. + instruction = phiInstruction(_, defLocation) and + defLocation.canReuseSSA() + ) + // We don't support reusing SSA for any location that could create a `Chi` instruction. +} + /** * Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the * `DebugSSA` module, which is then imported by PrintSSA. @@ -934,7 +1073,10 @@ module SSAConsistency { locationCount > 1 and func = operand.getEnclosingIRFunction() and funcText = Language::getIdentityString(func.getFunction()) and - message = "Operand has " + locationCount.toString() + " memory accesses in function '$@'." + message = + operand.getUse().toString() + " " + "Operand has " + locationCount.toString() + + " memory accesses in function '$@': " + + strictconcat(Alias::getOperandMemoryLocation(operand).toString(), ", ") ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll index a7b9160bdc7..f3e02c9f6a8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll @@ -17,7 +17,7 @@ private predicate isTotalAccess(Allocation var, AddressOperand addrOperand, IRTy * variable if its address never escapes and all reads and writes of that variable access the entire * variable using the original type of the variable. */ -private predicate isVariableModeled(Allocation var) { +predicate isVariableModeled(Allocation var) { not allocationEscapes(var) and forall(Instruction instr, AddressOperand addrOperand, IRType type | addrOperand = instr.getResultAddressOperand() and @@ -35,6 +35,17 @@ private predicate isVariableModeled(Allocation var) { ) } +/** + * Holds if the SSA use/def chain for the specified variable can be safely reused by later + * iterations of SSA construction. This will hold only if we modeled the variable soundly, so that + * subsequent iterations will recompute SSA for any variable that we assumed did not escape, but + * actually would have escaped if we had used a sound escape analysis. + */ +predicate canReuseSSAForVariable(IRAutomaticVariable var) { + isVariableModeled(var) and + not allocationEscapes(var) +} + private newtype TMemoryLocation = MkMemoryLocation(Allocation var) { isVariableModeled(var) } private MemoryLocation getMemoryLocation(Allocation var) { result.getAllocation() = var } @@ -57,8 +68,12 @@ class MemoryLocation extends TMemoryLocation { final Language::LanguageType getType() { result = var.getLanguageType() } final string getUniqueId() { result = var.getUniqueId() } + + final predicate canReuseSSA() { canReuseSSAForVariable(var) } } +predicate canReuseSSAForOldResult(Instruction instr) { none() } + /** * Represents a set of `MemoryLocation`s that cannot overlap with * `MemoryLocation`s outside of the set. The `VirtualVariable` will be diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll index f9a0c574f8c..ca643b56cbb 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll @@ -8,6 +8,16 @@ private newtype TOverlap = */ abstract class Overlap extends TOverlap { abstract string toString(); + + /** + * Gets a value representing how precise this overlap is. The higher the value, the more precise + * the overlap. The precision values are ordered as + * follows, from most to least precise: + * `MustExactlyOverlap` + * `MustTotallyOverlap` + * `MayPartiallyOverlap` + */ + abstract int getPrecision(); } /** @@ -16,6 +26,8 @@ abstract class Overlap extends TOverlap { */ class MayPartiallyOverlap extends Overlap, TMayPartiallyOverlap { final override string toString() { result = "MayPartiallyOverlap" } + + final override int getPrecision() { result = 0 } } /** @@ -24,6 +36,8 @@ class MayPartiallyOverlap extends Overlap, TMayPartiallyOverlap { */ class MustTotallyOverlap extends Overlap, TMustTotallyOverlap { final override string toString() { result = "MustTotallyOverlap" } + + final override int getPrecision() { result = 1 } } /** @@ -32,4 +46,25 @@ class MustTotallyOverlap extends Overlap, TMustTotallyOverlap { */ class MustExactlyOverlap extends Overlap, TMustExactlyOverlap { final override string toString() { result = "MustExactlyOverlap" } + + final override int getPrecision() { result = 2 } +} + +/** + * Gets the `Overlap` that best represents the relationship between two memory locations `a` and + * `c`, where `getOverlap(a, b) = previousOverlap` and `getOverlap(b, c) = newOverlap`, for some + * intermediate memory location `b`. + */ +Overlap combineOverlap(Overlap previousOverlap, Overlap newOverlap) { + // Note that it's possible that two less precise overlaps could combine to result in a more + // precise overlap. For example, both `previousOverlap` and `newOverlap` could be + // `MustTotallyOverlap` even though the actual relationship between `a` and `c` is + // `MustExactlyOverlap`. We will still return `MustTotallyOverlap` as the best conservative + // approximation we can make without additional input information. + result = + min(Overlap overlap | + overlap = [previousOverlap, newOverlap] + | + overlap order by overlap.getPrecision() + ) } diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll index 7a1ad0403f7..e249a164061 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll @@ -1,17 +1,64 @@ +import semmle.code.cpp.models.interfaces.Alias +import semmle.code.cpp.models.interfaces.SideEffect import semmle.code.cpp.models.interfaces.Taint +import semmle.code.cpp.models.interfaces.DataFlow import semmle.code.cpp.models.interfaces.PointerWrapper /** - * The `std::shared_ptr` and `std::unique_ptr` template classes. + * The `std::shared_ptr`, `std::weak_ptr`, and `std::unique_ptr` template classes. */ -private class UniqueOrSharedPtr extends Class, PointerWrapper { - UniqueOrSharedPtr() { this.hasQualifiedName(["std", "bsl"], ["shared_ptr", "unique_ptr"]) } +private class SmartPtr extends Class, PointerWrapper { + SmartPtr() { this.hasQualifiedName(["std", "bsl"], ["shared_ptr", "weak_ptr", "unique_ptr"]) } override MemberFunction getAnUnwrapperFunction() { result.(OverloadedPointerDereferenceFunction).getDeclaringType() = this or result.getClassAndName(["operator->", "get"]) = this } + + override predicate pointsToConst() { this.getTemplateArgument(0).(Type).isConst() } +} + +/** + * Any function that returns the address wrapped by a `PointerWrapper`, whether as a pointer or a + * reference. + * + * Examples: + * - `std::unique_ptr::get()` + * - `std::shared_ptr::operator->()` + * - `std::weak_ptr::operator*()` + */ +private class PointerUnwrapperFunction extends MemberFunction, TaintFunction, DataFlowFunction, + SideEffectFunction, AliasFunction { + PointerUnwrapperFunction() { + exists(PointerWrapper wrapper | wrapper.getAnUnwrapperFunction() = this) + } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + input.isReturnValueDeref() and + output.isQualifierObject() + } + + override predicate hasDataFlow(FunctionInput input, FunctionOutput output) { + input.isQualifierObject() and output.isReturnValue() + } + + override predicate hasOnlySpecificReadSideEffects() { any() } + + override predicate hasOnlySpecificWriteSideEffects() { any() } + + override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { + // Only reads from `*this`. + i = -1 and buffer = false + } + + override predicate parameterNeverEscapes(int index) { index = -1 } + + override predicate parameterEscapesOnlyViaReturn(int index) { none() } + + override predicate hasAddressFlow(FunctionInput input, FunctionOutput output) { + input.isQualifierObject() and output.isReturnValue() + } } /** @@ -42,31 +89,79 @@ private class MakeUniqueOrShared extends TaintFunction { } /** - * A prefix `operator*` member function for a `shared_ptr` or `unique_ptr` type. + * A function that sets the value of a smart pointer. + * + * This could be a constructor, an assignment operator, or a named member function like `reset()`. */ -private class UniqueOrSharedDereferenceMemberOperator extends MemberFunction, TaintFunction { - UniqueOrSharedDereferenceMemberOperator() { - this.hasName("operator*") and - this.getDeclaringType() instanceof UniqueOrSharedPtr +private class SmartPtrSetterFunction extends MemberFunction, AliasFunction, SideEffectFunction { + SmartPtrSetterFunction() { + this.getDeclaringType() instanceof SmartPtr and + not this.isStatic() and + ( + this instanceof Constructor + or + this.hasName("operator=") + or + this.hasName("reset") + ) } - override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { - input.isQualifierObject() and - output.isReturnValueDeref() - } -} + override predicate hasOnlySpecificReadSideEffects() { none() } -/** - * The `std::shared_ptr` or `std::unique_ptr` function `get`. - */ -private class UniqueOrSharedGet extends TaintFunction { - UniqueOrSharedGet() { - this.hasName("get") and - this.getDeclaringType() instanceof UniqueOrSharedPtr + override predicate hasOnlySpecificWriteSideEffects() { none() } + + override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) { + // Always write to the destination smart pointer itself. + i = -1 and buffer = false and mustWrite = true + or + // When taking ownership of a smart pointer via an rvalue reference, always overwrite the input + // smart pointer. + getPointerInput().isParameterDeref(i) and + this.getParameter(i).getUnspecifiedType() instanceof RValueReferenceType and + buffer = false and + mustWrite = true } - override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { - input.isQualifierObject() and + override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { + getPointerInput().isParameterDeref(i) and + buffer = false + or + not this instanceof Constructor and + i = -1 and + buffer = false + } + + override predicate parameterNeverEscapes(int index) { index = -1 } + + override predicate parameterEscapesOnlyViaReturn(int index) { none() } + + override predicate hasAddressFlow(FunctionInput input, FunctionOutput output) { + input = getPointerInput() and + output.isQualifierObject() + or + // Assignment operator always returns a reference to `*this`. + this.hasName("operator=") and + input.isQualifierAddress() and output.isReturnValue() } + + private FunctionInput getPointerInput() { + exists(Parameter param0 | param0 = this.getParameter(0) | + ( + param0.getUnspecifiedType().(ReferenceType).getBaseType() instanceof SmartPtr and + if this.getParameter(1).getUnspecifiedType() instanceof PointerType + then + // This is one of the constructors of `std::shared_ptr` that creates a smart pointer that + // wraps a raw pointer with ownership controlled by an unrelated smart pointer. We propagate + // the raw pointer in the second parameter, rather than the smart pointer in the first + // parameter. + result.isParameter(1) + else result.isParameterDeref(0) + or + // One of the functions that takes ownership of a raw pointer. + param0.getUnspecifiedType() instanceof PointerType and + result.isParameter(0) + ) + ) + } } diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Alias.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Alias.qll index 083bbf55c38..e947a93fc90 100644 --- a/cpp/ql/src/semmle/code/cpp/models/interfaces/Alias.qll +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Alias.qll @@ -50,5 +50,16 @@ abstract class AliasFunction extends Function { /** * Holds if the function always returns the value of the parameter at the specified index. */ - abstract predicate parameterIsAlwaysReturned(int index); + predicate parameterIsAlwaysReturned(int index) { none() } + + /** + * Holds if the address passed in via `input` is always propagated to `output`. + */ + predicate hasAddressFlow(FunctionInput input, FunctionOutput output) { + exists(int index | + // By default, just use the old `parameterIsAlwaysReturned` predicate to detect flow from the + // parameter to the return value. + input.isParameter(index) and output.isReturnValue() and this.parameterIsAlwaysReturned(index) + ) + } } diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/PointerWrapper.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/PointerWrapper.qll index 447e7f7cede..8948aee424b 100644 --- a/cpp/ql/src/semmle/code/cpp/models/interfaces/PointerWrapper.qll +++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/PointerWrapper.qll @@ -11,4 +11,7 @@ abstract class PointerWrapper extends Class { * that return a reference to the pointed-to object. */ abstract MemberFunction getAnUnwrapperFunction(); + + /** Holds if the type of the data that is pointed to by this pointer wrapper is `const`. */ + abstract predicate pointsToConst(); } diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll index f3bbbddd97e..79dbe49611e 100644 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll @@ -1617,6 +1617,20 @@ private module SimpleRangeAnalysisCached { defMightOverflowPositively(def, v) } + /** + * Holds if `e` is an expression where the concept of overflow makes sense. + * This predicate is used to filter out some of the unanalyzable expressions + * from `exprMightOverflowPositively` and `exprMightOverflowNegatively`. + */ + pragma[inline] + private predicate exprThatCanOverflow(Expr e) { + e instanceof UnaryArithmeticOperation or + e instanceof BinaryArithmeticOperation or + e instanceof AssignArithmeticOperation or + e instanceof LShiftExpr or + e instanceof AssignLShiftExpr + } + /** * Holds if the expression might overflow negatively. This predicate * does not consider the possibility that the expression might overflow @@ -1630,6 +1644,11 @@ private module SimpleRangeAnalysisCached { // bound of `x`, so the standard logic (above) does not work for // detecting whether it might overflow. getLowerBoundsImpl(expr.(PostfixDecrExpr)) = exprMinVal(expr) + or + // We can't conclude that any unanalyzable expression might overflow. This + // is because there are many expressions that the range analysis doesn't + // handle, but where the concept of overflow doesn't make sense. + exprThatCanOverflow(expr) and not analyzableExpr(expr) } /** @@ -1657,6 +1676,11 @@ private module SimpleRangeAnalysisCached { // bound of `x`, so the standard logic (above) does not work for // detecting whether it might overflow. getUpperBoundsImpl(expr.(PostfixIncrExpr)) = exprMaxVal(expr) + or + // We can't conclude that any unanalyzable expression might overflow. This + // is because there are many expressions that the range analysis doesn't + // handle, but where the concept of overflow doesn't make sense. + exprThatCanOverflow(expr) and not analyzableExpr(expr) } /** diff --git a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll index 606242e833c..55ef606483c 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll @@ -10,10 +10,18 @@ import cpp string getAnInsecureAlgorithmName() { result = [ - "DES", "RC2", "RC4", "RC5", "ARCFOUR" // ARCFOUR is a variant of RC4 + "DES", "RC2", "RC4", "RC5", "ARCFOUR", // ARCFOUR is a variant of RC4 + "3DES", "DES3" // also appears separated, e.g. "TRIPLE-DES", which will be matched as "DES". ] } +/** + * Gets the name of an algorithm that is known to be secure. + */ +string getASecureAlgorithmName() { + result = ["RSA", "SHA256", "CCM", "GCM", "AES", "Blowfish", "ECIES"] +} + /** * Gets the name of a hash algorithm that is insecure if it is being used for * encryption (but it is hard to know when that is happening). @@ -23,25 +31,40 @@ string getAnInsecureHashAlgorithmName() { result = ["SHA1", "MD5"] } /** * Gets the regular expression used for matching strings that look like they * contain an algorithm that is known to be insecure. + * + * Consider using `isInsecureEncryption` rather than accessing this regular + * expression directly. */ string getInsecureAlgorithmRegex() { result = // algorithms usually appear in names surrounded by characters that are not - // alphabetical characters in the same case. This handles the upper and lower - // case cases - "(^|.*[^A-Z])(" + strictconcat(getAnInsecureAlgorithmName(), "|") + ")([^A-Z].*|$)" + "|" + - // for lowercase, we want to be careful to avoid being confused by camelCase - // hence we require two preceding uppercase letters to be sure of a case switch, - // or a preceding non-alphabetic character - "(^|.*[A-Z]{2}|.*[^a-zA-Z])(" + strictconcat(getAnInsecureAlgorithmName().toLowerCase(), "|") + - ")([^a-z].*|$)" + // alphabetical characters in the same case or numerical digits. This + // handles the upper case: + "(^|.*[^A-Z0-9])(" + strictconcat(getAnInsecureAlgorithmName(), "|") + ")([^A-Z0-9].*|$)" + "|" + + // for lowercase, we want to be careful to avoid being confused by + //camelCase, hence we require two preceding uppercase letters to be + // sure of a case switch (or a preceding non-alphabetic, non-numeric + // character). + "(^|.*[A-Z]{2}|.*[^a-zA-Z0-9])(" + + strictconcat(getAnInsecureAlgorithmName().toLowerCase(), "|") + ")([^a-z0-9].*|$)" } /** - * Gets the name of an algorithm that is known to be secure. + * Holds if `name` looks like it might be related to operations with an + * insecure encyption algorithm. */ -string getASecureAlgorithmName() { - result = ["RSA", "SHA256", "CCM", "GCM", "AES", "Blowfish", "ECIES"] +bindingset[name] +predicate isInsecureEncryption(string name) { name.regexpMatch(getInsecureAlgorithmRegex()) } + +/** + * Holds if there is additional evidence that `name` looks like it might be + * related to operations with an encyption algorithm, besides the name of a + * specific algorithm. This can be used in conjuction with + * `isInsecureEncryption` to produce a stronger heuristic. + */ +bindingset[name] +predicate isEncryptionAdditionalEvidence(string name) { + name.toUpperCase().matches("%" + ["CRYPT", "CODE", "CODING", "CBC", "KEY", "CIPHER", "MAC"] + "%") } /** @@ -51,14 +74,15 @@ string getASecureAlgorithmName() { string getSecureAlgorithmRegex() { result = // algorithms usually appear in names surrounded by characters that are not - // alphabetical characters in the same case. This handles the upper and lower - // case cases - "(^|.*[^A-Z])(" + strictconcat(getASecureAlgorithmName(), "|") + ")([^A-Z].*|$)" + "|" + - // for lowercase, we want to be careful to avoid being confused by camelCase - // hence we require two preceding uppercase letters to be sure of a case - // switch, or a preceding non-alphabetic character - "(^|.*[A-Z]{2}|.*[^a-zA-Z])(" + strictconcat(getASecureAlgorithmName().toLowerCase(), "|") + - ")([^a-z].*|$)" + // alphabetical characters in the same case or numerical digits. This + // handles the upper case: + "(^|.*[^A-Z0-9])(" + strictconcat(getASecureAlgorithmName(), "|") + ")([^A-Z0-9].*|$)" + "|" + + // for lowercase, we want to be careful to avoid being confused by + //camelCase, hence we require two preceding uppercase letters to be + // sure of a case switch (or a preceding non-alphabetic, non-numeric + // character). + "(^|.*[A-Z]{2}|.*[^a-zA-Z0-9])(" + strictconcat(getASecureAlgorithmName().toLowerCase(), "|") + + ")([^a-z0-9].*|$)" } /** diff --git a/cpp/ql/src/semmle/code/cpp/security/Overflow.qll b/cpp/ql/src/semmle/code/cpp/security/Overflow.qll index e7ad1c559e6..b8ed406cb4a 100644 --- a/cpp/ql/src/semmle/code/cpp/security/Overflow.qll +++ b/cpp/ql/src/semmle/code/cpp/security/Overflow.qll @@ -5,12 +5,14 @@ import cpp import semmle.code.cpp.controlflow.Dominance +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils /** * Holds if the value of `use` is guarded using `abs`. */ predicate guardedAbs(Operation e, Expr use) { - exists(FunctionCall fc | fc.getTarget().getName() = "abs" | + exists(FunctionCall fc | fc.getTarget().getName() = ["abs", "labs", "llabs", "imaxabs"] | fc.getArgument(0).getAChild*() = use and guardedLesser(e, fc) ) @@ -94,9 +96,14 @@ predicate guardedGreater(Operation e, Expr use) { VariableAccess varUse(LocalScopeVariable v) { result = v.getAnAccess() } /** - * Holds if `e` is not guarded against overflow by `use`. + * Holds if `e` potentially overflows and `use` is an operand of `e` that is not guarded. */ predicate missingGuardAgainstOverflow(Operation e, VariableAccess use) { + // Since `e` is guarenteed to be a `BinaryArithmeticOperation`, a `UnaryArithmeticOperation` or + // an `AssignArithmeticOperation` by the other constraints in this predicate, we know that + // `convertedExprMightOverflowPositively` will have a result even when `e` is not analyzable + // by `SimpleRangeAnalysis`. + convertedExprMightOverflowPositively(e) and use = e.getAnOperand() and exists(LocalScopeVariable v | use.getTarget() = v | // overflow possible if large @@ -115,9 +122,14 @@ predicate missingGuardAgainstOverflow(Operation e, VariableAccess use) { } /** - * Holds if `e` is not guarded against underflow by `use`. + * Holds if `e` potentially underflows and `use` is an operand of `e` that is not guarded. */ predicate missingGuardAgainstUnderflow(Operation e, VariableAccess use) { + // Since `e` is guarenteed to be a `BinaryArithmeticOperation`, a `UnaryArithmeticOperation` or + // an `AssignArithmeticOperation` by the other constraints in this predicate, we know that + // `convertedExprMightOverflowNegatively` will have a result even when `e` is not analyzable + // by `SimpleRangeAnalysis`. + convertedExprMightOverflowNegatively(e) and use = e.getAnOperand() and exists(LocalScopeVariable v | use.getTarget() = v | // underflow possible if use is left operand and small diff --git a/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll b/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll index 0d555a6d340..ed1fb4fbb50 100644 --- a/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll +++ b/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll @@ -1136,6 +1136,11 @@ private predicate inForUpdate(Expr forUpdate, Expr child) { exists(Expr mid | inForUpdate(forUpdate, mid) and child.getParent() = mid) } +/** Gets the `rnk`'th `case` statement in `b`. */ +private int indexOfSwitchCaseRank(BlockStmt b, int rnk) { + result = rank[rnk](int i | b.getStmt(i) instanceof SwitchCase) +} + /** * A C/C++ 'switch case' statement. * @@ -1331,16 +1336,14 @@ class SwitchCase extends Stmt, @stmt_switch_case { * `default:` has results `{ x = 3; }, `x = 4;` and `break;`. */ Stmt getAStmt() { - exists(BlockStmt b, int i, int j | + exists(BlockStmt b, int rnk, int i | b.getStmt(i) = this and - b.getStmt(j) = result and - i < j and - not result instanceof SwitchCase and - not exists(SwitchCase sc, int k | - b.getStmt(k) = sc and - i < k and - j > k - ) + i = indexOfSwitchCaseRank(b, rnk) + | + pragma[only_bind_into](b).getStmt([i + 1 .. indexOfSwitchCaseRank(b, rnk + 1) - 1]) = result + or + not exists(indexOfSwitchCaseRank(b, rnk + 1)) and + b.getStmt([i + 1 .. b.getNumStmt() + 1]) = result ) } diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected new file mode 100644 index 00000000000..244a28cf332 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.expected @@ -0,0 +1 @@ +| test.c:14:9:14:16 | intIndex | A variable with this name is used in the $@ condition. | test.c:11:3:16:3 | while (...) ... | loop | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.qlref new file mode 100644 index 00000000000..6da5822f7f0 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/DeclarationOfVariableWithUnnecessarilyWideScope.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-1126/DeclarationOfVariableWithUnnecessarilyWideScope.ql diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c new file mode 100644 index 00000000000..47d89188e6b --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-1126/semmle/tests/test.c @@ -0,0 +1,59 @@ +void workFunction_0(char *s) { + int intIndex = 10; + int intGuard; + char buf[80]; + while(intIndex > 2) // GOOD + { + buf[intIndex] = 1; + intIndex--; + } + intIndex = 10; + while(intIndex > 2) + { + buf[intIndex] = 1; + int intIndex; // BAD + intIndex--; + } + intIndex = 10; + intGuard = 20; + while(intIndex < intGuard--) // GOOD + { + buf[intIndex] = 1; + int intIndex; + intIndex--; + } + intIndex = 10; + intGuard = 20; + while(intIndex < intGuard) // GOOD + { + buf[intIndex] = 1; + int intIndex; + intIndex++; + intGuard--; + } + intIndex = 10; + intGuard = 20; + while(intIndex < intGuard) // GOOD + { + buf[intIndex] = 1; + int intIndex; + intIndex--; + intGuard -= 4; + } + intIndex = 10; + while(intIndex > 2) // GOOD + { + buf[intIndex] = 1; + intIndex -= 2; + int intIndex; + intIndex--; + } + intIndex = 10; + while(intIndex > 2) // GOOD + { + buf[intIndex] = 1; + --intIndex; + int intIndex; + intIndex--; + } +} diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.expected new file mode 100644 index 00000000000..0ab66e5b26c --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.expected @@ -0,0 +1,4 @@ +| test.c:11:16:11:18 | buf | This pointer may have already been cleared in the line 10. | +| test.c:18:8:18:10 | buf | This pointer may have already been cleared in the line 17. | +| test.c:57:8:57:10 | buf | This pointer may have already been cleared in the line 55. | +| test.c:78:8:78:10 | buf | This pointer may have already been cleared in the line 77. | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.qlref new file mode 100644 index 00000000000..242beb593f8 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/DoubleFree.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-415/DoubleFree.ql diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/test.c new file mode 100644 index 00000000000..1c154c03094 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-415/semmle/tests/test.c @@ -0,0 +1,96 @@ +typedef unsigned long size_t; +void *malloc(size_t size); +void free(void *ptr); +#define NULL 0 + +void workFunction_0(char *s) { + int intSize = 10; + char *buf; + buf = (char *) malloc(intSize); + free(buf); // GOOD + if(buf) free(buf); // BAD +} +void workFunction_1(char *s) { + int intSize = 10; + char *buf; + buf = (char *) malloc(intSize); + free(buf); // GOOD + free(buf); // BAD +} +void workFunction_2(char *s) { + int intSize = 10; + char *buf; + buf = (char *) malloc(intSize); + free(buf); // GOOD + buf = NULL; + free(buf); // GOOD +} +void workFunction_3(char *s) { + int intSize = 10; + char *buf; + int intFlag; + buf = (char *) malloc(intSize); + if(buf[1]%5) { + free(buf); // GOOD + buf = NULL; + } + free(buf); // GOOD +} +void workFunction_4(char *s) { + int intSize = 10; + char *buf; + char *tmpbuf; + tmpbuf = (char *) malloc(intSize); + buf = (char *) malloc(intSize); + free(buf); // GOOD + buf = tmpbuf; + free(buf); // GOOD +} +void workFunction_5(char *s, int intFlag) { + int intSize = 10; + char *buf; + + buf = (char *) malloc(intSize); + if(intFlag) { + free(buf); // GOOD + } + free(buf); // BAD +} +void workFunction_6(char *s, int intFlag) { + int intSize = 10; + char *buf; + char *tmpbuf; + + tmpbuf = (char *) malloc(intSize); + buf = (char *) malloc(intSize); + if(intFlag) { + free(buf); // GOOD + buf = tmpbuf; + } + free(buf); // GOOD +} +void workFunction_7(char *s) { + int intSize = 10; + char *buf; + char *buf1; + buf = (char *) malloc(intSize); + buf1 = (char *) realloc(buf,intSize*4); + free(buf); // BAD +} +void workFunction_8(char *s) { + int intSize = 10; + char *buf; + char *buf1; + buf = (char *) malloc(intSize); + buf1 = (char *) realloc(buf,intSize*4); + if(!buf1) + free(buf); // GOOD +} +void workFunction_9(char *s) { + int intSize = 10; + char *buf; + char *buf1; + buf = (char *) malloc(intSize); + if(!(buf1 = (char *) realloc(buf,intSize*4))) + free(buf); // GOOD +} diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.expected deleted file mode 100644 index 80e82cff212..00000000000 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.expected +++ /dev/null @@ -1,5 +0,0 @@ -| test.cpp:30:15:30:26 | call to operator new[] | memory allocation error check is incorrect or missing | -| test.cpp:38:9:38:20 | call to operator new[] | memory allocation error check is incorrect or missing | -| test.cpp:50:13:50:38 | call to operator new[] | memory allocation error check is incorrect or missing | -| test.cpp:51:22:51:47 | call to operator new[] | memory allocation error check is incorrect or missing | -| test.cpp:53:18:53:43 | call to operator new[] | memory allocation error check is incorrect or missing | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.qlref deleted file mode 100644 index fc3252ef122..00000000000 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/WrongInDetectingAndHandlingMemoryAllocationErrors.qlref +++ /dev/null @@ -1 +0,0 @@ -experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.ql diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp deleted file mode 100644 index 4fc12d9ccbf..00000000000 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#define NULL ((void*)0) -class exception {}; - -namespace std{ - struct nothrow_t {}; - typedef unsigned long size_t; - class bad_alloc{ - const char* what() const throw(); - }; - extern const std::nothrow_t nothrow; -} - -using namespace std; - -void* operator new(std::size_t _Size); -void* operator new[](std::size_t _Size); -void* operator new( std::size_t count, const std::nothrow_t& tag ) noexcept; -void* operator new[]( std::size_t count, const std::nothrow_t& tag ) noexcept; - -void badNew_0_0() -{ - while (true) { - new int[100]; // BAD [NOT DETECTED] - if(!(new int[100])) // BAD [NOT DETECTED] - return; - } -} -void badNew_0_1() -{ - int * i = new int[100]; // BAD - if(i == 0) - return; - if(!i) - return; - if(i == NULL) - return; - int * j; - j = new int[100]; // BAD - if(j == 0) - return; - if(!j) - return; - if(j == NULL) - return; -} -void badNew_1_0() -{ - try { - while (true) { - new(std::nothrow) int[100]; // BAD - int* p = new(std::nothrow) int[100]; // BAD - int* p1; - p1 = new(std::nothrow) int[100]; // BAD - } - } catch (const exception &){//const std::bad_alloc& e) { -// std::cout << e.what() << '\n'; - } -} -void badNew_1_1() -{ - while (true) { - int* p = new(std::nothrow) int[100]; // BAD [NOT DETECTED] - new(std::nothrow) int[100]; // BAD [NOT DETECTED] - } -} - -void goodNew_0_0() -{ - try { - while (true) { - new int[100]; // GOOD - } - } catch (const exception &){//const std::bad_alloc& e) { -// std::cout << e.what() << '\n'; - } -} - -void goodNew_1_0() -{ - while (true) { - int* p = new(std::nothrow) int[100]; // GOOD - if (p == nullptr) { -// std::cout << "Allocation returned nullptr\n"; - break; - } - int* p1; - p1 = new(std::nothrow) int[100]; // GOOD - if (p1 == nullptr) { -// std::cout << "Allocation returned nullptr\n"; - break; - } - if (new(std::nothrow) int[100] == nullptr) { // GOOD -// std::cout << "Allocation returned nullptr\n"; - break; - } - } -} diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementAfterRefactoringTheCode.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementAfterRefactoringTheCode.expected new file mode 100644 index 00000000000..1eca77a526f --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementAfterRefactoringTheCode.expected @@ -0,0 +1,4 @@ +| test.c:15:6:15:16 | ... + ... | this expression needs your attention | +| test.c:17:17:17:27 | ... + ... | this expression needs your attention | +| test.c:22:10:22:15 | ... > ... | this expression needs your attention | +| test.c:26:10:26:15 | ... > ... | this expression needs your attention | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementAfterRefactoringTheCode.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementAfterRefactoringTheCode.qlref new file mode 100644 index 00000000000..496d5f1b7be --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementAfterRefactoringTheCode.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.ql diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementWhenUsingBitOperations.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementWhenUsingBitOperations.expected new file mode 100644 index 00000000000..d11bbd446a6 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementWhenUsingBitOperations.expected @@ -0,0 +1,2 @@ +| test.c:8:6:8:51 | ... & ... | This bitwise operation appears in a context where a Boolean operation is expected. | +| test.c:10:6:10:30 | ... & ... | This bitwise operation appears in a context where a Boolean operation is expected. | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementWhenUsingBitOperations.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementWhenUsingBitOperations.qlref new file mode 100644 index 00000000000..9bf28db3c8a --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/InsufficientControlFlowManagementWhenUsingBitOperations.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementWhenUsingBitOperations.ql diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/test.c new file mode 100644 index 00000000000..1f41f499ded --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-691/semmle/tests/test.c @@ -0,0 +1,32 @@ +int tmpFunction(){ + return 5; +} +void workFunction_0(char *s) { + int intSize; + char buf[80]; + if(intSize>0 && intSize<80 && memset(buf,0,intSize)) return; // GOOD + if(intSize>0 & intSize<80 & memset(buf,0,intSize)) return; // BAD + if(intSize>0 && tmpFunction()) return; + if(intSize<0 & tmpFunction()) return; // BAD +} +void workFunction_1(char *s) { + int intA,intB; + + if(intA + intB) return; // BAD + if(intA + intB>4) return; // GOOD + if(intA>0 && (intA + intB)) return; // BAD + while(intA>0) + { + if(intB - intA<10) break; + intA--; + }while(intA>0); // BAD + for(intA=100; intA>0; intA--) + { + if(intB - intA<10) break; + }while(intA>0); // BAD + while(intA>0) + { + if(intB - intA<10) break; + intA--; + } // GOOD +} diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.expected index 103afd8ffd9..75e40fb96b3 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.expected @@ -1,9 +1,9 @@ -| test.c:42:3:42:24 | ... = ... | potential unsafe or redundant assignment. | -| test.c:43:3:43:40 | ... = ... | potential unsafe or redundant assignment. | -| test.c:44:3:44:40 | ... = ... | potential unsafe or redundant assignment. | -| test.c:45:3:45:44 | ... = ... | potential unsafe or redundant assignment. | -| test.c:46:3:46:44 | ... = ... | potential unsafe or redundant assignment. | -| test.c:47:3:47:48 | ... = ... | potential unsafe or redundant assignment. | -| test.c:48:3:48:48 | ... = ... | potential unsafe or redundant assignment. | -| test.c:49:3:49:50 | ... = ... | potential unsafe or redundant assignment. | -| test.c:50:3:50:50 | ... = ... | potential unsafe or redundant assignment. | +| test.c:16:3:16:24 | ... = ... | potential unsafe or redundant assignment. | +| test.c:17:3:17:40 | ... = ... | potential unsafe or redundant assignment. | +| test.c:18:3:18:44 | ... = ... | potential unsafe or redundant assignment. | +| test.c:19:3:19:44 | ... = ... | potential unsafe or redundant assignment. | +| test.c:20:3:20:48 | ... = ... | potential unsafe or redundant assignment. | +| test.c:21:3:21:48 | ... = ... | potential unsafe or redundant assignment. | +| test.c:22:3:22:52 | ... = ... | potential unsafe or redundant assignment. | +| test.c:23:3:23:50 | ... = ... | potential unsafe or redundant assignment. | +| test.c:24:3:24:54 | ... = ... | potential unsafe or redundant assignment. | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.expected deleted file mode 100644 index af52dac0144..00000000000 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.expected +++ /dev/null @@ -1,3 +0,0 @@ -| test.c:4:3:4:9 | call to strncat | if the used buffer is full, writing out of the buffer is possible | -| test.c:11:3:11:9 | call to strncat | if the used buffer is full, writing out of the buffer is possible | -| test.c:19:3:19:9 | call to strncat | if the used buffer is full, writing out of the buffer is possible | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.qlref deleted file mode 100644 index 8fd8b1b3217..00000000000 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.qlref +++ /dev/null @@ -1 +0,0 @@ -experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.ql diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/OperatorPrecedenceLogicErrorWhenUseBoolType.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/OperatorPrecedenceLogicErrorWhenUseBoolType.expected new file mode 100644 index 00000000000..1209c7e7830 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/OperatorPrecedenceLogicErrorWhenUseBoolType.expected @@ -0,0 +1,5 @@ +| test.cpp:10:8:10:10 | - ... | this expression needs attention | +| test.cpp:12:3:12:6 | ... ++ | this expression needs attention | +| test.cpp:13:3:13:6 | ++ ... | this expression needs attention | +| test.cpp:14:6:14:21 | ... = ... | this expression needs attention | +| test.cpp:16:6:16:21 | ... = ... | this expression needs attention | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/OperatorPrecedenceLogicErrorWhenUseBoolType.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/OperatorPrecedenceLogicErrorWhenUseBoolType.qlref new file mode 100644 index 00000000000..5189abcce5d --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/OperatorPrecedenceLogicErrorWhenUseBoolType.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBoolType.ql diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/test.c index d986bb3b13c..a204aa4db29 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/test.c +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/test.c @@ -1,70 +1,46 @@ -void workFunction_0(char *s) { - char buf[80]; - strncat(buf, s, sizeof(buf)-strlen(buf)-1); // GOOD - strncat(buf, s, sizeof(buf)-strlen(buf)); // BAD - strncat(buf, "fix", sizeof(buf)-strlen(buf)); // BAD [NOT DETECTED] -} -void workFunction_1(char *s) { -#define MAX_SIZE 80 - char buf[MAX_SIZE]; - strncat(buf, s, MAX_SIZE-strlen(buf)-1); // GOOD - strncat(buf, s, MAX_SIZE-strlen(buf)); // BAD - strncat(buf, "fix", MAX_SIZE-strlen(buf)); // BAD [NOT DETECTED] -} -void workFunction_2_0(char *s) { - char * buf; - int len=80; - buf = (char *) malloc(len); - strncat(buf, s, len-strlen(buf)-1); // GOOD - strncat(buf, s, len-strlen(buf)); // BAD - strncat(buf, "fix", len-strlen(buf)); // BAD [NOT DETECTED] -} -void workFunction_2_1(char *s) { - char * buf; - int len=80; - buf = (char *) malloc(len+1); - strncat(buf, s, len-strlen(buf)-1); // GOOD - strncat(buf, s, len-strlen(buf)); // GOOD -} +char * strncat(char*, const char*, unsigned); +unsigned strlen(const char*); +void* malloc(unsigned); struct buffers { - unsigned char buff1[50]; - unsigned char *buff2; + unsigned char array[50]; + unsigned char *pointer; } globalBuff1,*globalBuff2,globalBuff1_c,*globalBuff2_c; - -void badFunc0(){ +void strlen_test1(){ unsigned char buff1[12]; struct buffers buffAll; struct buffers * buffAll1; buff1[strlen(buff1)]=0; // BAD - buffAll.buff1[strlen(buffAll.buff1)]=0; // BAD - buffAll.buff2[strlen(buffAll.buff2)]=0; // BAD - buffAll1->buff1[strlen(buffAll1->buff1)]=0; // BAD - buffAll1->buff2[strlen(buffAll1->buff2)]=0; // BAD - globalBuff1.buff1[strlen(globalBuff1.buff1)]=0; // BAD - globalBuff1.buff2[strlen(globalBuff1.buff2)]=0; // BAD - globalBuff2->buff1[strlen(globalBuff2->buff1)]=0; // BAD - globalBuff2->buff2[strlen(globalBuff2->buff2)]=0; // BAD + buffAll.array[strlen(buffAll.array)]=0; // BAD + buffAll.pointer[strlen(buffAll.pointer)]=0; // BAD + buffAll1->array[strlen(buffAll1->array)]=0; // BAD + buffAll1->pointer[strlen(buffAll1->pointer)]=0; // BAD + globalBuff1.array[strlen(globalBuff1.array)]=0; // BAD + globalBuff1.pointer[strlen(globalBuff1.pointer)]=0; // BAD + globalBuff2->array[strlen(globalBuff2->array)]=0; // BAD + globalBuff2->pointer[strlen(globalBuff2->pointer)]=0; // BAD } -void noBadFunc0(){ + +void strlen_test2(){ unsigned char buff1[12],buff1_c[12]; struct buffers buffAll,buffAll_c; struct buffers * buffAll1,*buffAll1_c; buff1[strlen(buff1_c)]=0; // GOOD - buffAll.buff1[strlen(buffAll_c.buff1)]=0; // GOOD - buffAll.buff2[strlen(buffAll.buff1)]=0; // GOOD - buffAll1->buff1[strlen(buffAll1_c->buff1)]=0; // GOOD - buffAll1->buff2[strlen(buffAll1->buff1)]=0; // GOOD - globalBuff1.buff1[strlen(globalBuff1_c.buff1)]=0; // GOOD - globalBuff1.buff2[strlen(globalBuff1.buff1)]=0; // GOOD - globalBuff2->buff1[strlen(globalBuff2_c->buff1)]=0; // GOOD - globalBuff2->buff2[strlen(globalBuff2->buff1)]=0; // GOOD + buffAll.array[strlen(buffAll_c.array)]=0; // GOOD + buffAll.pointer[strlen(buffAll.array)]=0; // GOOD + buffAll1->array[strlen(buffAll1_c->array)]=0; // GOOD + buffAll1->pointer[strlen(buffAll1->array)]=0; // GOOD + globalBuff1.array[strlen(globalBuff1_c.array)]=0; // GOOD + globalBuff1.pointer[strlen(globalBuff1.array)]=0; // GOOD + globalBuff2->array[strlen(globalBuff2_c->array)]=0; // GOOD + globalBuff2->pointer[strlen(globalBuff2->array)]=0; // GOOD } -void goodFunc0(){ + +void strlen_test3(){ unsigned char buffer[12]; int i; for(i = 0; i < 6; i++) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/test.cpp new file mode 100644 index 00000000000..f08d2a45757 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/test.cpp @@ -0,0 +1,26 @@ +int tmpFunc() +{ + return 12; +} +void testFunction() +{ + int i1,i2,i3; + bool b1,b2,b3; + char c1,c2,c3; + b1 = -b2; //BAD + b1 = !b2; //GOOD + b1++; //BAD + ++b1; //BAD + if(i1=tmpFunc()!=i2) //BAD + return; + if(i1=tmpFunc()!=11) //BAD + return; + if((i1=tmpFunc())!=i2) //GOOD + return; + if((i1=tmpFunc())!=11) //GOOD + return; + if(i1=tmpFunc()!=1) //GOOD + return; + if(i1=tmpFunc()==b1) //GOOD + return; +} diff --git a/cpp/ql/test/include/memory.h b/cpp/ql/test/include/memory.h new file mode 100644 index 00000000000..fdd09a7d43b --- /dev/null +++ b/cpp/ql/test/include/memory.h @@ -0,0 +1,139 @@ +#if !defined(CODEQL_MEMORY_H) +#define CODEQL_MEMORY_H + +namespace std { + namespace detail { + template + class compressed_pair_element { + T element; + + public: + compressed_pair_element() = default; + compressed_pair_element(const T& t) : element(t) {} + + T& get() { return element; } + + const T& get() const { return element; } + }; + + template + struct compressed_pair : private compressed_pair_element, private compressed_pair_element { + compressed_pair() = default; + compressed_pair(T& t) : compressed_pair_element(t), compressed_pair_element() {} + compressed_pair(const compressed_pair&) = delete; + compressed_pair(compressed_pair&&) noexcept = default; + + T& first() { return static_cast&>(*this).get(); } + U& second() { return static_cast&>(*this).get(); } + + const T& first() const { return static_cast&>(*this).get(); } + const U& second() const { return static_cast&>(*this).get(); } + }; + } + + template + struct default_delete { + void operator()(T* ptr) const { delete ptr; } + }; + + template + struct default_delete { + template + void operator()(U* ptr) const { delete[] ptr; } + }; + + template > + class unique_ptr { + private: + detail::compressed_pair data; + public: + constexpr unique_ptr() noexcept {} + explicit unique_ptr(T* ptr) noexcept : data(ptr) {} + unique_ptr(const unique_ptr& ptr) = delete; + unique_ptr(unique_ptr&& ptr) noexcept = default; + + unique_ptr& operator=(unique_ptr&& ptr) noexcept = default; + + T& operator*() const { return *get(); } + T* operator->() const noexcept { return get(); } + + T* get() const noexcept { return data.first(); } + T* release() noexcept { + Deleter& d = data.second(); + d(data.first()); + data.first() = nullptr; + } + + ~unique_ptr() { + Deleter& d = data.second(); + d(data.first()); + } + }; + + template unique_ptr make_unique(Args&&... args) { + return unique_ptr(new T(args...)); // std::forward calls elided for simplicity. + } + + class ctrl_block { + unsigned uses; + + public: + ctrl_block() : uses(1) {} + + void inc() { ++uses; } + bool dec() { return --uses == 0; } + + virtual void destroy() = 0; + virtual ~ctrl_block() {} + }; + + template > + struct ctrl_block_impl: public ctrl_block { + T* ptr; + Deleter d; + + ctrl_block_impl(T* ptr, Deleter d) : ptr(ptr), d(d) {} + virtual void destroy() override { d(ptr); } + }; + + template + class shared_ptr { + private: + ctrl_block* ctrl; + T* ptr; + + void dec() { + if(ctrl->dec()) { + ctrl->destroy(); + delete ctrl; + } + } + + void inc() { + ctrl->inc(); + } + + public: + constexpr shared_ptr() noexcept = default; + shared_ptr(T* ptr) : ctrl(new ctrl_block_impl(ptr, default_delete())) {} + shared_ptr(const shared_ptr& s) noexcept : ptr(s.ptr), ctrl(s.ctrl) { + inc(); + } + shared_ptr(shared_ptr&& s) noexcept = default; + shared_ptr(unique_ptr&& s) : shared_ptr(s.release()) { + } + T* operator->() const { return ptr; } + + T& operator*() const { return *ptr; } + + T* get() const noexcept { return ptr; } + + ~shared_ptr() { dec(); } + }; + + template shared_ptr make_shared(Args&&... args) { + return shared_ptr(new T(args...)); // std::forward calls elided for simplicity. + } +} + +#endif \ No newline at end of file diff --git a/cpp/ql/test/include/type_traits.h b/cpp/ql/test/include/type_traits.h new file mode 100644 index 00000000000..19bdd46906b --- /dev/null +++ b/cpp/ql/test/include/type_traits.h @@ -0,0 +1,21 @@ +#if !defined(CODEQL_TYPE_TRAITS_H) +#define CODEQL_TYPE_TRAITS_H + +namespace std { + template + struct remove_reference { + typedef T type; + }; + + template + struct remove_reference { + typedef T type; + }; + + template + struct remove_reference { + typedef T type; + }; +} + +#endif diff --git a/cpp/ql/test/include/utility.h b/cpp/ql/test/include/utility.h new file mode 100644 index 00000000000..24c8572886d --- /dev/null +++ b/cpp/ql/test/include/utility.h @@ -0,0 +1,13 @@ +#if !defined(CODEQL_UTILITY_H) +#define CODEQL_UTILITY_H + +#include "type_traits.h" + +namespace std { + template + typename remove_reference::type&& move(T&& src) { + return static_cast::type&&>(src); + } +} + +#endif diff --git a/cpp/ql/test/library-tests/clang_ms/element.expected b/cpp/ql/test/library-tests/clang_ms/element.expected index 11e28a50670..8ca381ef406 100644 --- a/cpp/ql/test/library-tests/clang_ms/element.expected +++ b/cpp/ql/test/library-tests/clang_ms/element.expected @@ -26,6 +26,8 @@ | clang_ms.cpp:17:1:17:32 | #pragma | | clang_ms.cpp:18:1:18:31 | #pragma | | file://:0:0:0:0 | | +| file://:0:0:0:0 | & | +| file://:0:0:0:0 | && | | file://:0:0:0:0 | (global namespace) | | file://:0:0:0:0 | (unnamed parameter 0) | | file://:0:0:0:0 | (unnamed parameter 0) | diff --git a/cpp/ql/test/library-tests/conditions/elements.expected b/cpp/ql/test/library-tests/conditions/elements.expected index 483f5ad22ed..cdab5557305 100644 --- a/cpp/ql/test/library-tests/conditions/elements.expected +++ b/cpp/ql/test/library-tests/conditions/elements.expected @@ -1,4 +1,6 @@ | file://:0:0:0:0 | | +| file://:0:0:0:0 | & | +| file://:0:0:0:0 | && | | file://:0:0:0:0 | (global namespace) | | file://:0:0:0:0 | (unnamed parameter 0) | | file://:0:0:0:0 | (unnamed parameter 0) | diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp index d64f1966b49..843587b576a 100644 --- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp +++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/annotate_sinks_only/defaulttainttracking.cpp @@ -187,10 +187,10 @@ void test_pointers1() ptr4 = &ptr3; sink(buffer); // $ ast,ir - sink(ptr1); // $ ast,ir + sink(ptr1); // $ ast MISSING: ir sink(ptr2); // $ SPURIOUS: ast sink(*ptr2); // $ ast MISSING: ir - sink(ptr3); // $ ast,ir + sink(ptr3); // $ ast MISSING: ir sink(ptr4); // $ SPURIOUS: ast sink(*ptr4); // $ ast MISSING: ir } diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected index fc6c97aa2a6..db9a86fbb57 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected @@ -26,6 +26,7 @@ unreachableNodeCCtx localCallNodes postIsNotPre postHasUniquePre +| test.cpp:373:5:373:20 | Store | PostUpdateNode should have one pre-update node but has 0. | uniquePostUpdate postIsInSameCallable reverseRead @@ -82,4 +83,5 @@ postWithInFlow | test.cpp:125:3:125:11 | Chi | PostUpdateNode should not be the target of local flow. | | test.cpp:359:5:359:20 | Chi | PostUpdateNode should not be the target of local flow. | | test.cpp:373:5:373:20 | Chi | PostUpdateNode should not be the target of local flow. | +| test.cpp:373:5:373:20 | Store | PostUpdateNode should not be the target of local flow. | | test.cpp:465:3:465:15 | Chi | PostUpdateNode should not be the target of local flow. | diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected index 63d3b2c0f48..fe7d8360403 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected @@ -20,7 +20,9 @@ unreachableNodeCCtx localCallNodes postIsNotPre postHasUniquePre +| D.cpp:57:5:57:42 | Store | PostUpdateNode should have one pre-update node but has 0. | | simple.cpp:65:5:65:22 | Store | PostUpdateNode should have one pre-update node but has 0. | +| simple.cpp:83:9:83:28 | Store | PostUpdateNode should have one pre-update node but has 0. | | simple.cpp:92:5:92:22 | Store | PostUpdateNode should have one pre-update node but has 0. | uniquePostUpdate postIsInSameCallable @@ -54,6 +56,7 @@ postWithInFlow | D.cpp:49:15:49:24 | Chi | PostUpdateNode should not be the target of local flow. | | D.cpp:56:15:56:24 | Chi | PostUpdateNode should not be the target of local flow. | | D.cpp:57:5:57:42 | Chi | PostUpdateNode should not be the target of local flow. | +| D.cpp:57:5:57:42 | Store | PostUpdateNode should not be the target of local flow. | | aliasing.cpp:9:3:9:22 | Chi | PostUpdateNode should not be the target of local flow. | | aliasing.cpp:13:3:13:21 | Chi | PostUpdateNode should not be the target of local flow. | | aliasing.cpp:17:3:17:21 | Chi | PostUpdateNode should not be the target of local flow. | @@ -150,6 +153,7 @@ postWithInFlow | simple.cpp:23:35:23:35 | Chi | PostUpdateNode should not be the target of local flow. | | simple.cpp:65:5:65:22 | Store | PostUpdateNode should not be the target of local flow. | | simple.cpp:83:9:83:28 | Chi | PostUpdateNode should not be the target of local flow. | +| simple.cpp:83:9:83:28 | Store | PostUpdateNode should not be the target of local flow. | | simple.cpp:92:5:92:22 | Store | PostUpdateNode should not be the target of local flow. | | struct_init.c:20:20:20:29 | Chi | PostUpdateNode should not be the target of local flow. | | struct_init.c:20:34:20:34 | Chi | PostUpdateNode should not be the target of local flow. | 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 37a0dc3832a..ee6b6a39bde 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 @@ -135,40 +135,24 @@ edges | 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:16:42:16 | f indirection [a_] | -| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:42:16:42:16 | Chi [b_] | | complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:42:16:42:16 | f indirection [b_] | | complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:43:16:43:16 | f indirection [b_] | -| complex.cpp:42:16:42:16 | Chi [b_] | complex.cpp:43:16:43:16 | f indirection [b_] | -| complex.cpp:42:16:42:16 | a output argument [b_] | complex.cpp:42:16:42:16 | Chi [b_] | | complex.cpp:42:16:42:16 | a output argument [b_] | complex.cpp:43:16:43:16 | f indirection [b_] | | complex.cpp:42:16:42:16 | f indirection [a_] | complex.cpp:42:18:42:18 | call to a | | complex.cpp:42:16:42:16 | f indirection [b_] | complex.cpp:42:16:42:16 | a output argument [b_] | | complex.cpp:43:16:43:16 | f indirection [b_] | complex.cpp:43:18:43:18 | call to b | -| complex.cpp:53:12:53:12 | Chi [a_] | complex.cpp:59:7:59:8 | b1 indirection [a_] | -| complex.cpp:53:12:53:12 | setA output argument [a_] | complex.cpp:53:12:53:12 | Chi [a_] | | complex.cpp:53:12:53:12 | setA output argument [a_] | complex.cpp:59:7:59:8 | b1 indirection [a_] | | complex.cpp:53:14:53:17 | call to user_input | complex.cpp:53:12:53:12 | setA output argument [a_] | | complex.cpp:53:19:53:28 | call to user_input | complex.cpp:53:14:53:17 | call to user_input | -| complex.cpp:54:12:54:12 | Chi [b_] | complex.cpp:62:7:62:8 | b2 indirection [b_] | -| complex.cpp:54:12:54:12 | setB output argument [b_] | complex.cpp:54:12:54:12 | Chi [b_] | | complex.cpp:54:12:54:12 | setB output argument [b_] | complex.cpp:62:7:62:8 | b2 indirection [b_] | | complex.cpp:54:14:54:17 | call to user_input | complex.cpp:54:12:54:12 | setB output argument [b_] | | complex.cpp:54:19:54:28 | call to user_input | complex.cpp:54:14:54:17 | call to user_input | -| complex.cpp:55:12:55:12 | Chi [a_] | complex.cpp:56:12:56:12 | Chi [a_] | -| complex.cpp:55:12:55:12 | Chi [a_] | complex.cpp:56:12:56:12 | f indirection [a_] | -| complex.cpp:55:12:55:12 | Chi [a_] | complex.cpp:65:7:65:8 | b3 indirection [a_] | -| complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:55:12:55:12 | Chi [a_] | -| complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:56:12:56:12 | Chi [a_] | | complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:56:12:56:12 | f indirection [a_] | | complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:65:7:65:8 | b3 indirection [a_] | | complex.cpp:55:14:55:17 | call to user_input | complex.cpp:55:12:55:12 | setA output argument [a_] | | complex.cpp:55:19:55:28 | call to user_input | complex.cpp:55:14:55:17 | call to user_input | -| complex.cpp:56:12:56:12 | Chi [a_] | complex.cpp:65:7:65:8 | b3 indirection [a_] | -| complex.cpp:56:12:56:12 | Chi [b_] | complex.cpp:65:7:65:8 | b3 indirection [b_] | | complex.cpp:56:12:56:12 | f indirection [a_] | complex.cpp:56:12:56:12 | setB output argument [a_] | -| complex.cpp:56:12:56:12 | setB output argument [a_] | complex.cpp:56:12:56:12 | Chi [a_] | | complex.cpp:56:12:56:12 | setB output argument [a_] | complex.cpp:65:7:65:8 | b3 indirection [a_] | -| complex.cpp:56:12:56:12 | setB output argument [b_] | complex.cpp:56:12:56:12 | Chi [b_] | | complex.cpp:56:12:56:12 | setB output argument [b_] | complex.cpp:65:7:65:8 | b3 indirection [b_] | | complex.cpp:56:14:56:17 | call to user_input | complex.cpp:56:12:56:12 | setB output argument [b_] | | complex.cpp:56:19:56:28 | call to user_input | complex.cpp:56:14:56:17 | call to user_input | @@ -228,8 +212,8 @@ edges | 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 | this indirection [f1] | -| simple.cpp:83:17:83:26 | call to user_input | simple.cpp:83:9:83:28 | Chi [f1] | +| simple.cpp:83:9:83:28 | Store [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 | Store [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] | @@ -410,27 +394,21 @@ nodes | by_reference.cpp:136:16:136:16 | a | semmle.label | a | | complex.cpp:40:17:40:17 | *b [a_] | semmle.label | *b [a_] | | complex.cpp:40:17:40:17 | *b [b_] | semmle.label | *b [b_] | -| complex.cpp:42:16:42:16 | Chi [b_] | semmle.label | Chi [b_] | | complex.cpp:42:16:42:16 | a output argument [b_] | semmle.label | a output argument [b_] | | complex.cpp:42:16:42:16 | f indirection [a_] | semmle.label | f indirection [a_] | | complex.cpp:42:16:42:16 | f indirection [b_] | semmle.label | f indirection [b_] | | complex.cpp:42:18:42:18 | call to a | semmle.label | call to a | | complex.cpp:43:16:43:16 | f indirection [b_] | semmle.label | f indirection [b_] | | complex.cpp:43:18:43:18 | call to b | semmle.label | call to b | -| complex.cpp:53:12:53:12 | Chi [a_] | semmle.label | Chi [a_] | | complex.cpp:53:12:53:12 | setA output argument [a_] | semmle.label | setA output argument [a_] | | complex.cpp:53:14:53:17 | call to user_input | semmle.label | call to user_input | | complex.cpp:53:19:53:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:54:12:54:12 | Chi [b_] | semmle.label | Chi [b_] | | complex.cpp:54:12:54:12 | setB output argument [b_] | semmle.label | setB output argument [b_] | | complex.cpp:54:14:54:17 | call to user_input | semmle.label | call to user_input | | complex.cpp:54:19:54:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:55:12:55:12 | Chi [a_] | semmle.label | Chi [a_] | | complex.cpp:55:12:55:12 | setA output argument [a_] | semmle.label | setA output argument [a_] | | complex.cpp:55:14:55:17 | call to user_input | semmle.label | call to user_input | | complex.cpp:55:19:55:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:56:12:56:12 | Chi [a_] | semmle.label | Chi [a_] | -| complex.cpp:56:12:56:12 | Chi [b_] | semmle.label | Chi [b_] | | complex.cpp:56:12:56:12 | f indirection [a_] | semmle.label | f indirection [a_] | | complex.cpp:56:12:56:12 | setB output argument [a_] | semmle.label | setB output argument [a_] | | complex.cpp:56:12:56:12 | setB output argument [b_] | semmle.label | setB output argument [b_] | @@ -494,7 +472,7 @@ nodes | simple.cpp:65:11:65:20 | call to user_input | semmle.label | call to user_input | | simple.cpp:66:12:66:12 | Store [i] | semmle.label | Store [i] | | simple.cpp:67:13:67:13 | i | semmle.label | i | -| simple.cpp:83:9:83:28 | Chi [f1] | semmle.label | Chi [f1] | +| simple.cpp:83:9:83:28 | Store [f1] | semmle.label | Store [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] | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.ql b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.ql index d8b6b4e0e69..fae4b06da5a 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.ql +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.ql @@ -19,15 +19,19 @@ class IRPartialDefNode extends IRNode { override string toString() { result = n.asPartialDefinition().toString() } } -from Node node, AST::Node astNode, IR::Node irNode, string msg +from Node node, string msg where - node.asIR() = irNode and - exists(irNode.asPartialDefinition()) and - not exists(AST::Node otherNode | otherNode.asPartialDefinition() = irNode.asPartialDefinition()) and + exists(IR::Node irNode, Expr partial | + node.asIR() = irNode and + partial = irNode.asPartialDefinition() and + not exists(AST::Node otherNode | otherNode.asPartialDefinition() = partial) + ) and msg = "IR only" or - node.asAST() = astNode and - exists(astNode.asPartialDefinition()) and - not exists(IR::Node otherNode | otherNode.asPartialDefinition() = astNode.asPartialDefinition()) and + exists(AST::Node astNode, Expr partial | + node.asAST() = astNode and + partial = astNode.asPartialDefinition() and + not exists(IR::Node otherNode | otherNode.asPartialDefinition() = partial) + ) and msg = "AST only" select node, msg diff --git a/cpp/ql/test/library-tests/dataflow/smart-pointers-taint/test.cpp b/cpp/ql/test/library-tests/dataflow/smart-pointers-taint/test.cpp index 0dbbd7a2384..bd24f0e72f2 100644 --- a/cpp/ql/test/library-tests/dataflow/smart-pointers-taint/test.cpp +++ b/cpp/ql/test/library-tests/dataflow/smart-pointers-taint/test.cpp @@ -7,7 +7,7 @@ void test_unique_ptr_int() { std::unique_ptr p1(new int(source())); std::unique_ptr p2 = std::make_unique(source()); - sink(*p1); // $ MISSING: ast,ir + sink(*p1); // $ ir MISSING: ast sink(*p2); // $ ast ir=8:50 } @@ -21,7 +21,7 @@ void test_unique_ptr_struct() { std::unique_ptr p1(new A{source(), 0}); std::unique_ptr p2 = std::make_unique(source(), 0); - sink(p1->x); // $ MISSING: ast,ir + sink(p1->x); // $ ir MISSING: ast sink(p1->y); sink(p2->x); // $ MISSING: ast,ir sink(p2->y); @@ -31,7 +31,7 @@ void test_shared_ptr_int() { std::shared_ptr p1(new int(source())); std::shared_ptr p2 = std::make_shared(source()); - sink(*p1); // $ ast + sink(*p1); // $ ast ir sink(*p2); // $ ast ir=32:50 } @@ -39,7 +39,7 @@ void test_shared_ptr_struct() { std::shared_ptr p1(new A{source(), 0}); std::shared_ptr p2 = std::make_shared(source(), 0); - sink(p1->x); // $ MISSING: ast,ir + sink(p1->x); // $ ir MISSING: ast sink(p1->y); sink(p2->x); // $ MISSING: ast,ir sink(p2->y); diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index 4444ea13267..ce939661c92 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -3223,52 +3223,235 @@ | smart_pointer.cpp:11:30:11:50 | call to make_shared | smart_pointer.cpp:12:11:12:11 | p | | | smart_pointer.cpp:11:30:11:50 | call to make_shared | smart_pointer.cpp:13:10:13:10 | p | | | smart_pointer.cpp:11:52:11:57 | call to source | smart_pointer.cpp:11:30:11:50 | call to make_shared | TAINT | -| smart_pointer.cpp:12:11:12:11 | p | smart_pointer.cpp:12:10:12:10 | call to operator* | TAINT | +| smart_pointer.cpp:12:11:12:11 | p | smart_pointer.cpp:12:10:12:10 | call to operator* | | | smart_pointer.cpp:12:11:12:11 | ref arg p | smart_pointer.cpp:13:10:13:10 | p | | | smart_pointer.cpp:17:32:17:54 | call to make_shared | smart_pointer.cpp:18:11:18:11 | p | | | smart_pointer.cpp:17:32:17:54 | call to make_shared | smart_pointer.cpp:19:10:19:10 | p | | +| smart_pointer.cpp:18:10:18:10 | ref arg call to operator* | smart_pointer.cpp:18:11:18:11 | p [inner post update] | | +| smart_pointer.cpp:18:10:18:10 | ref arg call to operator* | smart_pointer.cpp:18:11:18:11 | ref arg p | TAINT | +| smart_pointer.cpp:18:10:18:10 | ref arg call to operator* | smart_pointer.cpp:19:10:19:10 | p | | | smart_pointer.cpp:18:11:18:11 | p | smart_pointer.cpp:18:10:18:10 | call to operator* | TAINT | +| smart_pointer.cpp:18:11:18:11 | ref arg p | smart_pointer.cpp:18:11:18:11 | p [inner post update] | | | smart_pointer.cpp:18:11:18:11 | ref arg p | smart_pointer.cpp:19:10:19:10 | p | | | smart_pointer.cpp:23:30:23:50 | call to make_unique | smart_pointer.cpp:24:11:24:11 | p | | | smart_pointer.cpp:23:30:23:50 | call to make_unique | smart_pointer.cpp:25:10:25:10 | p | | | smart_pointer.cpp:23:52:23:57 | call to source | smart_pointer.cpp:23:30:23:50 | call to make_unique | TAINT | -| smart_pointer.cpp:24:11:24:11 | p | smart_pointer.cpp:24:10:24:10 | call to operator* | TAINT | +| smart_pointer.cpp:24:11:24:11 | p | smart_pointer.cpp:24:10:24:10 | call to operator* | | | smart_pointer.cpp:24:11:24:11 | ref arg p | smart_pointer.cpp:25:10:25:10 | p | | | smart_pointer.cpp:29:32:29:54 | call to make_unique | smart_pointer.cpp:30:11:30:11 | p | | | smart_pointer.cpp:29:32:29:54 | call to make_unique | smart_pointer.cpp:31:10:31:10 | p | | +| smart_pointer.cpp:30:10:30:10 | ref arg call to operator* | smart_pointer.cpp:30:11:30:11 | p [inner post update] | | +| smart_pointer.cpp:30:10:30:10 | ref arg call to operator* | smart_pointer.cpp:30:11:30:11 | ref arg p | TAINT | +| smart_pointer.cpp:30:10:30:10 | ref arg call to operator* | smart_pointer.cpp:31:10:31:10 | p | | | smart_pointer.cpp:30:11:30:11 | p | smart_pointer.cpp:30:10:30:10 | call to operator* | TAINT | +| smart_pointer.cpp:30:11:30:11 | ref arg p | smart_pointer.cpp:30:11:30:11 | p [inner post update] | | | smart_pointer.cpp:30:11:30:11 | ref arg p | smart_pointer.cpp:31:10:31:10 | p | | | smart_pointer.cpp:35:30:35:50 | call to make_shared | smart_pointer.cpp:37:6:37:6 | p | | | smart_pointer.cpp:35:30:35:50 | call to make_shared | smart_pointer.cpp:38:10:38:10 | p | | | smart_pointer.cpp:35:30:35:50 | call to make_shared | smart_pointer.cpp:39:11:39:11 | p | | +| smart_pointer.cpp:37:5:37:5 | call to operator* [post update] | smart_pointer.cpp:37:6:37:6 | p [inner post update] | | +| smart_pointer.cpp:37:5:37:5 | call to operator* [post update] | smart_pointer.cpp:37:6:37:6 | ref arg p | TAINT | +| smart_pointer.cpp:37:5:37:5 | call to operator* [post update] | smart_pointer.cpp:38:10:38:10 | p | | +| smart_pointer.cpp:37:5:37:5 | call to operator* [post update] | smart_pointer.cpp:39:11:39:11 | p | | | smart_pointer.cpp:37:5:37:17 | ... = ... | smart_pointer.cpp:37:5:37:5 | call to operator* [post update] | | -| smart_pointer.cpp:37:6:37:6 | p | smart_pointer.cpp:37:5:37:5 | call to operator* | TAINT | +| smart_pointer.cpp:37:6:37:6 | p | smart_pointer.cpp:37:5:37:5 | call to operator* | | +| smart_pointer.cpp:37:6:37:6 | ref arg p | smart_pointer.cpp:37:6:37:6 | p [inner post update] | | | smart_pointer.cpp:37:6:37:6 | ref arg p | smart_pointer.cpp:38:10:38:10 | p | | | smart_pointer.cpp:37:6:37:6 | ref arg p | smart_pointer.cpp:39:11:39:11 | p | | | smart_pointer.cpp:37:10:37:15 | call to source | smart_pointer.cpp:37:5:37:17 | ... = ... | | | smart_pointer.cpp:38:10:38:10 | ref arg p | smart_pointer.cpp:39:11:39:11 | p | | -| smart_pointer.cpp:39:11:39:11 | p | smart_pointer.cpp:39:10:39:10 | call to operator* | TAINT | +| smart_pointer.cpp:39:11:39:11 | p | smart_pointer.cpp:39:10:39:10 | call to operator* | | | smart_pointer.cpp:43:29:43:51 | call to unique_ptr | smart_pointer.cpp:45:6:45:6 | p | | | smart_pointer.cpp:43:29:43:51 | call to unique_ptr | smart_pointer.cpp:46:10:46:10 | p | | | smart_pointer.cpp:43:29:43:51 | call to unique_ptr | smart_pointer.cpp:47:11:47:11 | p | | +| smart_pointer.cpp:45:5:45:5 | call to operator* [post update] | smart_pointer.cpp:45:6:45:6 | p [inner post update] | | +| smart_pointer.cpp:45:5:45:5 | call to operator* [post update] | smart_pointer.cpp:45:6:45:6 | ref arg p | TAINT | +| smart_pointer.cpp:45:5:45:5 | call to operator* [post update] | smart_pointer.cpp:46:10:46:10 | p | | +| smart_pointer.cpp:45:5:45:5 | call to operator* [post update] | smart_pointer.cpp:47:11:47:11 | p | | | smart_pointer.cpp:45:5:45:17 | ... = ... | smart_pointer.cpp:45:5:45:5 | call to operator* [post update] | | -| smart_pointer.cpp:45:6:45:6 | p | smart_pointer.cpp:45:5:45:5 | call to operator* | TAINT | +| smart_pointer.cpp:45:6:45:6 | p | smart_pointer.cpp:45:5:45:5 | call to operator* | | +| smart_pointer.cpp:45:6:45:6 | ref arg p | smart_pointer.cpp:45:6:45:6 | p [inner post update] | | | smart_pointer.cpp:45:6:45:6 | ref arg p | smart_pointer.cpp:46:10:46:10 | p | | | smart_pointer.cpp:45:6:45:6 | ref arg p | smart_pointer.cpp:47:11:47:11 | p | | | smart_pointer.cpp:45:10:45:15 | call to source | smart_pointer.cpp:45:5:45:17 | ... = ... | | | smart_pointer.cpp:46:10:46:10 | ref arg p | smart_pointer.cpp:47:11:47:11 | p | | -| smart_pointer.cpp:47:11:47:11 | p | smart_pointer.cpp:47:10:47:10 | call to operator* | TAINT | +| smart_pointer.cpp:47:11:47:11 | p | smart_pointer.cpp:47:10:47:10 | call to operator* | | | smart_pointer.cpp:51:30:51:50 | call to make_shared | smart_pointer.cpp:52:10:52:10 | p | | | smart_pointer.cpp:51:52:51:57 | call to source | smart_pointer.cpp:51:30:51:50 | call to make_shared | TAINT | -| smart_pointer.cpp:52:10:52:10 | p | smart_pointer.cpp:52:12:52:14 | call to get | TAINT | +| smart_pointer.cpp:52:10:52:10 | p | smart_pointer.cpp:52:12:52:14 | call to get | | +| smart_pointer.cpp:52:12:52:14 | ref arg call to get | smart_pointer.cpp:52:10:52:10 | ref arg p | TAINT | | smart_pointer.cpp:56:30:56:50 | call to make_unique | smart_pointer.cpp:57:10:57:10 | p | | | smart_pointer.cpp:56:52:56:57 | call to source | smart_pointer.cpp:56:30:56:50 | call to make_unique | TAINT | -| smart_pointer.cpp:57:10:57:10 | p | smart_pointer.cpp:57:12:57:14 | call to get | TAINT | +| smart_pointer.cpp:57:10:57:10 | p | smart_pointer.cpp:57:12:57:14 | call to get | | +| smart_pointer.cpp:57:12:57:14 | ref arg call to get | smart_pointer.cpp:57:10:57:10 | ref arg p | TAINT | | smart_pointer.cpp:65:28:65:46 | call to make_unique | smart_pointer.cpp:66:10:66:10 | p | | | smart_pointer.cpp:65:28:65:46 | call to make_unique | smart_pointer.cpp:67:10:67:10 | p | | | smart_pointer.cpp:65:48:65:53 | call to source | smart_pointer.cpp:65:28:65:46 | call to make_unique | TAINT | | smart_pointer.cpp:65:58:65:58 | 0 | smart_pointer.cpp:65:28:65:46 | call to make_unique | TAINT | +| smart_pointer.cpp:66:10:66:10 | p | smart_pointer.cpp:66:11:66:11 | call to operator-> | | | smart_pointer.cpp:66:10:66:10 | ref arg p | smart_pointer.cpp:67:10:67:10 | p | | +| smart_pointer.cpp:67:10:67:10 | p | smart_pointer.cpp:67:11:67:11 | call to operator-> | | +| smart_pointer.cpp:70:37:70:39 | ptr | smart_pointer.cpp:70:37:70:39 | ptr | | +| smart_pointer.cpp:70:37:70:39 | ptr | smart_pointer.cpp:71:4:71:6 | ptr | | +| smart_pointer.cpp:71:3:71:3 | call to operator* [post update] | smart_pointer.cpp:70:37:70:39 | ptr | | +| smart_pointer.cpp:71:3:71:3 | call to operator* [post update] | smart_pointer.cpp:71:4:71:6 | ptr [inner post update] | | +| smart_pointer.cpp:71:3:71:3 | call to operator* [post update] | smart_pointer.cpp:71:4:71:6 | ref arg ptr | TAINT | +| smart_pointer.cpp:71:3:71:17 | ... = ... | smart_pointer.cpp:71:3:71:3 | call to operator* [post update] | | +| smart_pointer.cpp:71:4:71:6 | ptr | smart_pointer.cpp:71:3:71:3 | call to operator* | | +| smart_pointer.cpp:71:4:71:6 | ref arg ptr | smart_pointer.cpp:70:37:70:39 | ptr | | +| smart_pointer.cpp:71:4:71:6 | ref arg ptr | smart_pointer.cpp:71:4:71:6 | ptr [inner post update] | | +| smart_pointer.cpp:71:10:71:15 | call to source | smart_pointer.cpp:71:3:71:17 | ... = ... | | +| smart_pointer.cpp:75:26:75:33 | call to shared_ptr | smart_pointer.cpp:76:13:76:13 | p | | +| smart_pointer.cpp:75:26:75:33 | call to shared_ptr | smart_pointer.cpp:77:9:77:9 | p | | +| smart_pointer.cpp:76:13:76:13 | p | smart_pointer.cpp:76:13:76:13 | call to shared_ptr | | +| smart_pointer.cpp:76:13:76:13 | ref arg call to shared_ptr | smart_pointer.cpp:76:13:76:13 | p [inner post update] | | +| smart_pointer.cpp:76:13:76:13 | ref arg call to shared_ptr | smart_pointer.cpp:77:9:77:9 | p | | +| smart_pointer.cpp:76:13:76:13 | ref arg p | smart_pointer.cpp:76:13:76:13 | p [inner post update] | | +| smart_pointer.cpp:76:13:76:13 | ref arg p | smart_pointer.cpp:77:9:77:9 | p | | +| smart_pointer.cpp:77:9:77:9 | p | smart_pointer.cpp:77:8:77:8 | call to operator* | | +| smart_pointer.cpp:86:45:86:45 | p | smart_pointer.cpp:86:45:86:45 | p | | +| smart_pointer.cpp:86:45:86:45 | p | smart_pointer.cpp:87:3:87:3 | p | | +| smart_pointer.cpp:86:45:86:45 | p | smart_pointer.cpp:88:8:88:8 | p | | +| smart_pointer.cpp:86:45:86:45 | p | smart_pointer.cpp:89:8:89:8 | p | | +| smart_pointer.cpp:86:67:86:67 | q | smart_pointer.cpp:86:67:86:67 | q | | +| smart_pointer.cpp:86:67:86:67 | q | smart_pointer.cpp:91:3:91:3 | q | | +| smart_pointer.cpp:86:67:86:67 | q | smart_pointer.cpp:92:8:92:8 | q | | +| smart_pointer.cpp:86:67:86:67 | q | smart_pointer.cpp:93:8:93:8 | q | | +| smart_pointer.cpp:86:67:86:67 | q | smart_pointer.cpp:94:8:94:8 | q | | +| smart_pointer.cpp:87:3:87:3 | p | smart_pointer.cpp:87:4:87:4 | call to operator-> | | +| smart_pointer.cpp:87:3:87:3 | ref arg p | smart_pointer.cpp:86:45:86:45 | p | | +| smart_pointer.cpp:87:3:87:3 | ref arg p | smart_pointer.cpp:88:8:88:8 | p | | +| smart_pointer.cpp:87:3:87:3 | ref arg p | smart_pointer.cpp:89:8:89:8 | p | | +| smart_pointer.cpp:87:3:87:17 | ... = ... | smart_pointer.cpp:87:6:87:6 | x [post update] | | +| smart_pointer.cpp:87:3:87:17 | ... = ... | smart_pointer.cpp:88:11:88:11 | x | | +| smart_pointer.cpp:87:4:87:4 | call to operator-> [post update] | smart_pointer.cpp:87:3:87:3 | ref arg p | TAINT | +| smart_pointer.cpp:87:10:87:15 | call to source | smart_pointer.cpp:87:3:87:17 | ... = ... | | +| smart_pointer.cpp:88:8:88:8 | p | smart_pointer.cpp:88:9:88:9 | call to operator-> | | +| smart_pointer.cpp:88:8:88:8 | ref arg p | smart_pointer.cpp:86:45:86:45 | p | | +| smart_pointer.cpp:88:8:88:8 | ref arg p | smart_pointer.cpp:89:8:89:8 | p | | +| smart_pointer.cpp:89:8:89:8 | p | smart_pointer.cpp:89:9:89:9 | call to operator-> | | +| smart_pointer.cpp:89:8:89:8 | ref arg p | smart_pointer.cpp:86:45:86:45 | p | | +| smart_pointer.cpp:91:3:91:3 | q | smart_pointer.cpp:91:4:91:4 | call to operator-> | | +| smart_pointer.cpp:91:3:91:3 | ref arg q | smart_pointer.cpp:86:67:86:67 | q | | +| smart_pointer.cpp:91:3:91:3 | ref arg q | smart_pointer.cpp:92:8:92:8 | q | | +| smart_pointer.cpp:91:3:91:3 | ref arg q | smart_pointer.cpp:93:8:93:8 | q | | +| smart_pointer.cpp:91:3:91:3 | ref arg q | smart_pointer.cpp:94:8:94:8 | q | | +| smart_pointer.cpp:91:3:91:20 | ... = ... | smart_pointer.cpp:91:9:91:9 | x [post update] | | +| smart_pointer.cpp:91:3:91:20 | ... = ... | smart_pointer.cpp:92:14:92:14 | x | | +| smart_pointer.cpp:91:4:91:4 | call to operator-> [post update] | smart_pointer.cpp:91:3:91:3 | ref arg q | TAINT | +| smart_pointer.cpp:91:13:91:18 | call to source | smart_pointer.cpp:91:3:91:20 | ... = ... | | +| smart_pointer.cpp:92:8:92:8 | q | smart_pointer.cpp:92:9:92:9 | call to operator-> | | +| smart_pointer.cpp:92:8:92:8 | ref arg q | smart_pointer.cpp:86:67:86:67 | q | | +| smart_pointer.cpp:92:8:92:8 | ref arg q | smart_pointer.cpp:93:8:93:8 | q | | +| smart_pointer.cpp:92:8:92:8 | ref arg q | smart_pointer.cpp:94:8:94:8 | q | | +| smart_pointer.cpp:93:8:93:8 | q | smart_pointer.cpp:93:9:93:9 | call to operator-> | | +| smart_pointer.cpp:93:8:93:8 | ref arg q | smart_pointer.cpp:86:67:86:67 | q | | +| smart_pointer.cpp:93:8:93:8 | ref arg q | smart_pointer.cpp:94:8:94:8 | q | | +| smart_pointer.cpp:94:8:94:8 | q | smart_pointer.cpp:94:9:94:9 | call to operator-> | | +| smart_pointer.cpp:94:8:94:8 | ref arg q | smart_pointer.cpp:86:67:86:67 | q | | +| smart_pointer.cpp:97:17:97:18 | pa | smart_pointer.cpp:98:5:98:6 | pa | | +| smart_pointer.cpp:98:5:98:20 | ... = ... | smart_pointer.cpp:98:9:98:9 | x [post update] | | +| smart_pointer.cpp:98:13:98:18 | call to source | smart_pointer.cpp:98:5:98:20 | ... = ... | | +| smart_pointer.cpp:102:25:102:50 | call to unique_ptr | smart_pointer.cpp:103:11:103:11 | p | | +| smart_pointer.cpp:102:25:102:50 | call to unique_ptr | smart_pointer.cpp:104:8:104:8 | p | | +| smart_pointer.cpp:103:11:103:11 | p | smart_pointer.cpp:103:13:103:15 | call to get | | +| smart_pointer.cpp:103:11:103:11 | ref arg p | smart_pointer.cpp:104:8:104:8 | p | | +| smart_pointer.cpp:103:13:103:15 | ref arg call to get | smart_pointer.cpp:103:11:103:11 | ref arg p | TAINT | +| smart_pointer.cpp:104:8:104:8 | p | smart_pointer.cpp:104:9:104:9 | call to operator-> | | +| smart_pointer.cpp:112:40:112:42 | ptr | smart_pointer.cpp:112:40:112:42 | ptr | | +| smart_pointer.cpp:112:40:112:42 | ptr | smart_pointer.cpp:113:2:113:4 | ptr | | +| smart_pointer.cpp:113:2:113:4 | ptr | smart_pointer.cpp:113:5:113:5 | call to operator-> | | +| smart_pointer.cpp:113:2:113:4 | ref arg ptr | smart_pointer.cpp:112:40:112:42 | ptr | | +| smart_pointer.cpp:113:2:113:18 | ... = ... | smart_pointer.cpp:113:7:113:7 | x [post update] | | +| smart_pointer.cpp:113:5:113:5 | call to operator-> [post update] | smart_pointer.cpp:113:2:113:4 | ref arg ptr | TAINT | +| smart_pointer.cpp:113:11:113:16 | call to source | smart_pointer.cpp:113:2:113:18 | ... = ... | | +| smart_pointer.cpp:116:52:116:54 | ptr | smart_pointer.cpp:116:52:116:54 | ptr | | +| smart_pointer.cpp:116:52:116:54 | ptr | smart_pointer.cpp:117:2:117:4 | ptr | | +| smart_pointer.cpp:117:2:117:4 | ptr | smart_pointer.cpp:117:5:117:5 | call to operator-> | | +| smart_pointer.cpp:117:2:117:4 | ref arg ptr | smart_pointer.cpp:116:52:116:54 | ptr | | +| smart_pointer.cpp:117:2:117:18 | ... = ... | smart_pointer.cpp:117:7:117:7 | x [post update] | | +| smart_pointer.cpp:117:5:117:5 | call to operator-> [post update] | smart_pointer.cpp:117:2:117:4 | ref arg ptr | TAINT | +| smart_pointer.cpp:117:11:117:16 | call to source | smart_pointer.cpp:117:2:117:18 | ... = ... | | +| smart_pointer.cpp:120:48:120:50 | ptr | smart_pointer.cpp:120:48:120:50 | ptr | | +| smart_pointer.cpp:120:48:120:50 | ptr | smart_pointer.cpp:121:4:121:6 | ptr | | +| smart_pointer.cpp:121:3:121:3 | call to operator* [post update] | smart_pointer.cpp:120:48:120:50 | ptr | | +| smart_pointer.cpp:121:3:121:3 | call to operator* [post update] | smart_pointer.cpp:121:4:121:6 | ptr [inner post update] | | +| smart_pointer.cpp:121:3:121:3 | call to operator* [post update] | smart_pointer.cpp:121:4:121:6 | ref arg ptr | TAINT | +| smart_pointer.cpp:121:3:121:17 | ... = ... | smart_pointer.cpp:121:3:121:3 | call to operator* [post update] | | +| smart_pointer.cpp:121:4:121:6 | ptr | smart_pointer.cpp:121:3:121:3 | call to operator* | | +| smart_pointer.cpp:121:4:121:6 | ref arg ptr | smart_pointer.cpp:120:48:120:50 | ptr | | +| smart_pointer.cpp:121:4:121:6 | ref arg ptr | smart_pointer.cpp:121:4:121:6 | ptr [inner post update] | | +| smart_pointer.cpp:121:10:121:15 | call to source | smart_pointer.cpp:121:3:121:17 | ... = ... | | +| smart_pointer.cpp:124:48:124:49 | p1 | smart_pointer.cpp:124:48:124:49 | p1 | | +| smart_pointer.cpp:124:48:124:49 | p1 | smart_pointer.cpp:125:18:125:19 | p1 | | +| smart_pointer.cpp:124:48:124:49 | p1 | smart_pointer.cpp:126:8:126:9 | p1 | | +| smart_pointer.cpp:124:90:124:91 | p2 | smart_pointer.cpp:124:90:124:91 | p2 | | +| smart_pointer.cpp:124:90:124:91 | p2 | smart_pointer.cpp:128:14:128:15 | p2 | | +| smart_pointer.cpp:124:90:124:91 | p2 | smart_pointer.cpp:129:10:129:11 | p2 | | +| smart_pointer.cpp:125:18:125:19 | p1 | smart_pointer.cpp:125:20:125:20 | call to operator-> | | +| smart_pointer.cpp:125:18:125:19 | ref arg p1 | smart_pointer.cpp:124:48:124:49 | p1 | | +| smart_pointer.cpp:125:18:125:19 | ref arg p1 | smart_pointer.cpp:126:8:126:9 | p1 | | +| smart_pointer.cpp:125:18:125:22 | ref arg call to shared_ptr | smart_pointer.cpp:125:22:125:22 | q [inner post update] | | +| smart_pointer.cpp:125:20:125:20 | call to operator-> [post update] | smart_pointer.cpp:125:18:125:19 | ref arg p1 | TAINT | +| smart_pointer.cpp:125:22:125:22 | q | smart_pointer.cpp:125:18:125:22 | call to shared_ptr | | +| smart_pointer.cpp:125:22:125:22 | ref arg q | smart_pointer.cpp:125:22:125:22 | q [inner post update] | | +| smart_pointer.cpp:126:8:126:9 | p1 | smart_pointer.cpp:126:10:126:10 | call to operator-> | | +| smart_pointer.cpp:126:8:126:9 | ref arg p1 | smart_pointer.cpp:124:48:124:49 | p1 | | +| smart_pointer.cpp:126:10:126:10 | call to operator-> [post update] | smart_pointer.cpp:126:8:126:9 | ref arg p1 | TAINT | +| smart_pointer.cpp:126:12:126:12 | q | smart_pointer.cpp:126:13:126:13 | call to operator-> | | +| smart_pointer.cpp:128:13:128:13 | call to operator* | smart_pointer.cpp:128:13:128:15 | call to shared_ptr | TAINT | +| smart_pointer.cpp:128:13:128:13 | call to operator* [inner post update] | smart_pointer.cpp:128:14:128:15 | ref arg p2 | TAINT | +| smart_pointer.cpp:128:13:128:13 | ref arg call to operator* | smart_pointer.cpp:124:90:124:91 | p2 | | +| smart_pointer.cpp:128:13:128:13 | ref arg call to operator* | smart_pointer.cpp:128:13:128:13 | call to operator* [inner post update] | | +| smart_pointer.cpp:128:13:128:13 | ref arg call to operator* | smart_pointer.cpp:128:14:128:15 | p2 [inner post update] | | +| smart_pointer.cpp:128:13:128:13 | ref arg call to operator* | smart_pointer.cpp:128:14:128:15 | ref arg p2 | TAINT | +| smart_pointer.cpp:128:13:128:13 | ref arg call to operator* | smart_pointer.cpp:129:10:129:11 | p2 | | +| smart_pointer.cpp:128:13:128:15 | ref arg call to shared_ptr | smart_pointer.cpp:124:90:124:91 | p2 | | +| smart_pointer.cpp:128:13:128:15 | ref arg call to shared_ptr | smart_pointer.cpp:128:13:128:13 | call to operator* [inner post update] | | +| smart_pointer.cpp:128:13:128:15 | ref arg call to shared_ptr | smart_pointer.cpp:128:14:128:15 | p2 [inner post update] | | +| smart_pointer.cpp:128:13:128:15 | ref arg call to shared_ptr | smart_pointer.cpp:129:10:129:11 | p2 | | +| smart_pointer.cpp:128:14:128:15 | p2 | smart_pointer.cpp:128:13:128:13 | call to operator* | TAINT | +| smart_pointer.cpp:128:14:128:15 | ref arg p2 | smart_pointer.cpp:124:90:124:91 | p2 | | +| smart_pointer.cpp:128:14:128:15 | ref arg p2 | smart_pointer.cpp:128:14:128:15 | p2 [inner post update] | | +| smart_pointer.cpp:128:14:128:15 | ref arg p2 | smart_pointer.cpp:129:10:129:11 | p2 | | +| smart_pointer.cpp:129:9:129:9 | call to operator* | smart_pointer.cpp:129:8:129:8 | call to operator* | TAINT | +| smart_pointer.cpp:129:9:129:9 | ref arg call to operator* | smart_pointer.cpp:124:90:124:91 | p2 | | +| smart_pointer.cpp:129:9:129:9 | ref arg call to operator* | smart_pointer.cpp:129:10:129:11 | p2 [inner post update] | | +| smart_pointer.cpp:129:9:129:9 | ref arg call to operator* | smart_pointer.cpp:129:10:129:11 | ref arg p2 | TAINT | +| smart_pointer.cpp:129:10:129:11 | p2 | smart_pointer.cpp:129:8:129:8 | call to operator* | | +| smart_pointer.cpp:129:10:129:11 | p2 | smart_pointer.cpp:129:9:129:9 | call to operator* | TAINT | +| smart_pointer.cpp:129:10:129:11 | ref arg p2 | smart_pointer.cpp:124:90:124:91 | p2 | | +| smart_pointer.cpp:129:10:129:11 | ref arg p2 | smart_pointer.cpp:129:10:129:11 | p2 [inner post update] | | +| smart_pointer.cpp:132:53:132:54 | p1 | smart_pointer.cpp:132:53:132:54 | p1 | | +| smart_pointer.cpp:132:53:132:54 | p1 | smart_pointer.cpp:133:23:133:24 | p1 | | +| smart_pointer.cpp:132:53:132:54 | p1 | smart_pointer.cpp:134:8:134:9 | p1 | | +| smart_pointer.cpp:132:95:132:96 | p2 | smart_pointer.cpp:132:95:132:96 | p2 | | +| smart_pointer.cpp:132:95:132:96 | p2 | smart_pointer.cpp:136:18:136:19 | p2 | | +| smart_pointer.cpp:132:95:132:96 | p2 | smart_pointer.cpp:137:10:137:11 | p2 | | +| smart_pointer.cpp:133:23:133:24 | p1 | smart_pointer.cpp:133:25:133:25 | call to operator-> | | +| smart_pointer.cpp:133:23:133:24 | ref arg p1 | smart_pointer.cpp:132:53:132:54 | p1 | | +| smart_pointer.cpp:133:23:133:24 | ref arg p1 | smart_pointer.cpp:134:8:134:9 | p1 | | +| smart_pointer.cpp:133:25:133:25 | call to operator-> [post update] | smart_pointer.cpp:133:23:133:24 | ref arg p1 | TAINT | +| smart_pointer.cpp:134:8:134:9 | p1 | smart_pointer.cpp:134:10:134:10 | call to operator-> | | +| smart_pointer.cpp:134:8:134:9 | ref arg p1 | smart_pointer.cpp:132:53:132:54 | p1 | | +| smart_pointer.cpp:134:10:134:10 | call to operator-> [post update] | smart_pointer.cpp:134:8:134:9 | ref arg p1 | TAINT | +| smart_pointer.cpp:134:12:134:12 | q | smart_pointer.cpp:134:13:134:13 | call to operator-> | | +| smart_pointer.cpp:136:17:136:17 | ref arg call to operator* | smart_pointer.cpp:132:95:132:96 | p2 | | +| smart_pointer.cpp:136:17:136:17 | ref arg call to operator* | smart_pointer.cpp:136:18:136:19 | p2 [inner post update] | | +| smart_pointer.cpp:136:17:136:17 | ref arg call to operator* | smart_pointer.cpp:136:18:136:19 | ref arg p2 | TAINT | +| smart_pointer.cpp:136:17:136:17 | ref arg call to operator* | smart_pointer.cpp:137:10:137:11 | p2 | | +| smart_pointer.cpp:136:18:136:19 | p2 | smart_pointer.cpp:136:17:136:17 | call to operator* | TAINT | +| smart_pointer.cpp:136:18:136:19 | ref arg p2 | smart_pointer.cpp:132:95:132:96 | p2 | | +| smart_pointer.cpp:136:18:136:19 | ref arg p2 | smart_pointer.cpp:136:18:136:19 | p2 [inner post update] | | +| smart_pointer.cpp:136:18:136:19 | ref arg p2 | smart_pointer.cpp:137:10:137:11 | p2 | | +| smart_pointer.cpp:137:9:137:9 | call to operator* | smart_pointer.cpp:137:8:137:8 | call to operator* | TAINT | +| smart_pointer.cpp:137:9:137:9 | ref arg call to operator* | smart_pointer.cpp:132:95:132:96 | p2 | | +| smart_pointer.cpp:137:9:137:9 | ref arg call to operator* | smart_pointer.cpp:137:10:137:11 | p2 [inner post update] | | +| smart_pointer.cpp:137:9:137:9 | ref arg call to operator* | smart_pointer.cpp:137:10:137:11 | ref arg p2 | TAINT | +| smart_pointer.cpp:137:10:137:11 | p2 | smart_pointer.cpp:137:8:137:8 | call to operator* | | +| smart_pointer.cpp:137:10:137:11 | p2 | smart_pointer.cpp:137:9:137:9 | call to operator* | TAINT | +| smart_pointer.cpp:137:10:137:11 | ref arg p2 | smart_pointer.cpp:132:95:132:96 | p2 | | +| smart_pointer.cpp:137:10:137:11 | ref arg p2 | smart_pointer.cpp:137:10:137:11 | p2 [inner post update] | | | standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:39:45:39:51 | source1 | | | standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:40:11:40:17 | source1 | | | standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:41:12:41:18 | source1 | | @@ -3360,13 +3543,13 @@ | standalone_iterators.cpp:116:10:116:14 | call to begin | standalone_iterators.cpp:120:2:120:3 | it | | | standalone_iterators.cpp:116:10:116:14 | call to begin | standalone_iterators.cpp:121:7:121:8 | it | | | standalone_iterators.cpp:117:7:117:8 | it [post update] | standalone_iterators.cpp:122:7:122:8 | c1 | | -| standalone_iterators.cpp:118:2:118:3 | it | standalone_iterators.cpp:118:5:118:5 | call to operator+= | | +| standalone_iterators.cpp:118:2:118:3 | it | standalone_iterators.cpp:118:5:118:5 | call to operator+= | TAINT | | standalone_iterators.cpp:118:2:118:3 | ref arg it | standalone_iterators.cpp:119:7:119:8 | it | | | standalone_iterators.cpp:118:2:118:3 | ref arg it | standalone_iterators.cpp:120:2:120:3 | it | | | standalone_iterators.cpp:118:2:118:3 | ref arg it | standalone_iterators.cpp:121:7:121:8 | it | | | standalone_iterators.cpp:118:2:118:3 | ref arg it | standalone_iterators.cpp:122:7:122:8 | c1 | | | standalone_iterators.cpp:118:8:118:8 | 1 | standalone_iterators.cpp:118:2:118:3 | ref arg it | TAINT | -| standalone_iterators.cpp:120:2:120:3 | it | standalone_iterators.cpp:120:5:120:5 | call to operator+= | | +| standalone_iterators.cpp:120:2:120:3 | it | standalone_iterators.cpp:120:5:120:5 | call to operator+= | TAINT | | standalone_iterators.cpp:120:2:120:3 | ref arg it | standalone_iterators.cpp:121:7:121:8 | it | | | standalone_iterators.cpp:120:8:120:13 | call to source | standalone_iterators.cpp:120:2:120:3 | ref arg it | TAINT | | stl.h:75:8:75:8 | Unknown literal | stl.h:75:8:75:8 | constructor init of field container | TAINT | @@ -3388,125 +3571,125 @@ | stl.h:292:30:292:40 | call to allocator | stl.h:292:21:292:41 | noexcept(...) | TAINT | | stl.h:292:30:292:40 | call to allocator | stl.h:292:21:292:41 | noexcept(...) | TAINT | | stl.h:292:53:292:63 | 0 | stl.h:292:46:292:64 | (no string representation) | TAINT | -| stl.h:388:9:388:9 | Unknown literal | stl.h:388:9:388:9 | constructor init of field first | TAINT | -| stl.h:388:9:388:9 | Unknown literal | stl.h:388:9:388:9 | constructor init of field first | TAINT | -| stl.h:388:9:388:9 | Unknown literal | stl.h:388:9:388:9 | constructor init of field first | TAINT | -| stl.h:388:9:388:9 | Unknown literal | stl.h:388:9:388:9 | constructor init of field first | TAINT | -| stl.h:388:9:388:9 | Unknown literal | stl.h:388:9:388:9 | constructor init of field first | TAINT | -| stl.h:388:9:388:9 | Unknown literal | stl.h:388:9:388:9 | constructor init of field second | TAINT | -| stl.h:388:9:388:9 | Unknown literal | stl.h:388:9:388:9 | constructor init of field second | TAINT | -| stl.h:388:9:388:9 | Unknown literal | stl.h:388:9:388:9 | constructor init of field second | TAINT | -| stl.h:388:9:388:9 | Unknown literal | stl.h:388:9:388:9 | constructor init of field second | TAINT | -| stl.h:388:9:388:9 | Unknown literal | stl.h:388:9:388:9 | constructor init of field second | TAINT | -| stl.h:388:9:388:9 | constructor init of field first [post-this] | stl.h:388:9:388:9 | constructor init of field second [pre-this] | | -| stl.h:388:9:388:9 | constructor init of field first [post-this] | stl.h:388:9:388:9 | constructor init of field second [pre-this] | | -| stl.h:388:9:388:9 | constructor init of field first [post-this] | stl.h:388:9:388:9 | constructor init of field second [pre-this] | | -| stl.h:388:9:388:9 | constructor init of field first [post-this] | stl.h:388:9:388:9 | constructor init of field second [pre-this] | | -| stl.h:388:9:388:9 | constructor init of field first [post-this] | stl.h:388:9:388:9 | constructor init of field second [pre-this] | | -| stl.h:388:9:388:9 | constructor init of field first [pre-this] | stl.h:388:9:388:9 | constructor init of field second [pre-this] | | -| stl.h:388:9:388:9 | constructor init of field first [pre-this] | stl.h:388:9:388:9 | constructor init of field second [pre-this] | | -| stl.h:388:9:388:9 | constructor init of field first [pre-this] | stl.h:388:9:388:9 | constructor init of field second [pre-this] | | -| stl.h:388:9:388:9 | constructor init of field first [pre-this] | stl.h:388:9:388:9 | constructor init of field second [pre-this] | | -| stl.h:388:9:388:9 | constructor init of field first [pre-this] | stl.h:388:9:388:9 | constructor init of field second [pre-this] | | -| stl.h:388:9:388:9 | this | stl.h:388:9:388:9 | constructor init of field first [pre-this] | | -| stl.h:388:9:388:9 | this | stl.h:388:9:388:9 | constructor init of field first [pre-this] | | -| stl.h:388:9:388:9 | this | stl.h:388:9:388:9 | constructor init of field first [pre-this] | | -| stl.h:388:9:388:9 | this | stl.h:388:9:388:9 | constructor init of field first [pre-this] | | -| stl.h:388:9:388:9 | this | stl.h:388:9:388:9 | constructor init of field first [pre-this] | | -| stl.h:395:3:395:3 | this | stl.h:395:36:395:43 | constructor init of field first [pre-this] | | -| stl.h:395:3:395:3 | this | stl.h:395:36:395:43 | constructor init of field first [pre-this] | | -| stl.h:395:3:395:3 | this | stl.h:395:36:395:43 | constructor init of field first [pre-this] | | -| stl.h:395:3:395:3 | this | stl.h:395:36:395:43 | constructor init of field first [pre-this] | | -| stl.h:395:3:395:3 | this | stl.h:395:36:395:43 | constructor init of field first [pre-this] | | -| stl.h:395:3:395:6 | this | stl.h:395:36:395:43 | constructor init of field first [pre-this] | | -| stl.h:395:18:395:18 | x | stl.h:395:42:395:42 | x | | -| stl.h:395:18:395:18 | x | stl.h:395:42:395:42 | x | | -| stl.h:395:18:395:18 | x | stl.h:395:42:395:42 | x | | -| stl.h:395:18:395:18 | x | stl.h:395:42:395:42 | x | | -| stl.h:395:18:395:18 | x | stl.h:395:42:395:42 | x | | -| stl.h:395:18:395:18 | x | stl.h:395:42:395:42 | x | | -| stl.h:395:31:395:31 | y | stl.h:395:53:395:53 | y | | -| stl.h:395:31:395:31 | y | stl.h:395:53:395:53 | y | | -| stl.h:395:31:395:31 | y | stl.h:395:53:395:53 | y | | -| stl.h:395:31:395:31 | y | stl.h:395:53:395:53 | y | | -| stl.h:395:31:395:31 | y | stl.h:395:53:395:53 | y | | -| stl.h:395:31:395:31 | y | stl.h:395:53:395:53 | y | | -| stl.h:395:36:395:43 | call to unknown function | stl.h:395:36:395:43 | constructor init of field first | TAINT | -| stl.h:395:36:395:43 | constructor init of field first [post-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [post-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [post-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [post-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [post-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [post-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [pre-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [pre-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [pre-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [pre-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [pre-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:36:395:43 | constructor init of field first [pre-this] | stl.h:395:46:395:54 | constructor init of field second [pre-this] | | -| stl.h:395:42:395:42 | x | stl.h:395:36:395:43 | constructor init of field first | TAINT | -| stl.h:395:42:395:42 | x | stl.h:395:36:395:43 | constructor init of field first | TAINT | -| stl.h:395:42:395:42 | x | stl.h:395:36:395:43 | constructor init of field first | TAINT | -| stl.h:395:42:395:42 | x | stl.h:395:36:395:43 | constructor init of field first | TAINT | -| stl.h:395:42:395:42 | x | stl.h:395:36:395:43 | constructor init of field first | TAINT | -| stl.h:395:46:395:54 | call to unknown function | stl.h:395:46:395:54 | constructor init of field second | TAINT | -| stl.h:395:53:395:53 | y | stl.h:395:46:395:54 | constructor init of field second | TAINT | -| stl.h:395:53:395:53 | y | stl.h:395:46:395:54 | constructor init of field second | TAINT | -| stl.h:395:53:395:53 | y | stl.h:395:46:395:54 | constructor init of field second | TAINT | -| stl.h:395:53:395:53 | y | stl.h:395:46:395:54 | constructor init of field second | TAINT | -| stl.h:395:53:395:53 | y | stl.h:395:46:395:54 | constructor init of field second | TAINT | -| stl.h:401:87:401:87 | x | stl.h:401:87:401:87 | x | | -| stl.h:401:87:401:87 | x | stl.h:401:87:401:87 | x | | -| stl.h:401:87:401:87 | x | stl.h:401:87:401:87 | x | | -| stl.h:401:87:401:87 | x | stl.h:401:87:401:87 | x | | -| stl.h:401:87:401:87 | x | stl.h:401:87:401:87 | x | | -| stl.h:401:87:401:87 | x | stl.h:401:87:401:87 | x | | -| stl.h:401:87:401:87 | x | stl.h:401:87:401:87 | x | | -| stl.h:401:87:401:87 | x | stl.h:402:58:402:58 | x | | -| stl.h:401:87:401:87 | x | stl.h:402:58:402:58 | x | | -| stl.h:401:87:401:87 | x | stl.h:402:58:402:58 | x | | -| stl.h:401:87:401:87 | x | stl.h:402:58:402:58 | x | | -| stl.h:401:87:401:87 | x | stl.h:402:58:402:58 | x | | -| stl.h:401:87:401:87 | x | stl.h:402:58:402:58 | x | | -| stl.h:401:87:401:87 | x | stl.h:402:58:402:58 | x | | -| stl.h:401:95:401:95 | y | stl.h:401:95:401:95 | y | | -| stl.h:401:95:401:95 | y | stl.h:401:95:401:95 | y | | -| stl.h:401:95:401:95 | y | stl.h:401:95:401:95 | y | | -| stl.h:401:95:401:95 | y | stl.h:401:95:401:95 | y | | -| stl.h:401:95:401:95 | y | stl.h:401:95:401:95 | y | | -| stl.h:401:95:401:95 | y | stl.h:401:95:401:95 | y | | -| stl.h:401:95:401:95 | y | stl.h:401:95:401:95 | y | | -| stl.h:401:95:401:95 | y | stl.h:402:79:402:79 | y | | -| stl.h:401:95:401:95 | y | stl.h:402:79:402:79 | y | | -| stl.h:401:95:401:95 | y | stl.h:402:79:402:79 | y | | -| stl.h:401:95:401:95 | y | stl.h:402:79:402:79 | y | | -| stl.h:401:95:401:95 | y | stl.h:402:79:402:79 | y | | -| stl.h:401:95:401:95 | y | stl.h:402:79:402:79 | y | | -| stl.h:401:95:401:95 | y | stl.h:402:79:402:79 | y | | -| stl.h:402:58:402:58 | x | stl.h:402:41:402:56 | call to forward | | -| stl.h:402:58:402:58 | x | stl.h:402:41:402:56 | call to forward | | -| stl.h:402:58:402:58 | x | stl.h:402:41:402:56 | call to forward | | -| stl.h:402:58:402:58 | x | stl.h:402:41:402:56 | call to forward | | -| stl.h:402:58:402:58 | x | stl.h:402:41:402:56 | call to forward | | -| stl.h:402:58:402:58 | x | stl.h:402:41:402:56 | call to forward | | -| stl.h:402:62:402:77 | call to forward | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:62:402:77 | call to forward | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:62:402:77 | call to forward | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:62:402:77 | call to forward | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:62:402:77 | call to forward | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:62:402:77 | call to forward | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:79:402:79 | y | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:79:402:79 | y | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:79:402:79 | y | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:79:402:79 | y | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:79:402:79 | y | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:79:402:79 | y | stl.h:402:3:402:82 | call to pair | TAINT | -| stl.h:402:79:402:79 | y | stl.h:402:62:402:77 | call to forward | | -| stl.h:402:79:402:79 | y | stl.h:402:62:402:77 | call to forward | | -| stl.h:402:79:402:79 | y | stl.h:402:62:402:77 | call to forward | | -| stl.h:402:79:402:79 | y | stl.h:402:62:402:77 | call to forward | | -| stl.h:402:79:402:79 | y | stl.h:402:62:402:77 | call to forward | | -| stl.h:402:79:402:79 | y | stl.h:402:62:402:77 | call to forward | | +| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field first | TAINT | +| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field first | TAINT | +| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field first | TAINT | +| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field first | TAINT | +| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field first | TAINT | +| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field second | TAINT | +| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field second | TAINT | +| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field second | TAINT | +| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field second | TAINT | +| stl.h:389:9:389:9 | Unknown literal | stl.h:389:9:389:9 | constructor init of field second | TAINT | +| stl.h:389:9:389:9 | constructor init of field first [post-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | | +| stl.h:389:9:389:9 | constructor init of field first [post-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | | +| stl.h:389:9:389:9 | constructor init of field first [post-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | | +| stl.h:389:9:389:9 | constructor init of field first [post-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | | +| stl.h:389:9:389:9 | constructor init of field first [post-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | | +| stl.h:389:9:389:9 | constructor init of field first [pre-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | | +| stl.h:389:9:389:9 | constructor init of field first [pre-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | | +| stl.h:389:9:389:9 | constructor init of field first [pre-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | | +| stl.h:389:9:389:9 | constructor init of field first [pre-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | | +| stl.h:389:9:389:9 | constructor init of field first [pre-this] | stl.h:389:9:389:9 | constructor init of field second [pre-this] | | +| stl.h:389:9:389:9 | this | stl.h:389:9:389:9 | constructor init of field first [pre-this] | | +| stl.h:389:9:389:9 | this | stl.h:389:9:389:9 | constructor init of field first [pre-this] | | +| stl.h:389:9:389:9 | this | stl.h:389:9:389:9 | constructor init of field first [pre-this] | | +| stl.h:389:9:389:9 | this | stl.h:389:9:389:9 | constructor init of field first [pre-this] | | +| stl.h:389:9:389:9 | this | stl.h:389:9:389:9 | constructor init of field first [pre-this] | | +| stl.h:396:3:396:3 | this | stl.h:396:36:396:43 | constructor init of field first [pre-this] | | +| stl.h:396:3:396:3 | this | stl.h:396:36:396:43 | constructor init of field first [pre-this] | | +| stl.h:396:3:396:3 | this | stl.h:396:36:396:43 | constructor init of field first [pre-this] | | +| stl.h:396:3:396:3 | this | stl.h:396:36:396:43 | constructor init of field first [pre-this] | | +| stl.h:396:3:396:3 | this | stl.h:396:36:396:43 | constructor init of field first [pre-this] | | +| stl.h:396:3:396:6 | this | stl.h:396:36:396:43 | constructor init of field first [pre-this] | | +| stl.h:396:18:396:18 | x | stl.h:396:42:396:42 | x | | +| stl.h:396:18:396:18 | x | stl.h:396:42:396:42 | x | | +| stl.h:396:18:396:18 | x | stl.h:396:42:396:42 | x | | +| stl.h:396:18:396:18 | x | stl.h:396:42:396:42 | x | | +| stl.h:396:18:396:18 | x | stl.h:396:42:396:42 | x | | +| stl.h:396:18:396:18 | x | stl.h:396:42:396:42 | x | | +| stl.h:396:31:396:31 | y | stl.h:396:53:396:53 | y | | +| stl.h:396:31:396:31 | y | stl.h:396:53:396:53 | y | | +| stl.h:396:31:396:31 | y | stl.h:396:53:396:53 | y | | +| stl.h:396:31:396:31 | y | stl.h:396:53:396:53 | y | | +| stl.h:396:31:396:31 | y | stl.h:396:53:396:53 | y | | +| stl.h:396:31:396:31 | y | stl.h:396:53:396:53 | y | | +| stl.h:396:36:396:43 | call to unknown function | stl.h:396:36:396:43 | constructor init of field first | TAINT | +| stl.h:396:36:396:43 | constructor init of field first [post-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [post-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [post-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [post-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [post-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [post-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [pre-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [pre-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [pre-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [pre-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [pre-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:36:396:43 | constructor init of field first [pre-this] | stl.h:396:46:396:54 | constructor init of field second [pre-this] | | +| stl.h:396:42:396:42 | x | stl.h:396:36:396:43 | constructor init of field first | TAINT | +| stl.h:396:42:396:42 | x | stl.h:396:36:396:43 | constructor init of field first | TAINT | +| stl.h:396:42:396:42 | x | stl.h:396:36:396:43 | constructor init of field first | TAINT | +| stl.h:396:42:396:42 | x | stl.h:396:36:396:43 | constructor init of field first | TAINT | +| stl.h:396:42:396:42 | x | stl.h:396:36:396:43 | constructor init of field first | TAINT | +| stl.h:396:46:396:54 | call to unknown function | stl.h:396:46:396:54 | constructor init of field second | TAINT | +| stl.h:396:53:396:53 | y | stl.h:396:46:396:54 | constructor init of field second | TAINT | +| stl.h:396:53:396:53 | y | stl.h:396:46:396:54 | constructor init of field second | TAINT | +| stl.h:396:53:396:53 | y | stl.h:396:46:396:54 | constructor init of field second | TAINT | +| stl.h:396:53:396:53 | y | stl.h:396:46:396:54 | constructor init of field second | TAINT | +| stl.h:396:53:396:53 | y | stl.h:396:46:396:54 | constructor init of field second | TAINT | +| stl.h:402:87:402:87 | x | stl.h:402:87:402:87 | x | | +| stl.h:402:87:402:87 | x | stl.h:402:87:402:87 | x | | +| stl.h:402:87:402:87 | x | stl.h:402:87:402:87 | x | | +| stl.h:402:87:402:87 | x | stl.h:402:87:402:87 | x | | +| stl.h:402:87:402:87 | x | stl.h:402:87:402:87 | x | | +| stl.h:402:87:402:87 | x | stl.h:402:87:402:87 | x | | +| stl.h:402:87:402:87 | x | stl.h:402:87:402:87 | x | | +| stl.h:402:87:402:87 | x | stl.h:403:58:403:58 | x | | +| stl.h:402:87:402:87 | x | stl.h:403:58:403:58 | x | | +| stl.h:402:87:402:87 | x | stl.h:403:58:403:58 | x | | +| stl.h:402:87:402:87 | x | stl.h:403:58:403:58 | x | | +| stl.h:402:87:402:87 | x | stl.h:403:58:403:58 | x | | +| stl.h:402:87:402:87 | x | stl.h:403:58:403:58 | x | | +| stl.h:402:87:402:87 | x | stl.h:403:58:403:58 | x | | +| stl.h:402:95:402:95 | y | stl.h:402:95:402:95 | y | | +| stl.h:402:95:402:95 | y | stl.h:402:95:402:95 | y | | +| stl.h:402:95:402:95 | y | stl.h:402:95:402:95 | y | | +| stl.h:402:95:402:95 | y | stl.h:402:95:402:95 | y | | +| stl.h:402:95:402:95 | y | stl.h:402:95:402:95 | y | | +| stl.h:402:95:402:95 | y | stl.h:402:95:402:95 | y | | +| stl.h:402:95:402:95 | y | stl.h:402:95:402:95 | y | | +| stl.h:402:95:402:95 | y | stl.h:403:79:403:79 | y | | +| stl.h:402:95:402:95 | y | stl.h:403:79:403:79 | y | | +| stl.h:402:95:402:95 | y | stl.h:403:79:403:79 | y | | +| stl.h:402:95:402:95 | y | stl.h:403:79:403:79 | y | | +| stl.h:402:95:402:95 | y | stl.h:403:79:403:79 | y | | +| stl.h:402:95:402:95 | y | stl.h:403:79:403:79 | y | | +| stl.h:402:95:402:95 | y | stl.h:403:79:403:79 | y | | +| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT | +| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT | +| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT | +| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT | +| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT | +| stl.h:403:58:403:58 | x | stl.h:403:41:403:56 | call to forward | TAINT | +| stl.h:403:62:403:77 | call to forward | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:62:403:77 | call to forward | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:62:403:77 | call to forward | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:62:403:77 | call to forward | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:62:403:77 | call to forward | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:62:403:77 | call to forward | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:3:403:82 | call to pair | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT | +| stl.h:403:79:403:79 | y | stl.h:403:62:403:77 | call to forward | TAINT | | string.cpp:25:12:25:17 | call to source | string.cpp:29:7:29:7 | a | | | string.cpp:26:16:26:20 | 123 | string.cpp:26:16:26:21 | call to basic_string | TAINT | | string.cpp:26:16:26:21 | call to basic_string | string.cpp:30:7:30:7 | b | | @@ -4059,13 +4242,13 @@ | string.cpp:407:9:407:10 | i6 | string.cpp:407:8:407:8 | call to operator* | TAINT | | string.cpp:408:8:408:9 | i2 | string.cpp:408:3:408:9 | ... = ... | | | string.cpp:408:8:408:9 | i2 | string.cpp:409:10:409:11 | i7 | | -| string.cpp:409:10:409:11 | i7 | string.cpp:409:12:409:12 | call to operator+= | | +| string.cpp:409:10:409:11 | i7 | string.cpp:409:12:409:12 | call to operator+= | TAINT | | string.cpp:409:12:409:12 | call to operator+= | string.cpp:409:8:409:8 | call to operator* | TAINT | | string.cpp:409:12:409:12 | ref arg call to operator+= | string.cpp:409:10:409:11 | ref arg i7 | TAINT | | string.cpp:409:14:409:14 | 1 | string.cpp:409:10:409:11 | ref arg i7 | TAINT | | string.cpp:410:8:410:9 | i2 | string.cpp:410:3:410:9 | ... = ... | | | string.cpp:410:8:410:9 | i2 | string.cpp:411:10:411:11 | i8 | | -| string.cpp:411:10:411:11 | i8 | string.cpp:411:12:411:12 | call to operator-= | | +| string.cpp:411:10:411:11 | i8 | string.cpp:411:12:411:12 | call to operator-= | TAINT | | string.cpp:411:12:411:12 | call to operator-= | string.cpp:411:8:411:8 | call to operator* | TAINT | | string.cpp:411:12:411:12 | ref arg call to operator-= | string.cpp:411:10:411:11 | ref arg i8 | TAINT | | string.cpp:411:14:411:14 | 1 | string.cpp:411:10:411:11 | ref arg i8 | TAINT | @@ -4480,7 +4663,7 @@ | stringstream.cpp:33:7:33:9 | ref arg ss3 | stringstream.cpp:39:7:39:9 | ss3 | | | stringstream.cpp:33:7:33:9 | ref arg ss3 | stringstream.cpp:44:7:44:9 | ss3 | | | stringstream.cpp:33:7:33:9 | ss3 | stringstream.cpp:33:11:33:11 | call to operator<< | | -| stringstream.cpp:33:11:33:11 | call to operator<< | stringstream.cpp:33:20:33:20 | call to operator<< | | +| stringstream.cpp:33:11:33:11 | call to operator<< | stringstream.cpp:33:20:33:20 | call to operator<< | TAINT | | stringstream.cpp:33:11:33:11 | ref arg call to operator<< | stringstream.cpp:33:7:33:9 | ref arg ss3 | TAINT | | stringstream.cpp:33:14:33:18 | 123 | stringstream.cpp:33:7:33:9 | ref arg ss3 | TAINT | | stringstream.cpp:33:14:33:18 | 123 | stringstream.cpp:33:11:33:11 | call to operator<< | TAINT | @@ -4489,7 +4672,7 @@ | stringstream.cpp:34:7:34:9 | ref arg ss4 | stringstream.cpp:40:7:40:9 | ss4 | | | stringstream.cpp:34:7:34:9 | ref arg ss4 | stringstream.cpp:45:7:45:9 | ss4 | | | stringstream.cpp:34:7:34:9 | ss4 | stringstream.cpp:34:11:34:11 | call to operator<< | | -| stringstream.cpp:34:11:34:11 | call to operator<< | stringstream.cpp:34:23:34:23 | call to operator<< | | +| stringstream.cpp:34:11:34:11 | call to operator<< | stringstream.cpp:34:23:34:23 | call to operator<< | TAINT | | stringstream.cpp:34:11:34:11 | ref arg call to operator<< | stringstream.cpp:34:7:34:9 | ref arg ss4 | TAINT | | stringstream.cpp:34:14:34:19 | call to source | stringstream.cpp:34:7:34:9 | ref arg ss4 | TAINT | | stringstream.cpp:34:14:34:19 | call to source | stringstream.cpp:34:11:34:11 | call to operator<< | TAINT | @@ -4770,7 +4953,7 @@ | stringstream.cpp:147:7:147:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | | | stringstream.cpp:147:7:147:9 | ss2 | stringstream.cpp:147:11:147:11 | call to operator>> | | | stringstream.cpp:147:7:147:9 | ss2 | stringstream.cpp:147:14:147:15 | ref arg s3 | TAINT | -| stringstream.cpp:147:11:147:11 | call to operator>> | stringstream.cpp:147:17:147:17 | call to operator>> | | +| stringstream.cpp:147:11:147:11 | call to operator>> | stringstream.cpp:147:17:147:17 | call to operator>> | TAINT | | stringstream.cpp:147:11:147:11 | call to operator>> | stringstream.cpp:147:20:147:21 | ref arg s4 | TAINT | | stringstream.cpp:147:11:147:11 | ref arg call to operator>> | stringstream.cpp:147:7:147:9 | ref arg ss2 | TAINT | | stringstream.cpp:147:14:147:15 | ref arg s3 | stringstream.cpp:150:7:150:8 | s3 | | @@ -4802,7 +4985,7 @@ | stringstream.cpp:155:7:155:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | | | stringstream.cpp:155:7:155:9 | ss2 | stringstream.cpp:155:11:155:11 | call to operator>> | | | stringstream.cpp:155:7:155:9 | ss2 | stringstream.cpp:155:14:155:15 | ref arg b3 | TAINT | -| stringstream.cpp:155:11:155:11 | call to operator>> | stringstream.cpp:155:17:155:17 | call to operator>> | | +| stringstream.cpp:155:11:155:11 | call to operator>> | stringstream.cpp:155:17:155:17 | call to operator>> | TAINT | | stringstream.cpp:155:11:155:11 | call to operator>> | stringstream.cpp:155:20:155:21 | ref arg b4 | TAINT | | stringstream.cpp:155:11:155:11 | ref arg call to operator>> | stringstream.cpp:155:7:155:9 | ref arg ss2 | TAINT | | stringstream.cpp:155:14:155:15 | ref arg b3 | stringstream.cpp:158:7:158:8 | b3 | | @@ -5124,7 +5307,7 @@ | stringstream.cpp:245:15:245:17 | ss1 | stringstream.cpp:245:7:245:13 | call to getline | | | stringstream.cpp:245:15:245:17 | ss1 | stringstream.cpp:245:20:245:21 | ref arg s6 | TAINT | | stringstream.cpp:245:20:245:21 | ref arg s6 | stringstream.cpp:248:7:248:8 | s6 | | -| stringstream.cpp:250:15:250:21 | call to getline | stringstream.cpp:250:7:250:13 | call to getline | | +| stringstream.cpp:250:15:250:21 | call to getline | stringstream.cpp:250:7:250:13 | call to getline | TAINT | | stringstream.cpp:250:15:250:21 | call to getline | stringstream.cpp:250:33:250:34 | ref arg s8 | TAINT | | stringstream.cpp:250:15:250:21 | ref arg call to getline | stringstream.cpp:250:23:250:25 | ref arg ss2 | TAINT | | stringstream.cpp:250:23:250:25 | ss2 | stringstream.cpp:250:15:250:21 | call to getline | | @@ -5328,7 +5511,7 @@ | swap1.cpp:100:9:100:17 | ref arg call to move | swap1.cpp:103:10:103:10 | x | | | swap1.cpp:100:19:100:19 | x | swap1.cpp:100:5:100:5 | ref arg y | TAINT | | swap1.cpp:100:19:100:19 | x | swap1.cpp:100:7:100:7 | call to operator= | TAINT | -| swap1.cpp:100:19:100:19 | x | swap1.cpp:100:9:100:17 | call to move | | +| swap1.cpp:100:19:100:19 | x | swap1.cpp:100:9:100:17 | call to move | TAINT | | swap1.cpp:108:23:108:31 | move_from | swap1.cpp:109:5:109:13 | move_from | | | swap1.cpp:108:23:108:31 | move_from | swap1.cpp:111:10:111:18 | move_from | | | swap1.cpp:108:23:108:31 | move_from | swap1.cpp:113:41:113:49 | move_from | | @@ -5341,7 +5524,7 @@ | swap1.cpp:113:31:113:39 | call to move | swap1.cpp:113:31:113:51 | call to Class | TAINT | | swap1.cpp:113:31:113:39 | ref arg call to move | swap1.cpp:113:41:113:49 | move_from [inner post update] | | | swap1.cpp:113:31:113:51 | call to Class | swap1.cpp:115:10:115:16 | move_to | | -| swap1.cpp:113:41:113:49 | move_from | swap1.cpp:113:31:113:39 | call to move | | +| swap1.cpp:113:41:113:49 | move_from | swap1.cpp:113:31:113:39 | call to move | TAINT | | swap1.cpp:113:41:113:49 | move_from | swap1.cpp:113:31:113:51 | call to Class | | | swap1.cpp:120:23:120:23 | x | swap1.cpp:122:5:122:5 | x | | | swap1.cpp:120:23:120:23 | x | swap1.cpp:124:10:124:10 | x | | @@ -5375,7 +5558,7 @@ | swap1.cpp:142:5:142:5 | ref arg y | swap1.cpp:144:10:144:10 | y | | | swap1.cpp:142:19:142:27 | ref arg call to move | swap1.cpp:142:29:142:29 | x [inner post update] | | | swap1.cpp:142:19:142:27 | ref arg call to move | swap1.cpp:145:10:145:10 | x | | -| swap1.cpp:142:29:142:29 | x | swap1.cpp:142:19:142:27 | call to move | | +| swap1.cpp:142:29:142:29 | x | swap1.cpp:142:19:142:27 | call to move | TAINT | | swap2.cpp:14:17:14:17 | t | swap2.cpp:14:17:14:17 | t | | | swap2.cpp:14:17:14:17 | t | swap2.cpp:14:17:14:17 | t | | | swap2.cpp:14:17:14:17 | t | swap2.cpp:14:56:14:56 | t | | @@ -5507,7 +5690,7 @@ | swap2.cpp:100:9:100:17 | ref arg call to move | swap2.cpp:103:10:103:10 | x | | | swap2.cpp:100:19:100:19 | x | swap2.cpp:100:5:100:5 | ref arg y | TAINT | | swap2.cpp:100:19:100:19 | x | swap2.cpp:100:7:100:7 | call to operator= | TAINT | -| swap2.cpp:100:19:100:19 | x | swap2.cpp:100:9:100:17 | call to move | | +| swap2.cpp:100:19:100:19 | x | swap2.cpp:100:9:100:17 | call to move | TAINT | | swap2.cpp:108:23:108:31 | move_from | swap2.cpp:109:5:109:13 | move_from | | | swap2.cpp:108:23:108:31 | move_from | swap2.cpp:111:10:111:18 | move_from | | | swap2.cpp:108:23:108:31 | move_from | swap2.cpp:113:41:113:49 | move_from | | @@ -5520,7 +5703,7 @@ | swap2.cpp:113:31:113:39 | call to move | swap2.cpp:113:31:113:51 | call to Class | TAINT | | swap2.cpp:113:31:113:39 | ref arg call to move | swap2.cpp:113:41:113:49 | move_from [inner post update] | | | swap2.cpp:113:31:113:51 | call to Class | swap2.cpp:115:10:115:16 | move_to | | -| swap2.cpp:113:41:113:49 | move_from | swap2.cpp:113:31:113:39 | call to move | | +| swap2.cpp:113:41:113:49 | move_from | swap2.cpp:113:31:113:39 | call to move | TAINT | | swap2.cpp:113:41:113:49 | move_from | swap2.cpp:113:31:113:51 | call to Class | | | swap2.cpp:120:23:120:23 | x | swap2.cpp:122:5:122:5 | x | | | swap2.cpp:120:23:120:23 | x | swap2.cpp:124:10:124:10 | x | | @@ -5554,7 +5737,7 @@ | swap2.cpp:142:5:142:5 | ref arg y | swap2.cpp:144:10:144:10 | y | | | swap2.cpp:142:19:142:27 | ref arg call to move | swap2.cpp:142:29:142:29 | x [inner post update] | | | swap2.cpp:142:19:142:27 | ref arg call to move | swap2.cpp:145:10:145:10 | x | | -| swap2.cpp:142:29:142:29 | x | swap2.cpp:142:19:142:27 | call to move | | +| swap2.cpp:142:29:142:29 | x | swap2.cpp:142:19:142:27 | call to move | TAINT | | taint.cpp:4:27:4:33 | source1 | taint.cpp:6:13:6:19 | source1 | | | taint.cpp:4:40:4:45 | clean1 | taint.cpp:5:8:5:13 | clean1 | | | taint.cpp:4:40:4:45 | clean1 | taint.cpp:6:3:6:8 | clean1 | | @@ -7778,7 +7961,7 @@ | vector.cpp:527:9:527:10 | ref arg it | vector.cpp:529:9:529:10 | it | | | vector.cpp:527:9:527:10 | ref arg it | vector.cpp:530:3:530:4 | it | | | vector.cpp:527:9:527:10 | ref arg it | vector.cpp:531:9:531:10 | it | | -| vector.cpp:528:3:528:4 | it | vector.cpp:528:6:528:6 | call to operator+= | | +| vector.cpp:528:3:528:4 | it | vector.cpp:528:6:528:6 | call to operator+= | TAINT | | vector.cpp:528:3:528:4 | ref arg it | vector.cpp:529:9:529:10 | it | | | vector.cpp:528:3:528:4 | ref arg it | vector.cpp:530:3:530:4 | it | | | vector.cpp:528:3:528:4 | ref arg it | vector.cpp:531:9:531:10 | it | | @@ -7786,7 +7969,7 @@ | vector.cpp:529:9:529:10 | it | vector.cpp:529:8:529:8 | call to operator* | TAINT | | vector.cpp:529:9:529:10 | ref arg it | vector.cpp:530:3:530:4 | it | | | vector.cpp:529:9:529:10 | ref arg it | vector.cpp:531:9:531:10 | it | | -| vector.cpp:530:3:530:4 | it | vector.cpp:530:6:530:6 | call to operator+= | | +| vector.cpp:530:3:530:4 | it | vector.cpp:530:6:530:6 | call to operator+= | TAINT | | vector.cpp:530:3:530:4 | ref arg it | vector.cpp:531:9:531:10 | it | | | vector.cpp:530:9:530:14 | call to source | vector.cpp:530:3:530:4 | ref arg it | TAINT | | vector.cpp:531:9:531:10 | it | vector.cpp:531:8:531:8 | call to operator* | TAINT | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp index f45260228e8..28e44779009 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp @@ -9,7 +9,7 @@ template void sink(std::unique_ptr&); void test_make_shared() { std::shared_ptr p = std::make_shared(source()); - sink(*p); // $ ast MISSING: ir + sink(*p); // $ ast,ir sink(p); // $ ast,ir } @@ -21,7 +21,7 @@ void test_make_shared_array() { void test_make_unique() { std::unique_ptr p = std::make_unique(source()); - sink(*p); // $ ast MISSING: ir + sink(*p); // $ ast,ir sink(p); // $ ast,ir } @@ -35,16 +35,16 @@ void test_reverse_taint_shared() { std::shared_ptr p = std::make_shared(); *p = source(); - sink(p); // $ MISSING: ast,ir - sink(*p); // $ MISSING: ast,ir + sink(p); // $ ast MISSING: ir + sink(*p); // $ ast MISSING: ir } void test_reverse_taint_unique() { std::unique_ptr p = std::unique_ptr(); *p = source(); - sink(p); // $ MISSING: ast,ir - sink(*p); // $ MISSING: ast,ir + sink(p); // $ ast MISSING: ir + sink(*p); // $ ast MISSING: ir } void test_shared_get() { @@ -65,4 +65,74 @@ void test_shared_field_member() { std::unique_ptr p = std::make_unique(source(), 0); sink(p->x); // $ MISSING: ast,ir sink(p->y); // not tainted +} + +void getNumber(std::shared_ptr ptr) { + *ptr = source(); +} + +int test_from_issue_5190() { + std::shared_ptr p(new int); + getNumber(p); + sink(*p); // $ ast MISSING: ir +} + +struct B { + A a1; + A a2; + int z; +}; + +void test_operator_arrow(std::unique_ptr p, std::unique_ptr q) { + p->x = source(); + sink(p->x); // $ ast MISSING: ir + sink(p->y); + + q->a1.x = source(); + sink(q->a1.x); // $ ast MISSING: ir + sink(q->a1.y); + sink(q->a2.x); +} + +void taint_x(A* pa) { + pa->x = source(); +} + +void reverse_taint_smart_pointer() { + std::unique_ptr p = std::unique_ptr(new A); + taint_x(p.get()); + sink(p->x); // $ ast,ir +} + +struct C { + int z; + std::shared_ptr q; +}; + +void taint_x_shared(std::shared_ptr ptr) { + ptr->x = source(); +} + +void taint_x_shared_cref(const std::shared_ptr& ptr) { + ptr->x = source(); +} + +void getNumberCRef(const std::shared_ptr& ptr) { + *ptr = source(); +} + +int nested_shared_ptr_taint(std::shared_ptr p1, std::unique_ptr> p2) { + taint_x_shared(p1->q); + sink(p1->q->x); // $ ast MISSING: ir + + getNumber(*p2); + sink(**p2); // $ ast MISSING: ir +} + +int nested_shared_ptr_taint_cref(std::shared_ptr p1, std::unique_ptr> p2) { + taint_x_shared_cref(p1->q); + sink(p1->q->x); // $ ast MISSING: ir + + getNumberCRef(*p2); + sink(**p2); // $ ast MISSING: ir } \ No newline at end of file diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/stl.h b/cpp/ql/test/library-tests/dataflow/taint-tests/stl.h index 09e77c5a3b6..44550c3df76 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/stl.h +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/stl.h @@ -349,6 +349,7 @@ namespace std { public: shared_ptr() noexcept; explicit shared_ptr(T*); + shared_ptr(const shared_ptr&) noexcept; template shared_ptr(const shared_ptr&) noexcept; template shared_ptr(shared_ptr&&) noexcept; diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected index 44d379e46fe..295ad79b9e2 100644 --- a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected +++ b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected @@ -11365,6 +11365,225 @@ perf-regression.cpp: # 12| Type = [IntType] int # 12| Value = [Literal] 0 # 12| ValueCategory = prvalue +smart_ptr.cpp: +# 8| [TopLevelFunction] void unique_ptr_arg(std::unique_ptr>) +# 8| : +# 8| getParameter(0): [Parameter] up +# 8| Type = [ClassTemplateInstantiation] unique_ptr> +# 10| [TopLevelFunction] void call_unique_ptr_arg(int*) +# 10| : +# 10| getParameter(0): [Parameter] p +# 10| Type = [IntPointerType] int * +# 10| getEntryPoint(): [BlockStmt] { ... } +# 11| getStmt(0): [DeclStmt] declaration +# 11| getDeclarationEntry(0): [VariableDeclarationEntry] definition of up +# 11| Type = [ClassTemplateInstantiation] unique_ptr> +# 11| getVariable().getInitializer(): [Initializer] initializer for up +# 11| getExpr(): [ConstructorCall] call to unique_ptr +# 11| Type = [VoidType] void +# 11| ValueCategory = prvalue +# 11| getArgument(0): [VariableAccess] p +# 11| Type = [IntPointerType] int * +# 11| ValueCategory = prvalue(load) +# 12| getStmt(1): [ExprStmt] ExprStmt +# 12| getExpr(): [FunctionCall] call to unique_ptr_arg +# 12| Type = [VoidType] void +# 12| ValueCategory = prvalue +# 12| getArgument(0): [FunctionCall] call to move +# 12| Type = [RValueReferenceType] type && +# 12| ValueCategory = prvalue +# 12| getArgument(0): [VariableAccess] up +# 12| Type = [ClassTemplateInstantiation] unique_ptr> +# 12| ValueCategory = lvalue +# 12| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to) +# 12| Type = [LValueReferenceType] unique_ptr> & +# 12| ValueCategory = prvalue +# 12| getArgument(0).getFullyConverted(): [TemporaryObjectExpr] temporary object +# 12| Type = [ClassTemplateInstantiation] unique_ptr> +# 12| ValueCategory = lvalue +# 12| getExpr(): [ReferenceDereferenceExpr] (reference dereference) +# 12| Type = [CTypedefType,NestedTypedefType] type +# 12| ValueCategory = prvalue(load) +# 13| getStmt(2): [ReturnStmt] return ... +# 15| [TopLevelFunction] void shared_ptr_arg(std::shared_ptr) +# 15| : +# 15| getParameter(0): [Parameter] sp +# 15| Type = [ClassTemplateInstantiation] shared_ptr +# 17| [TopLevelFunction] void call_shared_ptr_arg(float*) +# 17| : +# 17| getParameter(0): [Parameter] p +# 17| Type = [PointerType] float * +# 17| getEntryPoint(): [BlockStmt] { ... } +# 18| getStmt(0): [DeclStmt] declaration +# 18| getDeclarationEntry(0): [VariableDeclarationEntry] definition of sp +# 18| Type = [ClassTemplateInstantiation] shared_ptr +# 18| getVariable().getInitializer(): [Initializer] initializer for sp +# 18| getExpr(): [ConstructorCall] call to shared_ptr +# 18| Type = [VoidType] void +# 18| ValueCategory = prvalue +# 18| getArgument(0): [VariableAccess] p +# 18| Type = [PointerType] float * +# 18| ValueCategory = prvalue(load) +# 19| getStmt(1): [ExprStmt] ExprStmt +# 19| getExpr(): [FunctionCall] call to shared_ptr_arg +# 19| Type = [VoidType] void +# 19| ValueCategory = prvalue +# 19| getArgument(0): [ConstructorCall] call to shared_ptr +# 19| Type = [VoidType] void +# 19| ValueCategory = prvalue +# 19| getArgument(0): [VariableAccess] sp +# 19| Type = [ClassTemplateInstantiation] shared_ptr +# 19| ValueCategory = lvalue +# 19| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to) +# 19| Type = [LValueReferenceType] const shared_ptr & +# 19| ValueCategory = prvalue +# 19| getExpr(): [CStyleCast] (const shared_ptr)... +# 19| Conversion = [GlvalueConversion] glvalue conversion +# 19| Type = [SpecifiedType] const shared_ptr +# 19| ValueCategory = lvalue +# 19| getArgument(0).getFullyConverted(): [TemporaryObjectExpr] temporary object +# 19| Type = [ClassTemplateInstantiation] shared_ptr +# 19| ValueCategory = lvalue +# 20| getStmt(2): [ReturnStmt] return ... +# 22| [TopLevelFunction] void shared_ptr_const_int(std::shared_ptr) +# 22| : +# 22| getParameter(0): [Parameter] (unnamed parameter 0) +# 22| Type = [ClassTemplateInstantiation] shared_ptr +# 23| [TopLevelFunction] void shared_ptr_const_int_ptr(std::shared_ptr) +# 23| : +# 23| getParameter(0): [Parameter] (unnamed parameter 0) +# 23| Type = [ClassTemplateInstantiation] shared_ptr +# 24| [TopLevelFunction] void shared_ptr_shared_ptr_const_int(std::shared_ptr>) +# 24| : +# 24| getParameter(0): [Parameter] (unnamed parameter 0) +# 24| Type = [ClassTemplateInstantiation] shared_ptr> +# 25| [TopLevelFunction] void shared_ptr_const_shared_ptr_int(std::shared_ptr const>) +# 25| : +# 25| getParameter(0): [Parameter] (unnamed parameter 0) +# 25| Type = [ClassTemplateInstantiation] shared_ptr> +# 26| [TopLevelFunction] void shared_ptr_const_shared_ptr_const_int(std::shared_ptr const>) +# 26| : +# 26| getParameter(0): [Parameter] (unnamed parameter 0) +# 26| Type = [ClassTemplateInstantiation] shared_ptr> +# 28| [TopLevelFunction] void call_shared_ptr_consts() +# 28| : +# 28| getEntryPoint(): [BlockStmt] { ... } +# 29| getStmt(0): [DeclStmt] declaration +# 29| getDeclarationEntry(0): [VariableDeclarationEntry] definition of sp_const_int +# 29| Type = [ClassTemplateInstantiation] shared_ptr +# 31| getStmt(1): [ExprStmt] ExprStmt +# 31| getExpr(): [FunctionCall] call to shared_ptr_const_int +# 31| Type = [VoidType] void +# 31| ValueCategory = prvalue +# 31| getArgument(0): [ConstructorCall] call to shared_ptr +# 31| Type = [VoidType] void +# 31| ValueCategory = prvalue +# 31| getArgument(0): [VariableAccess] sp_const_int +# 31| Type = [ClassTemplateInstantiation] shared_ptr +# 31| ValueCategory = lvalue +# 31| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to) +# 31| Type = [LValueReferenceType] const shared_ptr & +# 31| ValueCategory = prvalue +# 31| getExpr(): [CStyleCast] (const shared_ptr)... +# 31| Conversion = [GlvalueConversion] glvalue conversion +# 31| Type = [SpecifiedType] const shared_ptr +# 31| ValueCategory = lvalue +# 31| getArgument(0).getFullyConverted(): [TemporaryObjectExpr] temporary object +# 31| Type = [ClassTemplateInstantiation] shared_ptr +# 31| ValueCategory = lvalue +# 33| getStmt(2): [DeclStmt] declaration +# 33| getDeclarationEntry(0): [VariableDeclarationEntry] definition of sp_const_int_pointer +# 33| Type = [ClassTemplateInstantiation] shared_ptr +# 35| getStmt(3): [ExprStmt] ExprStmt +# 35| getExpr(): [FunctionCall] call to shared_ptr_const_int_ptr +# 35| Type = [VoidType] void +# 35| ValueCategory = prvalue +# 35| getArgument(0): [ConstructorCall] call to shared_ptr +# 35| Type = [VoidType] void +# 35| ValueCategory = prvalue +# 35| getArgument(0): [VariableAccess] sp_const_int_pointer +# 35| Type = [ClassTemplateInstantiation] shared_ptr +# 35| ValueCategory = lvalue +# 35| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to) +# 35| Type = [LValueReferenceType] const shared_ptr & +# 35| ValueCategory = prvalue +# 35| getExpr(): [CStyleCast] (const shared_ptr)... +# 35| Conversion = [GlvalueConversion] glvalue conversion +# 35| Type = [SpecifiedType] const shared_ptr +# 35| ValueCategory = lvalue +# 35| getArgument(0).getFullyConverted(): [TemporaryObjectExpr] temporary object +# 35| Type = [ClassTemplateInstantiation] shared_ptr +# 35| ValueCategory = lvalue +# 37| getStmt(4): [DeclStmt] declaration +# 37| getDeclarationEntry(0): [VariableDeclarationEntry] definition of sp_sp_const_int +# 37| Type = [ClassTemplateInstantiation] shared_ptr> +# 39| getStmt(5): [ExprStmt] ExprStmt +# 39| getExpr(): [FunctionCall] call to shared_ptr_shared_ptr_const_int +# 39| Type = [VoidType] void +# 39| ValueCategory = prvalue +# 39| getArgument(0): [ConstructorCall] call to shared_ptr +# 39| Type = [VoidType] void +# 39| ValueCategory = prvalue +# 39| getArgument(0): [VariableAccess] sp_sp_const_int +# 39| Type = [ClassTemplateInstantiation] shared_ptr> +# 39| ValueCategory = lvalue +# 39| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to) +# 39| Type = [LValueReferenceType] const shared_ptr> & +# 39| ValueCategory = prvalue +# 39| getExpr(): [CStyleCast] (const shared_ptr>)... +# 39| Conversion = [GlvalueConversion] glvalue conversion +# 39| Type = [SpecifiedType] const shared_ptr> +# 39| ValueCategory = lvalue +# 39| getArgument(0).getFullyConverted(): [TemporaryObjectExpr] temporary object +# 39| Type = [ClassTemplateInstantiation] shared_ptr> +# 39| ValueCategory = lvalue +# 41| getStmt(6): [DeclStmt] declaration +# 41| getDeclarationEntry(0): [VariableDeclarationEntry] definition of sp_const_sp_int +# 41| Type = [ClassTemplateInstantiation] shared_ptr> +# 43| getStmt(7): [ExprStmt] ExprStmt +# 43| getExpr(): [FunctionCall] call to shared_ptr_const_shared_ptr_int +# 43| Type = [VoidType] void +# 43| ValueCategory = prvalue +# 43| getArgument(0): [ConstructorCall] call to shared_ptr +# 43| Type = [VoidType] void +# 43| ValueCategory = prvalue +# 43| getArgument(0): [VariableAccess] sp_const_sp_int +# 43| Type = [ClassTemplateInstantiation] shared_ptr> +# 43| ValueCategory = lvalue +# 43| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to) +# 43| Type = [LValueReferenceType] const shared_ptr> & +# 43| ValueCategory = prvalue +# 43| getExpr(): [CStyleCast] (const shared_ptr>)... +# 43| Conversion = [GlvalueConversion] glvalue conversion +# 43| Type = [SpecifiedType] const shared_ptr> +# 43| ValueCategory = lvalue +# 43| getArgument(0).getFullyConverted(): [TemporaryObjectExpr] temporary object +# 43| Type = [ClassTemplateInstantiation] shared_ptr> +# 43| ValueCategory = lvalue +# 45| getStmt(8): [DeclStmt] declaration +# 45| getDeclarationEntry(0): [VariableDeclarationEntry] definition of sp_const_sp_const_int +# 45| Type = [ClassTemplateInstantiation] shared_ptr> +# 47| getStmt(9): [ExprStmt] ExprStmt +# 47| getExpr(): [FunctionCall] call to shared_ptr_const_shared_ptr_const_int +# 47| Type = [VoidType] void +# 47| ValueCategory = prvalue +# 47| getArgument(0): [ConstructorCall] call to shared_ptr +# 47| Type = [VoidType] void +# 47| ValueCategory = prvalue +# 47| getArgument(0): [VariableAccess] sp_const_sp_const_int +# 47| Type = [ClassTemplateInstantiation] shared_ptr> +# 47| ValueCategory = lvalue +# 47| getArgument(0).getFullyConverted(): [ReferenceToExpr] (reference to) +# 47| Type = [LValueReferenceType] const shared_ptr> & +# 47| ValueCategory = prvalue +# 47| getExpr(): [CStyleCast] (const shared_ptr>)... +# 47| Conversion = [GlvalueConversion] glvalue conversion +# 47| Type = [SpecifiedType] const shared_ptr> +# 47| ValueCategory = lvalue +# 47| getArgument(0).getFullyConverted(): [TemporaryObjectExpr] temporary object +# 47| Type = [ClassTemplateInstantiation] shared_ptr> +# 47| ValueCategory = lvalue +# 48| getStmt(10): [ReturnStmt] return ... struct_init.cpp: # 1| [TopLevelFunction] int handler1(void*) # 1| : diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.ql b/cpp/ql/test/library-tests/ir/ir/PrintAST.ql new file mode 100644 index 00000000000..e107c80de02 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/PrintAST.ql @@ -0,0 +1,11 @@ +/** + * @kind graph + */ + +private import cpp +private import semmle.code.cpp.PrintAST +private import PrintConfig + +private class PrintConfig extends PrintASTConfiguration { + override predicate shouldPrintFunction(Function func) { shouldDumpFunction(func) } +} diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.qlref b/cpp/ql/test/library-tests/ir/ir/PrintAST.qlref deleted file mode 100644 index 6fcb30ac7a6..00000000000 --- a/cpp/ql/test/library-tests/ir/ir/PrintAST.qlref +++ /dev/null @@ -1 +0,0 @@ -semmle/code/cpp/PrintAST.ql \ No newline at end of file diff --git a/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll b/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll new file mode 100644 index 00000000000..3253a1196b6 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll @@ -0,0 +1,10 @@ +private import cpp + +/** + * Holds if the AST or IR for the specified function should be printed in the test output. + * + * This predicate excludes functions defined in standard headers. + */ +predicate shouldDumpFunction(Function func) { + not func.getLocation().getFile().getAbsolutePath().regexpMatch(".*/include/[^/]+") +} diff --git a/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected b/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected index 31e5b01229c..57f16b48a1a 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected @@ -6,6 +6,7 @@ missingOperandType duplicateChiOperand sideEffectWithoutPrimary instructionWithoutSuccessor +| ../../../include/memory.h:68:25:68:33 | CopyValue: (reference to) | Instruction 'CopyValue: (reference to)' has no successors in function '$@'. | ../../../include/memory.h:67:5:67:5 | void std::unique_ptr>::~unique_ptr() | void std::unique_ptr>::~unique_ptr() | ambiguousSuccessors unexplainedLoop unnecessaryPhiInstruction diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index a7dac56cc96..3746b0e2c6c 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -42,7 +42,7 @@ bad_asts.cpp: # 16| r16_3(int) = Constant[1] : # 16| r16_4(int) = Call[MemberFunction] : func:r16_2, this:r16_1, 0:r16_3 # 16| mu16_5(unknown) = ^CallSideEffect : ~m? -# 16| v16_6(void) = ^BufferReadSideEffect[-1] : &:r16_1, ~m? +# 16| v16_6(void) = ^IndirectReadSideEffect[-1] : &:r16_1, ~m? # 16| mu16_7(S) = ^IndirectMayWriteSideEffect[-1] : &:r16_1 # 17| v17_1(void) = NoOp : # 14| v14_4(void) = ReturnVoid : @@ -3385,47 +3385,46 @@ ir.cpp: # 622| void CallMethods(String&, String*, String) # 622| Block 0 -# 622| v622_1(void) = EnterFunction : -# 622| mu622_2(unknown) = AliasedDefinition : -# 622| mu622_3(unknown) = InitializeNonLocal : -# 622| r622_4(glval) = VariableAddress[r] : -# 622| mu622_5(String &) = InitializeParameter[r] : &:r622_4 -# 622| r622_6(String &) = Load[r] : &:r622_4, ~m? -# 622| mu622_7(unknown) = InitializeIndirection[r] : &:r622_6 -# 622| r622_8(glval) = VariableAddress[p] : -# 622| mu622_9(String *) = InitializeParameter[p] : &:r622_8 -# 622| r622_10(String *) = Load[p] : &:r622_8, ~m? -# 622| mu622_11(unknown) = InitializeIndirection[p] : &:r622_10 -# 622| r622_12(glval) = VariableAddress[s] : -# 622| mu622_13(String) = InitializeParameter[s] : &:r622_12 -# 623| r623_1(glval) = VariableAddress[r] : -# 623| r623_2(String &) = Load[r] : &:r623_1, ~m? -# 623| r623_3(glval) = CopyValue : r623_2 -# 623| r623_4(glval) = Convert : r623_3 -# 623| r623_5(glval) = FunctionAddress[c_str] : -# 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? -# 624| r624_1(glval) = VariableAddress[p] : -# 624| r624_2(String *) = Load[p] : &:r624_1, ~m? -# 624| r624_3(String *) = Convert : r624_2 -# 624| r624_4(glval) = FunctionAddress[c_str] : -# 624| r624_5(char *) = Call[c_str] : func:r624_4, this:r624_3 -# 624| mu624_6(unknown) = ^CallSideEffect : ~m? -# 624| v624_7(void) = ^BufferReadSideEffect[-1] : &:r624_3, ~m? -# 624| mu624_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r624_3 -# 625| r625_1(glval) = VariableAddress[s] : -# 625| r625_2(glval) = Convert : r625_1 -# 625| r625_3(glval) = FunctionAddress[c_str] : -# 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? -# 626| v626_1(void) = NoOp : -# 622| v622_14(void) = ReturnIndirection[r] : &:r622_6, ~m? -# 622| v622_15(void) = ReturnIndirection[p] : &:r622_10, ~m? -# 622| v622_16(void) = ReturnVoid : -# 622| v622_17(void) = AliasedUse : ~m? -# 622| v622_18(void) = ExitFunction : +# 622| v622_1(void) = EnterFunction : +# 622| mu622_2(unknown) = AliasedDefinition : +# 622| mu622_3(unknown) = InitializeNonLocal : +# 622| r622_4(glval) = VariableAddress[r] : +# 622| mu622_5(String &) = InitializeParameter[r] : &:r622_4 +# 622| r622_6(String &) = Load[r] : &:r622_4, ~m? +# 622| mu622_7(unknown) = InitializeIndirection[r] : &:r622_6 +# 622| r622_8(glval) = VariableAddress[p] : +# 622| mu622_9(String *) = InitializeParameter[p] : &:r622_8 +# 622| r622_10(String *) = Load[p] : &:r622_8, ~m? +# 622| mu622_11(unknown) = InitializeIndirection[p] : &:r622_10 +# 622| r622_12(glval) = VariableAddress[s] : +# 622| mu622_13(String) = InitializeParameter[s] : &:r622_12 +# 623| r623_1(glval) = VariableAddress[r] : +# 623| r623_2(String &) = Load[r] : &:r623_1, ~m? +# 623| r623_3(glval) = CopyValue : r623_2 +# 623| r623_4(glval) = Convert : r623_3 +# 623| r623_5(glval) = FunctionAddress[c_str] : +# 623| r623_6(char *) = Call[c_str] : func:r623_5, this:r623_4 +# 623| mu623_7(unknown) = ^CallSideEffect : ~m? +# 623| v623_8(void) = ^IndirectReadSideEffect[-1] : &:r623_4, ~m? +# 624| r624_1(glval) = VariableAddress[p] : +# 624| r624_2(String *) = Load[p] : &:r624_1, ~m? +# 624| r624_3(String *) = Convert : r624_2 +# 624| r624_4(glval) = FunctionAddress[c_str] : +# 624| r624_5(char *) = Call[c_str] : func:r624_4, this:r624_3 +# 624| mu624_6(unknown) = ^CallSideEffect : ~m? +# 624| v624_7(void) = ^IndirectReadSideEffect[-1] : &:r624_3, ~m? +# 625| r625_1(glval) = VariableAddress[s] : +# 625| r625_2(glval) = Convert : r625_1 +# 625| r625_3(glval) = FunctionAddress[c_str] : +# 625| r625_4(char *) = Call[c_str] : func:r625_3, this:r625_2 +# 625| mu625_5(unknown) = ^CallSideEffect : ~m? +# 625| v625_6(void) = ^IndirectReadSideEffect[-1] : &:r625_2, ~m? +# 626| v626_1(void) = NoOp : +# 622| v622_14(void) = ReturnIndirection[r] : &:r622_6, ~m? +# 622| v622_15(void) = ReturnIndirection[p] : &:r622_10, ~m? +# 622| v622_16(void) = ReturnVoid : +# 622| v622_17(void) = AliasedUse : ~m? +# 622| v622_18(void) = ExitFunction : # 628| void C::~C() # 628| Block 0 @@ -3575,7 +3574,7 @@ ir.cpp: # 653| r653_4(int) = Constant[0] : # 653| r653_5(int) = Call[InstanceMemberFunction] : func:r653_3, this:r653_2, 0:r653_4 # 653| mu653_6(unknown) = ^CallSideEffect : ~m? -# 653| v653_7(void) = ^BufferReadSideEffect[-1] : &:r653_2, ~m? +# 653| v653_7(void) = ^IndirectReadSideEffect[-1] : &:r653_2, ~m? # 653| mu653_8(C) = ^IndirectMayWriteSideEffect[-1] : &:r653_2 # 654| r654_1(glval) = VariableAddress[#this] : # 654| r654_2(C *) = Load[#this] : &:r654_1, ~m? @@ -3584,7 +3583,7 @@ ir.cpp: # 654| r654_5(int) = Constant[1] : # 654| r654_6(int) = Call[InstanceMemberFunction] : func:r654_4, this:r654_3, 0:r654_5 # 654| mu654_7(unknown) = ^CallSideEffect : ~m? -# 654| v654_8(void) = ^BufferReadSideEffect[-1] : &:r654_3, ~m? +# 654| v654_8(void) = ^IndirectReadSideEffect[-1] : &:r654_3, ~m? # 654| mu654_9(C) = ^IndirectMayWriteSideEffect[-1] : &:r654_3 # 655| r655_1(glval) = VariableAddress[#this] : # 655| r655_2(C *) = Load[#this] : &:r655_1, ~m? @@ -3592,7 +3591,7 @@ ir.cpp: # 655| r655_4(int) = Constant[2] : # 655| r655_5(int) = Call[InstanceMemberFunction] : func:r655_3, this:r655_2, 0:r655_4 # 655| mu655_6(unknown) = ^CallSideEffect : ~m? -# 655| v655_7(void) = ^BufferReadSideEffect[-1] : &:r655_2, ~m? +# 655| v655_7(void) = ^IndirectReadSideEffect[-1] : &:r655_2, ~m? # 655| mu655_8(C) = ^IndirectMayWriteSideEffect[-1] : &:r655_2 # 656| v656_1(void) = NoOp : # 652| v652_8(void) = ReturnIndirection[#this] : &:r652_6, ~m? @@ -4006,7 +4005,7 @@ ir.cpp: #-----| r0_6(String &) = CopyValue : r745_15 # 745| r745_16(String &) = Call[operator=] : func:r745_12, this:r745_11, 0:r0_6 # 745| mu745_17(unknown) = ^CallSideEffect : ~m? -# 745| v745_18(void) = ^BufferReadSideEffect[-1] : &:r745_11, ~m? +# 745| v745_18(void) = ^IndirectReadSideEffect[-1] : &:r745_11, ~m? #-----| v0_7(void) = ^BufferReadSideEffect[0] : &:r0_6, ~m? # 745| mu745_19(String) = ^IndirectMayWriteSideEffect[-1] : &:r745_11 #-----| r0_8(glval) = CopyValue : r745_16 @@ -4113,7 +4112,7 @@ ir.cpp: #-----| r0_8(Base &) = CopyValue : r754_14 # 754| r754_15(Base &) = Call[operator=] : func:r754_10, this:r0_5, 0:r0_8 # 754| mu754_16(unknown) = ^CallSideEffect : ~m? -#-----| v0_9(void) = ^BufferReadSideEffect[-1] : &:r0_5, ~m? +#-----| v0_9(void) = ^IndirectReadSideEffect[-1] : &:r0_5, ~m? #-----| v0_10(void) = ^BufferReadSideEffect[0] : &:r0_8, ~m? #-----| mu0_11(Base) = ^IndirectMayWriteSideEffect[-1] : &:r0_5 #-----| r0_12(glval) = CopyValue : r754_15 @@ -4129,7 +4128,7 @@ ir.cpp: #-----| 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? +# 754| v754_27(void) = ^IndirectReadSideEffect[-1] : &:r754_20, ~m? #-----| v0_15(void) = ^BufferReadSideEffect[0] : &:r0_14, ~m? # 754| mu754_28(String) = ^IndirectMayWriteSideEffect[-1] : &:r754_20 #-----| r0_16(glval) = CopyValue : r754_25 @@ -4220,7 +4219,7 @@ ir.cpp: #-----| r0_8(Middle &) = CopyValue : r763_14 # 763| r763_15(Middle &) = Call[operator=] : func:r763_10, this:r0_5, 0:r0_8 # 763| mu763_16(unknown) = ^CallSideEffect : ~m? -#-----| v0_9(void) = ^BufferReadSideEffect[-1] : &:r0_5, ~m? +#-----| v0_9(void) = ^IndirectReadSideEffect[-1] : &:r0_5, ~m? #-----| v0_10(void) = ^BufferReadSideEffect[0] : &:r0_8, ~m? #-----| mu0_11(Middle) = ^IndirectMayWriteSideEffect[-1] : &:r0_5 #-----| r0_12(glval) = CopyValue : r763_15 @@ -4236,7 +4235,7 @@ ir.cpp: #-----| 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? +# 763| v763_27(void) = ^IndirectReadSideEffect[-1] : &:r763_20, ~m? #-----| v0_15(void) = ^BufferReadSideEffect[0] : &:r0_14, ~m? # 763| mu763_28(String) = ^IndirectMayWriteSideEffect[-1] : &:r763_20 #-----| r0_16(glval) = CopyValue : r763_25 @@ -4505,7 +4504,7 @@ ir.cpp: # 808| r808_5(Base &) = CopyValue : r808_4 # 808| r808_6(Base &) = Call[operator=] : func:r808_2, this:r808_1, 0:r808_5 # 808| mu808_7(unknown) = ^CallSideEffect : ~m? -# 808| v808_8(void) = ^BufferReadSideEffect[-1] : &:r808_1, ~m? +# 808| v808_8(void) = ^IndirectReadSideEffect[-1] : &:r808_1, ~m? # 808| v808_9(void) = ^BufferReadSideEffect[0] : &:r808_5, ~m? # 808| mu808_10(Base) = ^IndirectMayWriteSideEffect[-1] : &:r808_1 # 808| r808_11(glval) = CopyValue : r808_6 @@ -4525,7 +4524,7 @@ ir.cpp: # 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_17(void) = ^IndirectReadSideEffect[-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 @@ -4545,7 +4544,7 @@ ir.cpp: # 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_17(void) = ^IndirectReadSideEffect[-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 @@ -4577,7 +4576,7 @@ ir.cpp: # 816| r816_6(Middle &) = CopyValue : r816_5 # 816| r816_7(Middle &) = Call[operator=] : func:r816_2, this:r816_1, 0:r816_6 # 816| mu816_8(unknown) = ^CallSideEffect : ~m? -# 816| v816_9(void) = ^BufferReadSideEffect[-1] : &:r816_1, ~m? +# 816| v816_9(void) = ^IndirectReadSideEffect[-1] : &:r816_1, ~m? # 816| v816_10(void) = ^BufferReadSideEffect[0] : &:r816_6, ~m? # 816| mu816_11(Middle) = ^IndirectMayWriteSideEffect[-1] : &:r816_1 # 816| r816_12(glval) = CopyValue : r816_7 @@ -4589,7 +4588,7 @@ ir.cpp: # 817| r817_6(Middle &) = CopyValue : r817_5 # 817| r817_7(Middle &) = Call[operator=] : func:r817_2, this:r817_1, 0:r817_6 # 817| mu817_8(unknown) = ^CallSideEffect : ~m? -# 817| v817_9(void) = ^BufferReadSideEffect[-1] : &:r817_1, ~m? +# 817| v817_9(void) = ^IndirectReadSideEffect[-1] : &:r817_1, ~m? # 817| v817_10(void) = ^BufferReadSideEffect[0] : &:r817_6, ~m? # 817| mu817_11(Middle) = ^IndirectMayWriteSideEffect[-1] : &:r817_1 # 817| r817_12(glval) = CopyValue : r817_7 @@ -4616,7 +4615,7 @@ ir.cpp: # 822| r822_6(Base &) = CopyValue : r822_5 # 822| r822_7(Base &) = Call[operator=] : func:r822_2, this:r822_1, 0:r822_6 # 822| mu822_8(unknown) = ^CallSideEffect : ~m? -# 822| v822_9(void) = ^BufferReadSideEffect[-1] : &:r822_1, ~m? +# 822| v822_9(void) = ^IndirectReadSideEffect[-1] : &:r822_1, ~m? # 822| v822_10(void) = ^BufferReadSideEffect[0] : &:r822_6, ~m? # 822| mu822_11(Base) = ^IndirectMayWriteSideEffect[-1] : &:r822_1 # 822| r822_12(glval) = CopyValue : r822_7 @@ -4637,7 +4636,7 @@ ir.cpp: # 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_18(void) = ^IndirectReadSideEffect[-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 @@ -4658,7 +4657,7 @@ ir.cpp: # 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_18(void) = ^IndirectReadSideEffect[-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 @@ -4694,7 +4693,7 @@ ir.cpp: # 830| r830_7(Derived &) = CopyValue : r830_6 # 830| r830_8(Derived &) = Call[operator=] : func:r830_2, this:r830_1, 0:r830_7 # 830| mu830_9(unknown) = ^CallSideEffect : ~m? -# 830| v830_10(void) = ^BufferReadSideEffect[-1] : &:r830_1, ~m? +# 830| v830_10(void) = ^IndirectReadSideEffect[-1] : &:r830_1, ~m? # 830| v830_11(void) = ^BufferReadSideEffect[0] : &:r830_7, ~m? # 830| mu830_12(Derived) = ^IndirectMayWriteSideEffect[-1] : &:r830_1 # 830| r830_13(glval) = CopyValue : r830_8 @@ -4707,7 +4706,7 @@ ir.cpp: # 831| r831_7(Derived &) = CopyValue : r831_6 # 831| r831_8(Derived &) = Call[operator=] : func:r831_2, this:r831_1, 0:r831_7 # 831| mu831_9(unknown) = ^CallSideEffect : ~m? -# 831| v831_10(void) = ^BufferReadSideEffect[-1] : &:r831_1, ~m? +# 831| v831_10(void) = ^IndirectReadSideEffect[-1] : &:r831_1, ~m? # 831| v831_11(void) = ^BufferReadSideEffect[0] : &:r831_7, ~m? # 831| mu831_12(Derived) = ^IndirectMayWriteSideEffect[-1] : &:r831_1 # 831| r831_13(glval) = CopyValue : r831_8 @@ -5688,7 +5687,7 @@ ir.cpp: # 1044| r1044_4(float) = Constant[1.0] : # 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| v1044_7(void) = ^IndirectReadSideEffect[-1] : &:r1044_2, ~m? # 1045| r1045_1(glval) = VariableAddress[lambda_val] : # 1045| r1045_2(glval) = VariableAddress[#temp1045:20] : # 1045| mu1045_3(decltype([...](...){...})) = Uninitialized[#temp1045:20] : &:r1045_2 @@ -5709,7 +5708,7 @@ ir.cpp: # 1046| r1046_4(float) = Constant[2.0] : # 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| v1046_7(void) = ^IndirectReadSideEffect[-1] : &:r1046_2, ~m? # 1047| r1047_1(glval) = VariableAddress[lambda_ref_explicit] : # 1047| r1047_2(glval) = VariableAddress[#temp1047:29] : # 1047| mu1047_3(decltype([...](...){...})) = Uninitialized[#temp1047:29] : &:r1047_2 @@ -5727,7 +5726,7 @@ ir.cpp: # 1048| r1048_4(float) = Constant[3.0] : # 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| v1048_7(void) = ^IndirectReadSideEffect[-1] : &:r1048_2, ~m? # 1049| r1049_1(glval) = VariableAddress[lambda_val_explicit] : # 1049| r1049_2(glval) = VariableAddress[#temp1049:29] : # 1049| mu1049_3(decltype([...](...){...})) = Uninitialized[#temp1049:29] : &:r1049_2 @@ -5744,7 +5743,7 @@ ir.cpp: # 1050| r1050_4(float) = Constant[4.0] : # 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| v1050_7(void) = ^IndirectReadSideEffect[-1] : &:r1050_2, ~m? # 1051| r1051_1(glval) = VariableAddress[lambda_mixed_explicit] : # 1051| r1051_2(glval) = VariableAddress[#temp1051:31] : # 1051| mu1051_3(decltype([...](...){...})) = Uninitialized[#temp1051:31] : &:r1051_2 @@ -5766,7 +5765,7 @@ ir.cpp: # 1052| r1052_4(float) = Constant[5.0] : # 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| v1052_7(void) = ^IndirectReadSideEffect[-1] : &:r1052_2, ~m? # 1053| r1053_1(glval) = VariableAddress[r] : # 1053| r1053_2(glval) = VariableAddress[x] : # 1053| r1053_3(int) = Load[x] : &:r1053_2, ~m? @@ -5804,7 +5803,7 @@ ir.cpp: # 1055| r1055_4(float) = Constant[6.0] : # 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| v1055_7(void) = ^IndirectReadSideEffect[-1] : &:r1055_2, ~m? # 1056| v1056_1(void) = NoOp : # 1040| v1040_10(void) = ReturnIndirection[s] : &:r1040_8, ~m? # 1040| v1040_11(void) = ReturnVoid : @@ -5869,7 +5868,7 @@ ir.cpp: # 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| v1043_19(void) = ^IndirectReadSideEffect[-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 @@ -5921,7 +5920,7 @@ ir.cpp: # 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| v1045_17(void) = ^IndirectReadSideEffect[-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 @@ -5955,7 +5954,7 @@ ir.cpp: # 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| v1047_19(void) = ^IndirectReadSideEffect[-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? @@ -6003,7 +6002,7 @@ ir.cpp: # 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| v1049_17(void) = ^IndirectReadSideEffect[-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? @@ -6034,7 +6033,7 @@ ir.cpp: # 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| v1051_19(void) = ^IndirectReadSideEffect[-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 @@ -6068,7 +6067,7 @@ ir.cpp: # 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| v1054_19(void) = ^IndirectReadSideEffect[-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 @@ -6095,37 +6094,37 @@ ir.cpp: # 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? -# 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 +# 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) = ^IndirectReadSideEffect[-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) = ^IndirectReadSideEffect[-1] : &:r0_3, ~m? +# 1078| mu1078_20(iterator) = Store[(__end)] : &:r1078_14, r1078_18 #-----| Goto -> Block 1 # 1078| Block 1 @@ -6136,26 +6135,26 @@ ir.cpp: # 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? +#-----| v0_6(void) = ^IndirectReadSideEffect[-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_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 +# 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) = ^IndirectReadSideEffect[-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 @@ -6169,36 +6168,36 @@ ir.cpp: # 1078| r1078_37(glval) = FunctionAddress[operator++] : # 1078| r1078_38(iterator &) = Call[operator++] : func:r1078_37, this:r1078_36 # 1078| mu1078_39(unknown) = ^CallSideEffect : ~m? -# 1078| v1078_40(void) = ^BufferReadSideEffect[-1] : &:r1078_36, ~m? +# 1078| v1078_40(void) = ^IndirectReadSideEffect[-1] : &:r1078_36, ~m? # 1078| mu1078_41(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r1078_36 # 1078| r1078_42(glval) = CopyValue : r1078_38 #-----| 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_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 +# 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) = ^IndirectReadSideEffect[-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) = ^IndirectReadSideEffect[-1] : &:r0_11, ~m? +# 1084| mu1084_20(iterator) = Store[(__end)] : &:r1084_14, r1084_18 #-----| Goto -> Block 6 # 1084| Block 6 @@ -6209,7 +6208,7 @@ ir.cpp: # 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? +#-----| v0_14(void) = ^IndirectReadSideEffect[-1] : &:r0_13, ~m? # 1084| v1084_27(void) = ConditionalBranch : r1084_25 #-----| False -> Block 10 #-----| True -> Block 8 @@ -6219,29 +6218,29 @@ ir.cpp: # 1084| r1084_29(glval) = FunctionAddress[operator++] : # 1084| r1084_30(iterator &) = Call[operator++] : func:r1084_29, this:r1084_28 # 1084| mu1084_31(unknown) = ^CallSideEffect : ~m? -# 1084| v1084_32(void) = ^BufferReadSideEffect[-1] : &:r1084_28, ~m? +# 1084| v1084_32(void) = ^IndirectReadSideEffect[-1] : &:r1084_28, ~m? # 1084| mu1084_33(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r1084_28 # 1084| r1084_34(glval) = CopyValue : r1084_30 #-----| Goto (back edge) -> Block 6 # 1084| Block 8 -# 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 +# 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) = ^IndirectReadSideEffect[-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 @@ -7525,7 +7524,7 @@ ir.cpp: # 1373| r1373_8(glval) = FunctionAddress[c_str] : # 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| v1373_11(void) = ^IndirectReadSideEffect[-1] : &:r1373_7, ~m? # 1374| r1374_1(glval) = VariableAddress[#temp1374:5] : # 1374| r1374_2(glval) = FunctionAddress[returnValue] : # 1374| r1374_3(String) = Call[returnValue] : func:r1374_2 @@ -7535,7 +7534,7 @@ ir.cpp: # 1374| r1374_7(glval) = FunctionAddress[c_str] : # 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| v1374_10(void) = ^IndirectReadSideEffect[-1] : &:r1374_6, ~m? # 1376| r1376_1(glval) = VariableAddress[#temp1376:5] : # 1376| r1376_2(glval) = FunctionAddress[defaultConstruct] : # 1376| r1376_3(String) = Call[defaultConstruct] : func:r1376_2 @@ -7589,7 +7588,7 @@ ir.cpp: # 1385| r1385_4(glval) = FunctionAddress[method] : # 1385| v1385_5(void) = Call[method] : func:r1385_4, this:r1385_1 # 1385| mu1385_6(unknown) = ^CallSideEffect : ~m? -# 1385| v1385_7(void) = ^BufferReadSideEffect[-1] : &:r1385_1, ~m? +# 1385| v1385_7(void) = ^IndirectReadSideEffect[-1] : &:r1385_1, ~m? # 1385| mu1385_8(destructor_only) = ^IndirectMayWriteSideEffect[-1] : &:r1385_1 # 1386| r1386_1(glval) = VariableAddress[#temp1386:5] : # 1386| r1386_2(glval) = FunctionAddress[returnValue] : @@ -7599,7 +7598,7 @@ ir.cpp: # 1386| r1386_6(glval) = FunctionAddress[method] : # 1386| v1386_7(void) = Call[method] : func:r1386_6, this:r1386_1 # 1386| mu1386_8(unknown) = ^CallSideEffect : ~m? -# 1386| v1386_9(void) = ^BufferReadSideEffect[-1] : &:r1386_1, ~m? +# 1386| v1386_9(void) = ^IndirectReadSideEffect[-1] : &:r1386_1, ~m? # 1386| mu1386_10(destructor_only) = ^IndirectMayWriteSideEffect[-1] : &:r1386_1 # 1388| r1388_1(glval) = VariableAddress[#temp1388:5] : # 1388| r1388_2(glval) = FunctionAddress[defaultConstruct] : @@ -7667,7 +7666,7 @@ ir.cpp: # 1397| r1397_7(glval) = FunctionAddress[method] : # 1397| v1397_8(void) = Call[method] : func:r1397_7, this:r1397_1 # 1397| mu1397_9(unknown) = ^CallSideEffect : ~m? -# 1397| v1397_10(void) = ^BufferReadSideEffect[-1] : &:r1397_1, ~m? +# 1397| v1397_10(void) = ^IndirectReadSideEffect[-1] : &:r1397_1, ~m? # 1397| mu1397_11(copy_constructor) = ^IndirectMayWriteSideEffect[-1] : &:r1397_1 # 1398| r1398_1(glval) = VariableAddress[#temp1398:5] : # 1398| r1398_2(glval) = FunctionAddress[returnValue] : @@ -7677,7 +7676,7 @@ ir.cpp: # 1398| r1398_6(glval) = FunctionAddress[method] : # 1398| v1398_7(void) = Call[method] : func:r1398_6, this:r1398_1 # 1398| mu1398_8(unknown) = ^CallSideEffect : ~m? -# 1398| v1398_9(void) = ^BufferReadSideEffect[-1] : &:r1398_1, ~m? +# 1398| v1398_9(void) = ^IndirectReadSideEffect[-1] : &:r1398_1, ~m? # 1398| mu1398_10(copy_constructor) = ^IndirectMayWriteSideEffect[-1] : &:r1398_1 # 1399| r1399_1(glval) = VariableAddress[#temp1399:5] : # 1399| r1399_2(glval) = FunctionAddress[defaultConstruct] : @@ -7851,7 +7850,7 @@ ir.cpp: # 1447| r1447_9(glval) = FunctionAddress[f] : # 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| v1447_12(void) = ^IndirectReadSideEffect[-1] : &:r1447_8, ~m? # 1447| mu1447_13(float) = Store[f] : &:r1447_1, r1447_10 # 1448| v1448_1(void) = NoOp : # 1443| v1443_4(void) = ReturnVoid : @@ -7904,6 +7903,180 @@ perf-regression.cpp: # 9| v9_6(void) = AliasedUse : ~m? # 9| v9_7(void) = ExitFunction : +smart_ptr.cpp: +# 10| void call_unique_ptr_arg(int*) +# 10| Block 0 +# 10| v10_1(void) = EnterFunction : +# 10| mu10_2(unknown) = AliasedDefinition : +# 10| mu10_3(unknown) = InitializeNonLocal : +# 10| r10_4(glval) = VariableAddress[p] : +# 10| mu10_5(int *) = InitializeParameter[p] : &:r10_4 +# 10| r10_6(int *) = Load[p] : &:r10_4, ~m? +# 10| mu10_7(unknown) = InitializeIndirection[p] : &:r10_6 +# 11| r11_1(glval>>) = VariableAddress[up] : +# 11| mu11_2(unique_ptr>) = Uninitialized[up] : &:r11_1 +# 11| r11_3(glval) = FunctionAddress[unique_ptr] : +# 11| r11_4(glval) = VariableAddress[p] : +# 11| r11_5(int *) = Load[p] : &:r11_4, ~m? +# 11| v11_6(void) = Call[unique_ptr] : func:r11_3, this:r11_1, 0:r11_5 +# 11| mu11_7(unknown) = ^CallSideEffect : ~m? +# 11| mu11_8(unique_ptr>) = ^IndirectMustWriteSideEffect[-1] : &:r11_1 +# 12| r12_1(glval) = FunctionAddress[unique_ptr_arg] : +# 12| r12_2(glval>>) = VariableAddress[#temp12:20] : +# 12| r12_3(glval) = FunctionAddress[move] : +# 12| r12_4(glval>>) = VariableAddress[up] : +# 12| r12_5(unique_ptr> &) = CopyValue : r12_4 +# 12| r12_6(unique_ptr> &&) = Call[move] : func:r12_3, 0:r12_5 +# 12| r12_7(unique_ptr>) = Load[?] : &:r12_6, ~m? +# 12| mu12_8(unique_ptr>) = Store[#temp12:20] : &:r12_2, r12_7 +# 12| r12_9(unique_ptr>) = Load[#temp12:20] : &:r12_2, ~m? +# 12| v12_10(void) = Call[unique_ptr_arg] : func:r12_1, 0:r12_9 +# 12| mu12_11(unknown) = ^CallSideEffect : ~m? +# 12| v12_12(void) = ^BufferReadSideEffect[0] : &:r12_9, ~m? +# 12| mu12_13(unknown) = ^BufferMayWriteSideEffect[0] : &:r12_9 +# 13| v13_1(void) = NoOp : +# 10| v10_8(void) = ReturnIndirection[p] : &:r10_6, ~m? +# 10| v10_9(void) = ReturnVoid : +# 10| v10_10(void) = AliasedUse : ~m? +# 10| v10_11(void) = ExitFunction : + +# 17| void call_shared_ptr_arg(float*) +# 17| Block 0 +# 17| v17_1(void) = EnterFunction : +# 17| mu17_2(unknown) = AliasedDefinition : +# 17| mu17_3(unknown) = InitializeNonLocal : +# 17| r17_4(glval) = VariableAddress[p] : +# 17| mu17_5(float *) = InitializeParameter[p] : &:r17_4 +# 17| r17_6(float *) = Load[p] : &:r17_4, ~m? +# 17| mu17_7(unknown) = InitializeIndirection[p] : &:r17_6 +# 18| r18_1(glval>) = VariableAddress[sp] : +# 18| mu18_2(shared_ptr) = Uninitialized[sp] : &:r18_1 +# 18| r18_3(glval) = FunctionAddress[shared_ptr] : +# 18| r18_4(glval) = VariableAddress[p] : +# 18| r18_5(float *) = Load[p] : &:r18_4, ~m? +# 18| v18_6(void) = Call[shared_ptr] : func:r18_3, this:r18_1, 0:r18_5 +# 18| mu18_7(unknown) = ^CallSideEffect : ~m? +# 18| mu18_8(shared_ptr) = ^IndirectMustWriteSideEffect[-1] : &:r18_1 +# 19| r19_1(glval) = FunctionAddress[shared_ptr_arg] : +# 19| r19_2(glval>) = VariableAddress[#temp19:20] : +# 19| mu19_3(shared_ptr) = Uninitialized[#temp19:20] : &:r19_2 +# 19| r19_4(glval) = FunctionAddress[shared_ptr] : +# 19| r19_5(glval>) = VariableAddress[sp] : +# 19| r19_6(glval>) = Convert : r19_5 +# 19| r19_7(shared_ptr &) = CopyValue : r19_6 +# 19| v19_8(void) = Call[shared_ptr] : func:r19_4, this:r19_2, 0:r19_7 +# 19| mu19_9(unknown) = ^CallSideEffect : ~m? +# 19| mu19_10(shared_ptr) = ^IndirectMustWriteSideEffect[-1] : &:r19_2 +# 19| v19_11(void) = ^IndirectReadSideEffect[0] : &:r19_7, ~m? +# 19| r19_12(shared_ptr) = Load[#temp19:20] : &:r19_2, ~m? +# 19| v19_13(void) = Call[shared_ptr_arg] : func:r19_1, 0:r19_12 +# 19| mu19_14(unknown) = ^CallSideEffect : ~m? +# 19| v19_15(void) = ^BufferReadSideEffect[0] : &:r19_12, ~m? +# 19| mu19_16(unknown) = ^BufferMayWriteSideEffect[0] : &:r19_12 +# 20| v20_1(void) = NoOp : +# 17| v17_8(void) = ReturnIndirection[p] : &:r17_6, ~m? +# 17| v17_9(void) = ReturnVoid : +# 17| v17_10(void) = AliasedUse : ~m? +# 17| v17_11(void) = ExitFunction : + +# 28| void call_shared_ptr_consts() +# 28| Block 0 +# 28| v28_1(void) = EnterFunction : +# 28| mu28_2(unknown) = AliasedDefinition : +# 28| mu28_3(unknown) = InitializeNonLocal : +# 29| r29_1(glval>) = VariableAddress[sp_const_int] : +# 29| mu29_2(shared_ptr) = Uninitialized[sp_const_int] : &:r29_1 +# 31| r31_1(glval) = FunctionAddress[shared_ptr_const_int] : +# 31| r31_2(glval>) = VariableAddress[#temp31:26] : +# 31| mu31_3(shared_ptr) = Uninitialized[#temp31:26] : &:r31_2 +# 31| r31_4(glval) = FunctionAddress[shared_ptr] : +# 31| r31_5(glval>) = VariableAddress[sp_const_int] : +# 31| r31_6(glval>) = Convert : r31_5 +# 31| r31_7(shared_ptr &) = CopyValue : r31_6 +# 31| v31_8(void) = Call[shared_ptr] : func:r31_4, this:r31_2, 0:r31_7 +# 31| mu31_9(unknown) = ^CallSideEffect : ~m? +# 31| mu31_10(shared_ptr) = ^IndirectMustWriteSideEffect[-1] : &:r31_2 +# 31| v31_11(void) = ^IndirectReadSideEffect[0] : &:r31_7, ~m? +# 31| r31_12(shared_ptr) = Load[#temp31:26] : &:r31_2, ~m? +# 31| v31_13(void) = Call[shared_ptr_const_int] : func:r31_1, 0:r31_12 +# 31| mu31_14(unknown) = ^CallSideEffect : ~m? +# 31| v31_15(void) = ^BufferReadSideEffect[0] : &:r31_12, ~m? +# 33| r33_1(glval>) = VariableAddress[sp_const_int_pointer] : +# 33| mu33_2(shared_ptr) = Uninitialized[sp_const_int_pointer] : &:r33_1 +# 35| r35_1(glval) = FunctionAddress[shared_ptr_const_int_ptr] : +# 35| r35_2(glval>) = VariableAddress[#temp35:30] : +# 35| mu35_3(shared_ptr) = Uninitialized[#temp35:30] : &:r35_2 +# 35| r35_4(glval) = FunctionAddress[shared_ptr] : +# 35| r35_5(glval>) = VariableAddress[sp_const_int_pointer] : +# 35| r35_6(glval>) = Convert : r35_5 +# 35| r35_7(shared_ptr &) = CopyValue : r35_6 +# 35| v35_8(void) = Call[shared_ptr] : func:r35_4, this:r35_2, 0:r35_7 +# 35| mu35_9(unknown) = ^CallSideEffect : ~m? +# 35| mu35_10(shared_ptr) = ^IndirectMustWriteSideEffect[-1] : &:r35_2 +# 35| v35_11(void) = ^IndirectReadSideEffect[0] : &:r35_7, ~m? +# 35| r35_12(shared_ptr) = Load[#temp35:30] : &:r35_2, ~m? +# 35| v35_13(void) = Call[shared_ptr_const_int_ptr] : func:r35_1, 0:r35_12 +# 35| mu35_14(unknown) = ^CallSideEffect : ~m? +# 35| v35_15(void) = ^BufferReadSideEffect[0] : &:r35_12, ~m? +# 35| mu35_16(unknown) = ^BufferMayWriteSideEffect[0] : &:r35_12 +# 37| r37_1(glval>>) = VariableAddress[sp_sp_const_int] : +# 37| mu37_2(shared_ptr>) = Uninitialized[sp_sp_const_int] : &:r37_1 +# 39| r39_1(glval) = FunctionAddress[shared_ptr_shared_ptr_const_int] : +# 39| r39_2(glval>>) = VariableAddress[#temp39:37] : +# 39| mu39_3(shared_ptr>) = Uninitialized[#temp39:37] : &:r39_2 +# 39| r39_4(glval) = FunctionAddress[shared_ptr] : +# 39| r39_5(glval>>) = VariableAddress[sp_sp_const_int] : +# 39| r39_6(glval>>) = Convert : r39_5 +# 39| r39_7(shared_ptr> &) = CopyValue : r39_6 +# 39| v39_8(void) = Call[shared_ptr] : func:r39_4, this:r39_2, 0:r39_7 +# 39| mu39_9(unknown) = ^CallSideEffect : ~m? +# 39| mu39_10(shared_ptr>) = ^IndirectMustWriteSideEffect[-1] : &:r39_2 +# 39| v39_11(void) = ^IndirectReadSideEffect[0] : &:r39_7, ~m? +# 39| r39_12(shared_ptr>) = Load[#temp39:37] : &:r39_2, ~m? +# 39| v39_13(void) = Call[shared_ptr_shared_ptr_const_int] : func:r39_1, 0:r39_12 +# 39| mu39_14(unknown) = ^CallSideEffect : ~m? +# 39| v39_15(void) = ^BufferReadSideEffect[0] : &:r39_12, ~m? +# 39| mu39_16(unknown) = ^BufferMayWriteSideEffect[0] : &:r39_12 +# 41| r41_1(glval>>) = VariableAddress[sp_const_sp_int] : +# 41| mu41_2(shared_ptr>) = Uninitialized[sp_const_sp_int] : &:r41_1 +# 43| r43_1(glval) = FunctionAddress[shared_ptr_const_shared_ptr_int] : +# 43| r43_2(glval>>) = VariableAddress[#temp43:37] : +# 43| mu43_3(shared_ptr>) = Uninitialized[#temp43:37] : &:r43_2 +# 43| r43_4(glval) = FunctionAddress[shared_ptr] : +# 43| r43_5(glval>>) = VariableAddress[sp_const_sp_int] : +# 43| r43_6(glval>>) = Convert : r43_5 +# 43| r43_7(shared_ptr> &) = CopyValue : r43_6 +# 43| v43_8(void) = Call[shared_ptr] : func:r43_4, this:r43_2, 0:r43_7 +# 43| mu43_9(unknown) = ^CallSideEffect : ~m? +# 43| mu43_10(shared_ptr>) = ^IndirectMustWriteSideEffect[-1] : &:r43_2 +# 43| v43_11(void) = ^IndirectReadSideEffect[0] : &:r43_7, ~m? +# 43| r43_12(shared_ptr>) = Load[#temp43:37] : &:r43_2, ~m? +# 43| v43_13(void) = Call[shared_ptr_const_shared_ptr_int] : func:r43_1, 0:r43_12 +# 43| mu43_14(unknown) = ^CallSideEffect : ~m? +# 43| v43_15(void) = ^BufferReadSideEffect[0] : &:r43_12, ~m? +# 43| mu43_16(unknown) = ^BufferMayWriteSideEffect[0] : &:r43_12 +# 45| r45_1(glval>>) = VariableAddress[sp_const_sp_const_int] : +# 45| mu45_2(shared_ptr>) = Uninitialized[sp_const_sp_const_int] : &:r45_1 +# 47| r47_1(glval) = FunctionAddress[shared_ptr_const_shared_ptr_const_int] : +# 47| r47_2(glval>>) = VariableAddress[#temp47:43] : +# 47| mu47_3(shared_ptr>) = Uninitialized[#temp47:43] : &:r47_2 +# 47| r47_4(glval) = FunctionAddress[shared_ptr] : +# 47| r47_5(glval>>) = VariableAddress[sp_const_sp_const_int] : +# 47| r47_6(glval>>) = Convert : r47_5 +# 47| r47_7(shared_ptr> &) = CopyValue : r47_6 +# 47| v47_8(void) = Call[shared_ptr] : func:r47_4, this:r47_2, 0:r47_7 +# 47| mu47_9(unknown) = ^CallSideEffect : ~m? +# 47| mu47_10(shared_ptr>) = ^IndirectMustWriteSideEffect[-1] : &:r47_2 +# 47| v47_11(void) = ^IndirectReadSideEffect[0] : &:r47_7, ~m? +# 47| r47_12(shared_ptr>) = Load[#temp47:43] : &:r47_2, ~m? +# 47| v47_13(void) = Call[shared_ptr_const_shared_ptr_const_int] : func:r47_1, 0:r47_12 +# 47| mu47_14(unknown) = ^CallSideEffect : ~m? +# 47| v47_15(void) = ^BufferReadSideEffect[0] : &:r47_12, ~m? +# 48| v48_1(void) = NoOp : +# 28| v28_4(void) = ReturnVoid : +# 28| v28_5(void) = AliasedUse : ~m? +# 28| v28_6(void) = ExitFunction : + struct_init.cpp: # 16| void let_info_escape(Info*) # 16| Block 0 diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.ql b/cpp/ql/test/library-tests/ir/ir/raw_ir.ql new file mode 100644 index 00000000000..a0ebe4d2bdd --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.ql @@ -0,0 +1,11 @@ +/** + * @kind graph + */ + +private import cpp +private import semmle.code.cpp.ir.implementation.raw.PrintIR +private import PrintConfig + +private class PrintConfig extends PrintIRConfiguration { + override predicate shouldPrintFunction(Function func) { shouldDumpFunction(func) } +} diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.qlref b/cpp/ql/test/library-tests/ir/ir/raw_ir.qlref deleted file mode 100644 index fa4c4bda903..00000000000 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.qlref +++ /dev/null @@ -1 +0,0 @@ -semmle/code/cpp/ir/implementation/raw/PrintIR.ql \ No newline at end of file diff --git a/cpp/ql/test/library-tests/ir/ir/smart_ptr.cpp b/cpp/ql/test/library-tests/ir/ir/smart_ptr.cpp new file mode 100644 index 00000000000..f40c54bde44 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/smart_ptr.cpp @@ -0,0 +1,48 @@ +#include "../../../include/memory.h" +#include "../../../include/utility.h" + +using std::move; +using std::shared_ptr; +using std::unique_ptr; + +void unique_ptr_arg(unique_ptr up); + +void call_unique_ptr_arg(int* p) { + unique_ptr up(p); + unique_ptr_arg(move(up)); +} + +void shared_ptr_arg(shared_ptr sp); + +void call_shared_ptr_arg(float* p) { + shared_ptr sp(p); + shared_ptr_arg(sp); +} + +void shared_ptr_const_int(shared_ptr); +void shared_ptr_const_int_ptr(shared_ptr); +void shared_ptr_shared_ptr_const_int(shared_ptr>); +void shared_ptr_const_shared_ptr_int(shared_ptr>); +void shared_ptr_const_shared_ptr_const_int(shared_ptr>); + +void call_shared_ptr_consts() { + shared_ptr sp_const_int; + // cannot modify *sp_const_int + shared_ptr_const_int(sp_const_int); + + shared_ptr sp_const_int_pointer; + // can modify **sp_const_int_pointer + shared_ptr_const_int_ptr(sp_const_int_pointer); + + shared_ptr> sp_sp_const_int; + // can modify *sp_const_int_pointer + shared_ptr_shared_ptr_const_int(sp_sp_const_int); + + shared_ptr> sp_const_sp_int; + // can modify **sp_const_int_pointer + shared_ptr_const_shared_ptr_int(sp_const_sp_int); + + shared_ptr> sp_const_sp_const_int; + // cannot modify *sp_const_int_pointer or **sp_const_int_pointer + shared_ptr_const_shared_ptr_const_int(sp_const_sp_const_int); +} \ No newline at end of file diff --git a/cpp/ql/test/library-tests/ir/points_to/points_to.cpp b/cpp/ql/test/library-tests/ir/points_to/points_to.cpp index 024a3190706..fa4d062c5f8 100644 --- a/cpp/ql/test/library-tests/ir/points_to/points_to.cpp +++ b/cpp/ql/test/library-tests/ir/points_to/points_to.cpp @@ -37,51 +37,51 @@ void Locals() { } void PointsTo( - int a, //$raw,ussa=a - Point& b, //$raw,ussa=b ussa=*b - Point* c, //$raw,ussa=c ussa=*c - int* d, //$raw,ussa=d ussa=*d - DerivedSI* e, //$raw,ussa=e ussa=*e - DerivedMI* f, //$raw,ussa=f ussa=*f - DerivedVI* g //$raw,ussa=g ussa=*g + int a, //$raw=a + Point& b, //$raw=b ussa=*b + Point* c, //$raw=c ussa=*c + int* d, //$raw=d ussa=*d + DerivedSI* e, //$raw=e ussa=*e + DerivedMI* f, //$raw=f ussa=*f + DerivedVI* g //$raw=g ussa=*g ) { - int i = a; //$raw,ussa=a - i = *&a; //$raw,ussa=a - i = *(&a + 0); //$raw,ussa=a - i = b.x; //$raw,ussa=b ussa=*b[0..4) - i = b.y; //$raw,ussa=b ussa=*b[4..8) - i = c->x; //$raw,ussa=c ussa=*c[0..4) - i = c->y; //$raw,ussa=c ussa=*c[4..8) - i = *d; //$raw,ussa=d ussa=*d[0..4) - i = *(d + 0); //$raw,ussa=d ussa=*d[0..4) - i = d[5]; //$raw,ussa=d ussa=*d[20..24) - i = 5[d]; //$raw,ussa=d ussa=*d[20..24) - i = d[a]; //$raw,ussa=d raw,ussa=a ussa=*d[?..?) - i = a[d]; //$raw,ussa=d raw,ussa=a ussa=*d[?..?) + int i = a; //$raw=a + i = *&a; //$raw=a + i = *(&a + 0); //$raw=a + i = b.x; //$raw=b ussa=*b[0..4) + i = b.y; //$raw=b ussa=*b[4..8) + i = c->x; //$raw=c ussa=*c[0..4) + i = c->y; //$raw=c ussa=*c[4..8) + i = *d; //$raw=d ussa=*d[0..4) + i = *(d + 0); //$raw=d ussa=*d[0..4) + i = d[5]; //$raw=d ussa=*d[20..24) + i = 5[d]; //$raw=d ussa=*d[20..24) + i = d[a]; //$raw=d raw=a ussa=*d[?..?) + i = a[d]; //$raw=d raw=a ussa=*d[?..?) - int* p = &b.x; //$raw,ussa=b + int* p = &b.x; //$raw=b i = *p; //$ussa=*b[0..4) - p = &b.y; //$raw,ussa=b + p = &b.y; //$raw=b i = *p; //$ussa=*b[4..8) - p = &c->x; //$raw,ussa=c + p = &c->x; //$raw=c i = *p; //$ussa=*c[0..4) - p = &c->y; //$raw,ussa=c + p = &c->y; //$raw=c i = *p; //$ussa=*c[4..8) - p = &d[5]; //$raw,ussa=d + p = &d[5]; //$raw=d i = *p; //$ussa=*d[20..24) - p = &d[a]; //$raw,ussa=d raw,ussa=a + p = &d[a]; //$raw=d raw=a i = *p; //$ussa=*d[?..?) - Point* q = &c[a]; //$raw,ussa=c raw,ussa=a + Point* q = &c[a]; //$raw=c raw=a i = q->x; //$ussa=*c[?..?) i = q->y; //$ussa=*c[?..?) - i = e->b1; //$raw,ussa=e ussa=*e[0..4) - i = e->dsi; //$raw,ussa=e ussa=*e[4..8) - i = f->b1; //$raw,ussa=f ussa=*f[0..4) - i = f->b2; //$raw,ussa=f ussa=*f[4..8) - i = f->dmi; //$raw,ussa=f ussa=*f[8..12) - i = g->b1; //$raw,ussa=g ussa=*g[?..?) - i = g->dvi; //$raw,ussa=g ussa=*g[8..12) + i = e->b1; //$raw=e ussa=*e[0..4) + i = e->dsi; //$raw=e ussa=*e[4..8) + i = f->b1; //$raw=f ussa=*f[0..4) + i = f->b2; //$raw=f ussa=*f[4..8) + i = f->dmi; //$raw=f ussa=*f[8..12) + i = g->b1; //$raw=g ussa=*g[?..?) + i = g->dvi; //$raw=g ussa=*g[8..12) } \ No newline at end of file diff --git a/cpp/ql/test/library-tests/ir/points_to/points_to.ql b/cpp/ql/test/library-tests/ir/points_to/points_to.ql index 89d1e31e119..da090c5552e 100644 --- a/cpp/ql/test/library-tests/ir/points_to/points_to.ql +++ b/cpp/ql/test/library-tests/ir/points_to/points_to.ql @@ -5,7 +5,16 @@ private import semmle.code.cpp.ir.internal.IntegerConstant as Ints private predicate ignoreAllocation(string name) { name = "i" or name = "p" or - name = "q" + name = "q" or + name = "s" or + name = "t" or + name = "?{AllAliased}" +} + +private predicate ignoreFile(File file) { + // Ignore standard headers. + file.getBaseName() = ["memory.h", "type_traits.h", "utility.h"] or + not file.fromSource() } module Raw { @@ -29,7 +38,8 @@ module Raw { not ignoreAllocation(memLocation.getAllocation().getAllocationString()) and value = memLocation.toString() and element = instr.toString() and - location = instr.getLocation() + location = instr.getLocation() and + not ignoreFile(location.getFile()) ) } } @@ -52,13 +62,14 @@ module UnaliasedSSA { override predicate hasActualResult(Location location, string element, string tag, string value) { exists(Instruction instr, MemoryLocation memLocation | memLocation = getAMemoryAccess(instr) and - not memLocation instanceof AliasedVirtualVariable and + not memLocation.getVirtualVariable() instanceof AliasedVirtualVariable and not memLocation instanceof AllNonLocalMemory and tag = "ussa" and not ignoreAllocation(memLocation.getAllocation().getAllocationString()) and value = memLocation.toString() and element = instr.toString() and - location = instr.getLocation() + location = instr.getLocation() and + not ignoreFile(location.getFile()) ) } } diff --git a/cpp/ql/test/library-tests/ir/points_to/smart_pointer.cpp b/cpp/ql/test/library-tests/ir/points_to/smart_pointer.cpp new file mode 100644 index 00000000000..de4cbc96e5f --- /dev/null +++ b/cpp/ql/test/library-tests/ir/points_to/smart_pointer.cpp @@ -0,0 +1,33 @@ +#include "../../../include/memory.h" +#include "../../../include/utility.h" + +using std::shared_ptr; +using std::unique_ptr; + +struct S { + int x; +}; + +void unique_ptr_init(S s) { + unique_ptr p(new S); //$ussa=dynamic{1} + int i = (*p).x; //$ussa=dynamic{1}[0..4) + *p = s; //$ussa=dynamic{1}[0..4) + unique_ptr q = std::move(p); + *(q.get()) = s; //$ussa=dynamic{1}[0..4) + shared_ptr t(std::move(q)); + t->x = 5; //$ussa=dynamic{1}[0..4) + *t = s; //$ussa=dynamic{1}[0..4) + *(t.get()) = s; //$ussa=dynamic{1}[0..4) +} + +void shared_ptr_init(S s) { + shared_ptr p(new S); //$ussa=dynamic{1} + int i = (*p).x; //$ussa=dynamic{1}[0..4) + *p = s; //$ussa=dynamic{1}[0..4) + shared_ptr q = std::move(p); + *(q.get()) = s; //$ussa=dynamic{1}[0..4) + shared_ptr t(q); + t->x = 5; //$ussa=dynamic{1}[0..4) + *t = s; //$ussa=dynamic{1}[0..4) + *(t.get()) = s; //$ussa=dynamic{1}[0..4) +} diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected index c4019a38251..59505ce6f39 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected @@ -219,11 +219,11 @@ ssa.cpp: #-----| Goto -> Block 1 # 69| Block 1 -# 69| m69_1(char *) = Phi : from 0:m68_8, from 2:m70_6 -# 69| m69_2(int) = Phi : from 0:m68_6, from 2:m69_8 -# 69| m69_3(unknown) = Phi : from 0:~m68_4, from 2:~m70_10 +# 69| m69_1(unknown) = Phi : from 0:~m68_4, from 2:~m70_10 +# 69| m69_2(char *) = Phi : from 0:m68_8, from 2:m70_6 +# 69| m69_3(int) = Phi : from 0:m68_6, from 2:m69_8 # 69| r69_4(glval) = VariableAddress[n] : -# 69| r69_5(int) = Load[n] : &:r69_4, m69_2 +# 69| r69_5(int) = Load[n] : &:r69_4, m69_3 # 69| r69_6(int) = Constant[1] : # 69| r69_7(int) = Sub : r69_5, r69_6 # 69| m69_8(int) = Store[n] : &:r69_4, r69_7 @@ -237,21 +237,21 @@ ssa.cpp: # 70| Block 2 # 70| r70_1(char) = Constant[0] : # 70| r70_2(glval) = VariableAddress[p] : -# 70| r70_3(char *) = Load[p] : &:r70_2, m69_1 +# 70| r70_3(char *) = Load[p] : &:r70_2, m69_2 # 70| r70_4(int) = Constant[1] : # 70| r70_5(char *) = PointerAdd[1] : r70_3, r70_4 # 70| m70_6(char *) = Store[p] : &:r70_2, r70_5 # 70| r70_7(char *) = CopyValue : r70_3 # 70| r70_8(glval) = CopyValue : r70_7 # 70| m70_9(char) = Store[?] : &:r70_8, r70_1 -# 70| m70_10(unknown) = Chi : total:m69_3, partial:m70_9 +# 70| m70_10(unknown) = Chi : total:m69_1, partial:m70_9 #-----| Goto (back edge) -> Block 1 # 71| Block 3 # 71| v71_1(void) = NoOp : # 68| v68_11(void) = ReturnIndirection[p] : &:r68_9, m68_10 # 68| v68_12(void) = ReturnVoid : -# 68| v68_13(void) = AliasedUse : ~m69_3 +# 68| v68_13(void) = AliasedUse : ~m69_1 # 68| v68_14(void) = ExitFunction : # 75| void ScalarPhi(bool) @@ -1059,7 +1059,7 @@ ssa.cpp: # 241| v241_3(void) = Call[g] : func:r241_2, this:r241_1 # 241| m241_4(unknown) = ^CallSideEffect : ~m240_7 # 241| m241_5(unknown) = Chi : total:m240_7, partial:m241_4 -# 241| v241_6(void) = ^BufferReadSideEffect[-1] : &:r241_1, ~m240_9 +# 241| v241_6(void) = ^IndirectReadSideEffect[-1] : &:r241_1, m240_9 # 241| m241_7(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r241_1 # 241| m241_8(Constructible) = Chi : total:m240_9, partial:m241_7 # 242| r242_1(glval) = VariableAddress[c] : @@ -1067,7 +1067,7 @@ ssa.cpp: # 242| v242_3(void) = Call[g] : func:r242_2, this:r242_1 # 242| m242_4(unknown) = ^CallSideEffect : ~m241_5 # 242| m242_5(unknown) = Chi : total:m241_5, partial:m242_4 -# 242| v242_6(void) = ^BufferReadSideEffect[-1] : &:r242_1, ~m241_8 +# 242| v242_6(void) = ^IndirectReadSideEffect[-1] : &:r242_1, m241_8 # 242| m242_7(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r242_1 # 242| m242_8(Constructible) = Chi : total:m241_8, partial:m242_7 # 243| r243_1(glval) = VariableAddress[c2] : @@ -1084,7 +1084,7 @@ ssa.cpp: # 244| v244_3(void) = Call[g] : func:r244_2, this:r244_1 # 244| m244_4(unknown) = ^CallSideEffect : ~m243_7 # 244| m244_5(unknown) = Chi : total:m243_7, partial:m244_4 -# 244| v244_6(void) = ^BufferReadSideEffect[-1] : &:r244_1, ~m243_9 +# 244| v244_6(void) = ^IndirectReadSideEffect[-1] : &:r244_1, m243_9 # 244| m244_7(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r244_1 # 244| m244_8(Constructible) = Chi : total:m243_9, partial:m244_7 # 245| v245_1(void) = NoOp : @@ -1502,3 +1502,315 @@ ssa.cpp: # 310| v310_12(void) = ReturnVoid : # 310| v310_13(void) = AliasedUse : m310_3 # 310| v310_14(void) = ExitFunction : + +# 319| void DoubleIndirectionEscapes(char*) +# 319| Block 0 +# 319| v319_1(void) = EnterFunction : +# 319| m319_2(unknown) = AliasedDefinition : +# 319| m319_3(unknown) = InitializeNonLocal : +# 319| m319_4(unknown) = Chi : total:m319_2, partial:m319_3 +# 319| r319_5(glval) = VariableAddress[s] : +# 319| m319_6(char *) = InitializeParameter[s] : &:r319_5 +# 319| r319_7(char *) = Load[s] : &:r319_5, m319_6 +# 319| m319_8(unknown) = InitializeIndirection[s] : &:r319_7 +# 321| r321_1(glval) = VariableAddress[buffer] : +# 321| m321_2(char[1024]) = Uninitialized[buffer] : &:r321_1 +# 321| m321_3(unknown) = Chi : total:m319_4, partial:m321_2 +# 322| r322_1(glval) = VariableAddress[ptr1] : +# 322| m322_2(char *) = Uninitialized[ptr1] : &:r322_1 +# 322| m322_3(unknown) = Chi : total:m321_3, partial:m322_2 +# 322| r322_4(glval) = VariableAddress[ptr2] : +# 322| m322_5(char **) = Uninitialized[ptr2] : &:r322_4 +# 323| r323_1(glval) = VariableAddress[ptr3] : +# 323| m323_2(char *) = Uninitialized[ptr3] : &:r323_1 +# 323| r323_3(glval) = VariableAddress[ptr4] : +# 323| m323_4(char **) = Uninitialized[ptr4] : &:r323_3 +# 325| r325_1(glval) = VariableAddress[buffer] : +# 325| r325_2(char *) = Convert : r325_1 +# 325| r325_3(glval) = VariableAddress[ptr1] : +# 325| m325_4(char *) = Store[ptr1] : &:r325_3, r325_2 +# 325| m325_5(unknown) = Chi : total:m322_3, partial:m325_4 +# 326| r326_1(glval) = VariableAddress[ptr1] : +# 326| r326_2(char **) = CopyValue : r326_1 +# 326| r326_3(glval) = VariableAddress[ptr2] : +# 326| m326_4(char **) = Store[ptr2] : &:r326_3, r326_2 +# 327| r327_1(glval) = FunctionAddress[memcpy] : +# 327| r327_2(glval) = VariableAddress[ptr2] : +# 327| r327_3(char **) = Load[ptr2] : &:r327_2, m326_4 +# 327| r327_4(char *) = Load[?] : &:r327_3, m325_4 +# 327| r327_5(void *) = Convert : r327_4 +# 327| r327_6(glval) = VariableAddress[s] : +# 327| r327_7(char *) = Load[s] : &:r327_6, m319_6 +# 327| r327_8(void *) = Convert : r327_7 +# 327| r327_9(int) = Constant[1024] : +# 327| r327_10(void *) = Call[memcpy] : func:r327_1, 0:r327_5, 1:r327_8, 2:r327_9 +# 327| v327_11(void) = ^SizedBufferReadSideEffect[1] : &:r327_8, r327_9, ~m319_8 +# 327| m327_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r327_5, r327_9 +# 327| m327_13(unknown) = Chi : total:m325_5, partial:m327_12 +# 329| r329_1(glval) = FunctionAddress[sink] : +# 329| r329_2(glval) = VariableAddress[buffer] : +# 329| r329_3(char *) = Convert : r329_2 +# 329| v329_4(void) = Call[sink] : func:r329_1, 0:r329_3 +# 329| m329_5(unknown) = ^CallSideEffect : ~m327_13 +# 329| m329_6(unknown) = Chi : total:m327_13, partial:m329_5 +# 329| v329_7(void) = ^BufferReadSideEffect[0] : &:r329_3, ~m329_6 +# 329| m329_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r329_3 +# 329| m329_9(unknown) = Chi : total:m329_6, partial:m329_8 +# 330| r330_1(glval) = FunctionAddress[sink] : +# 330| r330_2(glval) = VariableAddress[ptr1] : +# 330| r330_3(char *) = Load[ptr1] : &:r330_2, ~m329_6 +# 330| v330_4(void) = Call[sink] : func:r330_1, 0:r330_3 +# 330| m330_5(unknown) = ^CallSideEffect : ~m329_9 +# 330| m330_6(unknown) = Chi : total:m329_9, partial:m330_5 +# 330| v330_7(void) = ^BufferReadSideEffect[0] : &:r330_3, ~m330_6 +# 330| m330_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r330_3 +# 330| m330_9(unknown) = Chi : total:m330_6, partial:m330_8 +# 331| r331_1(glval) = FunctionAddress[sink] : +# 331| r331_2(glval) = VariableAddress[ptr2] : +# 331| r331_3(char **) = Load[ptr2] : &:r331_2, m326_4 +# 331| v331_4(void) = Call[sink] : func:r331_1, 0:r331_3 +# 331| m331_5(unknown) = ^CallSideEffect : ~m330_9 +# 331| m331_6(unknown) = Chi : total:m330_9, partial:m331_5 +# 331| v331_7(void) = ^BufferReadSideEffect[0] : &:r331_3, ~m331_6 +# 331| m331_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r331_3 +# 331| m331_9(unknown) = Chi : total:m331_6, partial:m331_8 +# 332| r332_1(glval) = FunctionAddress[sink] : +# 332| r332_2(glval) = VariableAddress[ptr2] : +# 332| r332_3(char **) = Load[ptr2] : &:r332_2, m326_4 +# 332| r332_4(char *) = Load[?] : &:r332_3, ~m331_9 +# 332| v332_5(void) = Call[sink] : func:r332_1, 0:r332_4 +# 332| m332_6(unknown) = ^CallSideEffect : ~m331_9 +# 332| m332_7(unknown) = Chi : total:m331_9, partial:m332_6 +# 332| v332_8(void) = ^BufferReadSideEffect[0] : &:r332_4, ~m332_7 +# 332| m332_9(unknown) = ^BufferMayWriteSideEffect[0] : &:r332_4 +# 332| m332_10(unknown) = Chi : total:m332_7, partial:m332_9 +# 333| v333_1(void) = NoOp : +# 319| v319_9(void) = ReturnIndirection[s] : &:r319_7, m319_8 +# 319| v319_10(void) = ReturnVoid : +# 319| v319_11(void) = AliasedUse : ~m332_10 +# 319| v319_12(void) = ExitFunction : + +# 335| int UnreachablePhiOperand(int, int) +# 335| Block 0 +# 335| v335_1(void) = EnterFunction : +# 335| m335_2(unknown) = AliasedDefinition : +# 335| m335_3(unknown) = InitializeNonLocal : +# 335| m335_4(unknown) = Chi : total:m335_2, partial:m335_3 +# 335| r335_5(glval) = VariableAddress[x] : +# 335| m335_6(int) = InitializeParameter[x] : &:r335_5 +# 335| r335_7(glval) = VariableAddress[y] : +# 335| m335_8(int) = InitializeParameter[y] : &:r335_7 +# 336| r336_1(glval) = VariableAddress[b] : +# 336| r336_2(bool) = Constant[1] : +# 336| m336_3(bool) = Store[b] : &:r336_1, r336_2 +# 337| r337_1(glval) = VariableAddress[ret] : +# 337| m337_2(int) = Uninitialized[ret] : &:r337_1 +# 339| r339_1(glval) = VariableAddress[b] : +# 339| r339_2(bool) = Load[b] : &:r339_1, m336_3 +# 339| v339_3(void) = ConditionalBranch : r339_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 340| Block 1 +# 340| r340_1(glval) = VariableAddress[x] : +# 340| r340_2(int) = Load[x] : &:r340_1, m335_6 +# 340| r340_3(glval) = VariableAddress[ret] : +# 340| m340_4(int) = Store[ret] : &:r340_3, r340_2 +# 345| r345_1(glval) = VariableAddress[#return] : +# 345| r345_2(glval) = VariableAddress[ret] : +# 345| r345_3(int) = Load[ret] : &:r345_2, m340_4 +# 345| m345_4(int) = Store[#return] : &:r345_1, r345_3 +# 335| r335_9(glval) = VariableAddress[#return] : +# 335| v335_10(void) = ReturnValue : &:r335_9, m345_4 +# 335| v335_11(void) = AliasedUse : m335_3 +# 335| v335_12(void) = ExitFunction : + +# 335| Block 2 +# 335| v335_13(void) = Unreached : + +# 348| int UnreachablePhiOperand2(int, int, int, bool) +# 348| Block 0 +# 348| v348_1(void) = EnterFunction : +# 348| m348_2(unknown) = AliasedDefinition : +# 348| m348_3(unknown) = InitializeNonLocal : +# 348| m348_4(unknown) = Chi : total:m348_2, partial:m348_3 +# 348| r348_5(glval) = VariableAddress[x] : +# 348| m348_6(int) = InitializeParameter[x] : &:r348_5 +# 348| r348_7(glval) = VariableAddress[y] : +# 348| m348_8(int) = InitializeParameter[y] : &:r348_7 +# 348| r348_9(glval) = VariableAddress[z] : +# 348| m348_10(int) = InitializeParameter[z] : &:r348_9 +# 348| r348_11(glval) = VariableAddress[b1] : +# 348| m348_12(bool) = InitializeParameter[b1] : &:r348_11 +# 349| r349_1(glval) = VariableAddress[b2] : +# 349| r349_2(bool) = Constant[1] : +# 349| m349_3(bool) = Store[b2] : &:r349_1, r349_2 +# 350| r350_1(glval) = VariableAddress[ret] : +# 350| m350_2(int) = Uninitialized[ret] : &:r350_1 +# 352| r352_1(glval) = VariableAddress[b1] : +# 352| r352_2(bool) = Load[b1] : &:r352_1, m348_12 +# 352| v352_3(void) = ConditionalBranch : r352_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 353| Block 1 +# 353| r353_1(glval) = VariableAddress[x] : +# 353| r353_2(int) = Load[x] : &:r353_1, m348_6 +# 353| r353_3(glval) = VariableAddress[ret] : +# 353| m353_4(int) = Store[ret] : &:r353_3, r353_2 +#-----| Goto -> Block 4 + +# 355| Block 2 +# 355| r355_1(glval) = VariableAddress[b2] : +# 355| r355_2(bool) = Load[b2] : &:r355_1, m349_3 +# 355| v355_3(void) = ConditionalBranch : r355_2 +#-----| False -> Block 5 +#-----| True -> Block 3 + +# 356| Block 3 +# 356| r356_1(glval) = VariableAddress[y] : +# 356| r356_2(int) = Load[y] : &:r356_1, m348_8 +# 356| r356_3(glval) = VariableAddress[ret] : +# 356| m356_4(int) = Store[ret] : &:r356_3, r356_2 +#-----| Goto -> Block 4 + +# 362| Block 4 +# 362| m362_1(int) = Phi : from 1:m353_4, from 3:m356_4 +# 362| r362_2(glval) = VariableAddress[#return] : +# 362| r362_3(glval) = VariableAddress[ret] : +# 362| r362_4(int) = Load[ret] : &:r362_3, m362_1 +# 362| m362_5(int) = Store[#return] : &:r362_2, r362_4 +# 348| r348_13(glval) = VariableAddress[#return] : +# 348| v348_14(void) = ReturnValue : &:r348_13, m362_5 +# 348| v348_15(void) = AliasedUse : m348_3 +# 348| v348_16(void) = ExitFunction : + +# 348| Block 5 +# 348| v348_17(void) = Unreached : + +# 365| int DegeneratePhi(int, int, bool) +# 365| Block 0 +# 365| v365_1(void) = EnterFunction : +# 365| m365_2(unknown) = AliasedDefinition : +# 365| m365_3(unknown) = InitializeNonLocal : +# 365| m365_4(unknown) = Chi : total:m365_2, partial:m365_3 +# 365| r365_5(glval) = VariableAddress[x] : +# 365| m365_6(int) = InitializeParameter[x] : &:r365_5 +# 365| r365_7(glval) = VariableAddress[y] : +# 365| m365_8(int) = InitializeParameter[y] : &:r365_7 +# 365| r365_9(glval) = VariableAddress[b1] : +# 365| m365_10(bool) = InitializeParameter[b1] : &:r365_9 +# 366| r366_1(glval) = VariableAddress[b2] : +# 366| r366_2(bool) = Constant[1] : +# 366| m366_3(bool) = Store[b2] : &:r366_1, r366_2 +# 367| r367_1(glval) = VariableAddress[ret1] : +# 367| m367_2(int) = Uninitialized[ret1] : &:r367_1 +# 368| r368_1(glval) = VariableAddress[ret2] : +# 368| r368_2(glval) = VariableAddress[x] : +# 368| r368_3(int) = Load[x] : &:r368_2, m365_6 +# 368| m368_4(int) = Store[ret2] : &:r368_1, r368_3 +# 370| r370_1(glval) = VariableAddress[b1] : +# 370| r370_2(bool) = Load[b1] : &:r370_1, m365_10 +# 370| v370_3(void) = ConditionalBranch : r370_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 371| Block 1 +# 371| r371_1(glval) = VariableAddress[x] : +# 371| r371_2(int) = Load[x] : &:r371_1, m365_6 +# 371| r371_3(glval) = VariableAddress[ret1] : +# 371| m371_4(int) = Store[ret1] : &:r371_3, r371_2 +#-----| Goto -> Block 4 + +# 373| Block 2 +# 373| r373_1(glval) = VariableAddress[b2] : +# 373| r373_2(bool) = Load[b2] : &:r373_1, m366_3 +# 373| v373_3(void) = ConditionalBranch : r373_2 +#-----| False -> Block 5 +#-----| True -> Block 3 + +# 374| Block 3 +# 374| r374_1(glval) = VariableAddress[x] : +# 374| r374_2(int) = Load[x] : &:r374_1, m365_6 +# 374| r374_3(glval) = VariableAddress[ret1] : +# 374| m374_4(int) = Store[ret1] : &:r374_3, r374_2 +#-----| Goto -> Block 4 + +# 380| Block 4 +# 380| m380_1(int) = Phi : from 1:m368_4, from 3:m368_4 +# 380| m380_2(int) = Phi : from 1:m371_4, from 3:m374_4 +# 380| r380_3(glval) = VariableAddress[#return] : +# 380| r380_4(glval) = VariableAddress[ret1] : +# 380| r380_5(int) = Load[ret1] : &:r380_4, m380_2 +# 380| r380_6(glval) = VariableAddress[ret2] : +# 380| r380_7(int) = Load[ret2] : &:r380_6, m380_1 +# 380| r380_8(int) = Add : r380_5, r380_7 +# 380| m380_9(int) = Store[#return] : &:r380_3, r380_8 +# 365| r365_11(glval) = VariableAddress[#return] : +# 365| v365_12(void) = ReturnValue : &:r365_11, m380_9 +# 365| v365_13(void) = AliasedUse : m365_3 +# 365| v365_14(void) = ExitFunction : + +# 365| Block 5 +# 365| v365_15(void) = Unreached : + +# 383| int FusedBlockPhiOperand(int, int, int, bool) +# 383| Block 0 +# 383| v383_1(void) = EnterFunction : +# 383| m383_2(unknown) = AliasedDefinition : +# 383| m383_3(unknown) = InitializeNonLocal : +# 383| m383_4(unknown) = Chi : total:m383_2, partial:m383_3 +# 383| r383_5(glval) = VariableAddress[x] : +# 383| m383_6(int) = InitializeParameter[x] : &:r383_5 +# 383| r383_7(glval) = VariableAddress[y] : +# 383| m383_8(int) = InitializeParameter[y] : &:r383_7 +# 383| r383_9(glval) = VariableAddress[z] : +# 383| m383_10(int) = InitializeParameter[z] : &:r383_9 +# 383| r383_11(glval) = VariableAddress[b1] : +# 383| m383_12(bool) = InitializeParameter[b1] : &:r383_11 +# 384| r384_1(glval) = VariableAddress[b2] : +# 384| r384_2(bool) = Constant[1] : +# 384| m384_3(bool) = Store[b2] : &:r384_1, r384_2 +# 385| r385_1(glval) = VariableAddress[ret] : +# 385| m385_2(int) = Uninitialized[ret] : &:r385_1 +# 387| r387_1(glval) = VariableAddress[b1] : +# 387| r387_2(bool) = Load[b1] : &:r387_1, m383_12 +# 387| v387_3(void) = ConditionalBranch : r387_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 388| Block 1 +# 388| r388_1(glval) = VariableAddress[x] : +# 388| r388_2(int) = Load[x] : &:r388_1, m383_6 +# 388| r388_3(glval) = VariableAddress[ret] : +# 388| m388_4(int) = Store[ret] : &:r388_3, r388_2 +#-----| Goto -> Block 4 + +# 390| Block 2 +# 390| r390_1(glval) = VariableAddress[b2] : +# 390| r390_2(bool) = Load[b2] : &:r390_1, m384_3 +# 390| v390_3(void) = ConditionalBranch : r390_2 +#-----| False -> Block 5 +#-----| True -> Block 3 + +# 391| Block 3 +# 391| r391_1(glval) = VariableAddress[y] : +# 391| r391_2(int) = Load[y] : &:r391_1, m383_8 +# 391| r391_3(glval) = VariableAddress[ret] : +# 391| m391_4(int) = Store[ret] : &:r391_3, r391_2 +# 395| v395_1(void) = NoOp : +#-----| Goto -> Block 4 + +# 398| Block 4 +# 398| m398_1(int) = Phi : from 1:m388_4, from 3:m391_4 +# 398| r398_2(glval) = VariableAddress[#return] : +# 398| r398_3(glval) = VariableAddress[ret] : +# 398| r398_4(int) = Load[ret] : &:r398_3, m398_1 +# 398| m398_5(int) = Store[#return] : &:r398_2, r398_4 +# 383| r383_13(glval) = VariableAddress[#return] : +# 383| v383_14(void) = ReturnValue : &:r383_13, m398_5 +# 383| v383_15(void) = AliasedUse : m383_3 +# 383| v383_16(void) = ExitFunction : + +# 383| Block 5 +# 383| v383_17(void) = Unreached : diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected index a101e638cd0..b87b65363dc 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected @@ -219,11 +219,11 @@ ssa.cpp: #-----| Goto -> Block 1 # 69| Block 1 -# 69| m69_1(char *) = Phi : from 0:m68_8, from 2:m70_6 -# 69| m69_2(int) = Phi : from 0:m68_6, from 2:m69_8 -# 69| m69_3(unknown) = Phi : from 0:~m68_4, from 2:~m70_10 +# 69| m69_1(unknown) = Phi : from 0:~m68_4, from 2:~m70_10 +# 69| m69_2(char *) = Phi : from 0:m68_8, from 2:m70_6 +# 69| m69_3(int) = Phi : from 0:m68_6, from 2:m69_8 # 69| r69_4(glval) = VariableAddress[n] : -# 69| r69_5(int) = Load[n] : &:r69_4, m69_2 +# 69| r69_5(int) = Load[n] : &:r69_4, m69_3 # 69| r69_6(int) = Constant[1] : # 69| r69_7(int) = Sub : r69_5, r69_6 # 69| m69_8(int) = Store[n] : &:r69_4, r69_7 @@ -237,21 +237,21 @@ ssa.cpp: # 70| Block 2 # 70| r70_1(char) = Constant[0] : # 70| r70_2(glval) = VariableAddress[p] : -# 70| r70_3(char *) = Load[p] : &:r70_2, m69_1 +# 70| r70_3(char *) = Load[p] : &:r70_2, m69_2 # 70| r70_4(int) = Constant[1] : # 70| r70_5(char *) = PointerAdd[1] : r70_3, r70_4 # 70| m70_6(char *) = Store[p] : &:r70_2, r70_5 # 70| r70_7(char *) = CopyValue : r70_3 # 70| r70_8(glval) = CopyValue : r70_7 # 70| m70_9(char) = Store[?] : &:r70_8, r70_1 -# 70| m70_10(unknown) = Chi : total:m69_3, partial:m70_9 +# 70| m70_10(unknown) = Chi : total:m69_1, partial:m70_9 #-----| Goto (back edge) -> Block 1 # 71| Block 3 # 71| v71_1(void) = NoOp : # 68| v68_11(void) = ReturnIndirection[p] : &:r68_9, m68_10 # 68| v68_12(void) = ReturnVoid : -# 68| v68_13(void) = AliasedUse : ~m69_3 +# 68| v68_13(void) = AliasedUse : ~m69_1 # 68| v68_14(void) = ExitFunction : # 75| void ScalarPhi(bool) @@ -1054,7 +1054,7 @@ ssa.cpp: # 241| v241_3(void) = Call[g] : func:r241_2, this:r241_1 # 241| m241_4(unknown) = ^CallSideEffect : ~m240_7 # 241| m241_5(unknown) = Chi : total:m240_7, partial:m241_4 -# 241| v241_6(void) = ^BufferReadSideEffect[-1] : &:r241_1, ~m240_9 +# 241| v241_6(void) = ^IndirectReadSideEffect[-1] : &:r241_1, m240_9 # 241| m241_7(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r241_1 # 241| m241_8(Constructible) = Chi : total:m240_9, partial:m241_7 # 242| r242_1(glval) = VariableAddress[c] : @@ -1062,7 +1062,7 @@ ssa.cpp: # 242| v242_3(void) = Call[g] : func:r242_2, this:r242_1 # 242| m242_4(unknown) = ^CallSideEffect : ~m241_5 # 242| m242_5(unknown) = Chi : total:m241_5, partial:m242_4 -# 242| v242_6(void) = ^BufferReadSideEffect[-1] : &:r242_1, ~m241_8 +# 242| v242_6(void) = ^IndirectReadSideEffect[-1] : &:r242_1, m241_8 # 242| m242_7(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r242_1 # 242| m242_8(Constructible) = Chi : total:m241_8, partial:m242_7 # 243| r243_1(glval) = VariableAddress[c2] : @@ -1079,7 +1079,7 @@ ssa.cpp: # 244| v244_3(void) = Call[g] : func:r244_2, this:r244_1 # 244| m244_4(unknown) = ^CallSideEffect : ~m243_7 # 244| m244_5(unknown) = Chi : total:m243_7, partial:m244_4 -# 244| v244_6(void) = ^BufferReadSideEffect[-1] : &:r244_1, ~m243_9 +# 244| v244_6(void) = ^IndirectReadSideEffect[-1] : &:r244_1, m243_9 # 244| m244_7(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r244_1 # 244| m244_8(Constructible) = Chi : total:m243_9, partial:m244_7 # 245| v245_1(void) = NoOp : @@ -1495,3 +1495,312 @@ ssa.cpp: # 310| v310_12(void) = ReturnVoid : # 310| v310_13(void) = AliasedUse : m310_3 # 310| v310_14(void) = ExitFunction : + +# 319| void DoubleIndirectionEscapes(char*) +# 319| Block 0 +# 319| v319_1(void) = EnterFunction : +# 319| m319_2(unknown) = AliasedDefinition : +# 319| m319_3(unknown) = InitializeNonLocal : +# 319| m319_4(unknown) = Chi : total:m319_2, partial:m319_3 +# 319| r319_5(glval) = VariableAddress[s] : +# 319| m319_6(char *) = InitializeParameter[s] : &:r319_5 +# 319| r319_7(char *) = Load[s] : &:r319_5, m319_6 +# 319| m319_8(unknown) = InitializeIndirection[s] : &:r319_7 +# 321| r321_1(glval) = VariableAddress[buffer] : +# 321| m321_2(char[1024]) = Uninitialized[buffer] : &:r321_1 +# 322| r322_1(glval) = VariableAddress[ptr1] : +# 322| m322_2(char *) = Uninitialized[ptr1] : &:r322_1 +# 322| r322_3(glval) = VariableAddress[ptr2] : +# 322| m322_4(char **) = Uninitialized[ptr2] : &:r322_3 +# 323| r323_1(glval) = VariableAddress[ptr3] : +# 323| m323_2(char *) = Uninitialized[ptr3] : &:r323_1 +# 323| r323_3(glval) = VariableAddress[ptr4] : +# 323| m323_4(char **) = Uninitialized[ptr4] : &:r323_3 +# 325| r325_1(glval) = VariableAddress[buffer] : +# 325| r325_2(char *) = Convert : r325_1 +# 325| r325_3(glval) = VariableAddress[ptr1] : +# 325| m325_4(char *) = Store[ptr1] : &:r325_3, r325_2 +# 326| r326_1(glval) = VariableAddress[ptr1] : +# 326| r326_2(char **) = CopyValue : r326_1 +# 326| r326_3(glval) = VariableAddress[ptr2] : +# 326| m326_4(char **) = Store[ptr2] : &:r326_3, r326_2 +# 327| r327_1(glval) = FunctionAddress[memcpy] : +# 327| r327_2(glval) = VariableAddress[ptr2] : +# 327| r327_3(char **) = Load[ptr2] : &:r327_2, m326_4 +# 327| r327_4(char *) = Load[?] : &:r327_3, m325_4 +# 327| r327_5(void *) = Convert : r327_4 +# 327| r327_6(glval) = VariableAddress[s] : +# 327| r327_7(char *) = Load[s] : &:r327_6, m319_6 +# 327| r327_8(void *) = Convert : r327_7 +# 327| r327_9(int) = Constant[1024] : +# 327| r327_10(void *) = Call[memcpy] : func:r327_1, 0:r327_5, 1:r327_8, 2:r327_9 +# 327| v327_11(void) = ^SizedBufferReadSideEffect[1] : &:r327_8, r327_9, ~m319_8 +# 327| m327_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r327_5, r327_9 +# 327| m327_13(unknown) = Chi : total:m319_4, partial:m327_12 +# 329| r329_1(glval) = FunctionAddress[sink] : +# 329| r329_2(glval) = VariableAddress[buffer] : +# 329| r329_3(char *) = Convert : r329_2 +# 329| v329_4(void) = Call[sink] : func:r329_1, 0:r329_3 +# 329| m329_5(unknown) = ^CallSideEffect : ~m327_13 +# 329| m329_6(unknown) = Chi : total:m327_13, partial:m329_5 +# 329| v329_7(void) = ^BufferReadSideEffect[0] : &:r329_3, ~m321_2 +# 329| m329_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r329_3 +# 329| m329_9(char[1024]) = Chi : total:m321_2, partial:m329_8 +# 330| r330_1(glval) = FunctionAddress[sink] : +# 330| r330_2(glval) = VariableAddress[ptr1] : +# 330| r330_3(char *) = Load[ptr1] : &:r330_2, m325_4 +# 330| v330_4(void) = Call[sink] : func:r330_1, 0:r330_3 +# 330| m330_5(unknown) = ^CallSideEffect : ~m329_6 +# 330| m330_6(unknown) = Chi : total:m329_6, partial:m330_5 +# 330| v330_7(void) = ^BufferReadSideEffect[0] : &:r330_3, ~m330_6 +# 330| m330_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r330_3 +# 330| m330_9(unknown) = Chi : total:m330_6, partial:m330_8 +# 331| r331_1(glval) = FunctionAddress[sink] : +# 331| r331_2(glval) = VariableAddress[ptr2] : +# 331| r331_3(char **) = Load[ptr2] : &:r331_2, m326_4 +# 331| v331_4(void) = Call[sink] : func:r331_1, 0:r331_3 +# 331| m331_5(unknown) = ^CallSideEffect : ~m330_9 +# 331| m331_6(unknown) = Chi : total:m330_9, partial:m331_5 +# 331| v331_7(void) = ^BufferReadSideEffect[0] : &:r331_3, ~m325_4 +# 331| m331_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r331_3 +# 331| m331_9(char *) = Chi : total:m325_4, partial:m331_8 +# 332| r332_1(glval) = FunctionAddress[sink] : +# 332| r332_2(glval) = VariableAddress[ptr2] : +# 332| r332_3(char **) = Load[ptr2] : &:r332_2, m326_4 +# 332| r332_4(char *) = Load[?] : &:r332_3, m331_9 +# 332| v332_5(void) = Call[sink] : func:r332_1, 0:r332_4 +# 332| m332_6(unknown) = ^CallSideEffect : ~m331_6 +# 332| m332_7(unknown) = Chi : total:m331_6, partial:m332_6 +# 332| v332_8(void) = ^BufferReadSideEffect[0] : &:r332_4, ~m332_7 +# 332| m332_9(unknown) = ^BufferMayWriteSideEffect[0] : &:r332_4 +# 332| m332_10(unknown) = Chi : total:m332_7, partial:m332_9 +# 333| v333_1(void) = NoOp : +# 319| v319_9(void) = ReturnIndirection[s] : &:r319_7, m319_8 +# 319| v319_10(void) = ReturnVoid : +# 319| v319_11(void) = AliasedUse : ~m332_10 +# 319| v319_12(void) = ExitFunction : + +# 335| int UnreachablePhiOperand(int, int) +# 335| Block 0 +# 335| v335_1(void) = EnterFunction : +# 335| m335_2(unknown) = AliasedDefinition : +# 335| m335_3(unknown) = InitializeNonLocal : +# 335| m335_4(unknown) = Chi : total:m335_2, partial:m335_3 +# 335| r335_5(glval) = VariableAddress[x] : +# 335| m335_6(int) = InitializeParameter[x] : &:r335_5 +# 335| r335_7(glval) = VariableAddress[y] : +# 335| m335_8(int) = InitializeParameter[y] : &:r335_7 +# 336| r336_1(glval) = VariableAddress[b] : +# 336| r336_2(bool) = Constant[1] : +# 336| m336_3(bool) = Store[b] : &:r336_1, r336_2 +# 337| r337_1(glval) = VariableAddress[ret] : +# 337| m337_2(int) = Uninitialized[ret] : &:r337_1 +# 339| r339_1(glval) = VariableAddress[b] : +# 339| r339_2(bool) = Load[b] : &:r339_1, m336_3 +# 339| v339_3(void) = ConditionalBranch : r339_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 340| Block 1 +# 340| r340_1(glval) = VariableAddress[x] : +# 340| r340_2(int) = Load[x] : &:r340_1, m335_6 +# 340| r340_3(glval) = VariableAddress[ret] : +# 340| m340_4(int) = Store[ret] : &:r340_3, r340_2 +# 345| r345_1(glval) = VariableAddress[#return] : +# 345| r345_2(glval) = VariableAddress[ret] : +# 345| r345_3(int) = Load[ret] : &:r345_2, m340_4 +# 345| m345_4(int) = Store[#return] : &:r345_1, r345_3 +# 335| r335_9(glval) = VariableAddress[#return] : +# 335| v335_10(void) = ReturnValue : &:r335_9, m345_4 +# 335| v335_11(void) = AliasedUse : m335_3 +# 335| v335_12(void) = ExitFunction : + +# 335| Block 2 +# 335| v335_13(void) = Unreached : + +# 348| int UnreachablePhiOperand2(int, int, int, bool) +# 348| Block 0 +# 348| v348_1(void) = EnterFunction : +# 348| m348_2(unknown) = AliasedDefinition : +# 348| m348_3(unknown) = InitializeNonLocal : +# 348| m348_4(unknown) = Chi : total:m348_2, partial:m348_3 +# 348| r348_5(glval) = VariableAddress[x] : +# 348| m348_6(int) = InitializeParameter[x] : &:r348_5 +# 348| r348_7(glval) = VariableAddress[y] : +# 348| m348_8(int) = InitializeParameter[y] : &:r348_7 +# 348| r348_9(glval) = VariableAddress[z] : +# 348| m348_10(int) = InitializeParameter[z] : &:r348_9 +# 348| r348_11(glval) = VariableAddress[b1] : +# 348| m348_12(bool) = InitializeParameter[b1] : &:r348_11 +# 349| r349_1(glval) = VariableAddress[b2] : +# 349| r349_2(bool) = Constant[1] : +# 349| m349_3(bool) = Store[b2] : &:r349_1, r349_2 +# 350| r350_1(glval) = VariableAddress[ret] : +# 350| m350_2(int) = Uninitialized[ret] : &:r350_1 +# 352| r352_1(glval) = VariableAddress[b1] : +# 352| r352_2(bool) = Load[b1] : &:r352_1, m348_12 +# 352| v352_3(void) = ConditionalBranch : r352_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 353| Block 1 +# 353| r353_1(glval) = VariableAddress[x] : +# 353| r353_2(int) = Load[x] : &:r353_1, m348_6 +# 353| r353_3(glval) = VariableAddress[ret] : +# 353| m353_4(int) = Store[ret] : &:r353_3, r353_2 +#-----| Goto -> Block 4 + +# 355| Block 2 +# 355| r355_1(glval) = VariableAddress[b2] : +# 355| r355_2(bool) = Load[b2] : &:r355_1, m349_3 +# 355| v355_3(void) = ConditionalBranch : r355_2 +#-----| False -> Block 5 +#-----| True -> Block 3 + +# 356| Block 3 +# 356| r356_1(glval) = VariableAddress[y] : +# 356| r356_2(int) = Load[y] : &:r356_1, m348_8 +# 356| r356_3(glval) = VariableAddress[ret] : +# 356| m356_4(int) = Store[ret] : &:r356_3, r356_2 +#-----| Goto -> Block 4 + +# 362| Block 4 +# 362| m362_1(int) = Phi : from 1:m353_4, from 3:m356_4 +# 362| r362_2(glval) = VariableAddress[#return] : +# 362| r362_3(glval) = VariableAddress[ret] : +# 362| r362_4(int) = Load[ret] : &:r362_3, m362_1 +# 362| m362_5(int) = Store[#return] : &:r362_2, r362_4 +# 348| r348_13(glval) = VariableAddress[#return] : +# 348| v348_14(void) = ReturnValue : &:r348_13, m362_5 +# 348| v348_15(void) = AliasedUse : m348_3 +# 348| v348_16(void) = ExitFunction : + +# 348| Block 5 +# 348| v348_17(void) = Unreached : + +# 365| int DegeneratePhi(int, int, bool) +# 365| Block 0 +# 365| v365_1(void) = EnterFunction : +# 365| m365_2(unknown) = AliasedDefinition : +# 365| m365_3(unknown) = InitializeNonLocal : +# 365| m365_4(unknown) = Chi : total:m365_2, partial:m365_3 +# 365| r365_5(glval) = VariableAddress[x] : +# 365| m365_6(int) = InitializeParameter[x] : &:r365_5 +# 365| r365_7(glval) = VariableAddress[y] : +# 365| m365_8(int) = InitializeParameter[y] : &:r365_7 +# 365| r365_9(glval) = VariableAddress[b1] : +# 365| m365_10(bool) = InitializeParameter[b1] : &:r365_9 +# 366| r366_1(glval) = VariableAddress[b2] : +# 366| r366_2(bool) = Constant[1] : +# 366| m366_3(bool) = Store[b2] : &:r366_1, r366_2 +# 367| r367_1(glval) = VariableAddress[ret1] : +# 367| m367_2(int) = Uninitialized[ret1] : &:r367_1 +# 368| r368_1(glval) = VariableAddress[ret2] : +# 368| r368_2(glval) = VariableAddress[x] : +# 368| r368_3(int) = Load[x] : &:r368_2, m365_6 +# 368| m368_4(int) = Store[ret2] : &:r368_1, r368_3 +# 370| r370_1(glval) = VariableAddress[b1] : +# 370| r370_2(bool) = Load[b1] : &:r370_1, m365_10 +# 370| v370_3(void) = ConditionalBranch : r370_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 371| Block 1 +# 371| r371_1(glval) = VariableAddress[x] : +# 371| r371_2(int) = Load[x] : &:r371_1, m365_6 +# 371| r371_3(glval) = VariableAddress[ret1] : +# 371| m371_4(int) = Store[ret1] : &:r371_3, r371_2 +#-----| Goto -> Block 4 + +# 373| Block 2 +# 373| r373_1(glval) = VariableAddress[b2] : +# 373| r373_2(bool) = Load[b2] : &:r373_1, m366_3 +# 373| v373_3(void) = ConditionalBranch : r373_2 +#-----| False -> Block 5 +#-----| True -> Block 3 + +# 374| Block 3 +# 374| r374_1(glval) = VariableAddress[x] : +# 374| r374_2(int) = Load[x] : &:r374_1, m365_6 +# 374| r374_3(glval) = VariableAddress[ret1] : +# 374| m374_4(int) = Store[ret1] : &:r374_3, r374_2 +#-----| Goto -> Block 4 + +# 380| Block 4 +# 380| m380_1(int) = Phi : from 1:m368_4, from 3:m368_4 +# 380| m380_2(int) = Phi : from 1:m371_4, from 3:m374_4 +# 380| r380_3(glval) = VariableAddress[#return] : +# 380| r380_4(glval) = VariableAddress[ret1] : +# 380| r380_5(int) = Load[ret1] : &:r380_4, m380_2 +# 380| r380_6(glval) = VariableAddress[ret2] : +# 380| r380_7(int) = Load[ret2] : &:r380_6, m380_1 +# 380| r380_8(int) = Add : r380_5, r380_7 +# 380| m380_9(int) = Store[#return] : &:r380_3, r380_8 +# 365| r365_11(glval) = VariableAddress[#return] : +# 365| v365_12(void) = ReturnValue : &:r365_11, m380_9 +# 365| v365_13(void) = AliasedUse : m365_3 +# 365| v365_14(void) = ExitFunction : + +# 365| Block 5 +# 365| v365_15(void) = Unreached : + +# 383| int FusedBlockPhiOperand(int, int, int, bool) +# 383| Block 0 +# 383| v383_1(void) = EnterFunction : +# 383| m383_2(unknown) = AliasedDefinition : +# 383| m383_3(unknown) = InitializeNonLocal : +# 383| m383_4(unknown) = Chi : total:m383_2, partial:m383_3 +# 383| r383_5(glval) = VariableAddress[x] : +# 383| m383_6(int) = InitializeParameter[x] : &:r383_5 +# 383| r383_7(glval) = VariableAddress[y] : +# 383| m383_8(int) = InitializeParameter[y] : &:r383_7 +# 383| r383_9(glval) = VariableAddress[z] : +# 383| m383_10(int) = InitializeParameter[z] : &:r383_9 +# 383| r383_11(glval) = VariableAddress[b1] : +# 383| m383_12(bool) = InitializeParameter[b1] : &:r383_11 +# 384| r384_1(glval) = VariableAddress[b2] : +# 384| r384_2(bool) = Constant[1] : +# 384| m384_3(bool) = Store[b2] : &:r384_1, r384_2 +# 385| r385_1(glval) = VariableAddress[ret] : +# 385| m385_2(int) = Uninitialized[ret] : &:r385_1 +# 387| r387_1(glval) = VariableAddress[b1] : +# 387| r387_2(bool) = Load[b1] : &:r387_1, m383_12 +# 387| v387_3(void) = ConditionalBranch : r387_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 388| Block 1 +# 388| r388_1(glval) = VariableAddress[x] : +# 388| r388_2(int) = Load[x] : &:r388_1, m383_6 +# 388| r388_3(glval) = VariableAddress[ret] : +# 388| m388_4(int) = Store[ret] : &:r388_3, r388_2 +#-----| Goto -> Block 4 + +# 390| Block 2 +# 390| r390_1(glval) = VariableAddress[b2] : +# 390| r390_2(bool) = Load[b2] : &:r390_1, m384_3 +# 390| v390_3(void) = ConditionalBranch : r390_2 +#-----| False -> Block 5 +#-----| True -> Block 3 + +# 391| Block 3 +# 391| r391_1(glval) = VariableAddress[y] : +# 391| r391_2(int) = Load[y] : &:r391_1, m383_8 +# 391| r391_3(glval) = VariableAddress[ret] : +# 391| m391_4(int) = Store[ret] : &:r391_3, r391_2 +# 395| v395_1(void) = NoOp : +#-----| Goto -> Block 4 + +# 398| Block 4 +# 398| m398_1(int) = Phi : from 1:m388_4, from 3:m391_4 +# 398| r398_2(glval) = VariableAddress[#return] : +# 398| r398_3(glval) = VariableAddress[ret] : +# 398| r398_4(int) = Load[ret] : &:r398_3, m398_1 +# 398| m398_5(int) = Store[#return] : &:r398_2, r398_4 +# 383| r383_13(glval) = VariableAddress[#return] : +# 383| v383_14(void) = ReturnValue : &:r383_13, m398_5 +# 383| v383_15(void) = AliasedUse : m383_3 +# 383| v383_16(void) = ExitFunction : + +# 383| Block 5 +# 383| v383_17(void) = Unreached : diff --git a/cpp/ql/test/library-tests/ir/ssa/ssa.cpp b/cpp/ql/test/library-tests/ir/ssa/ssa.cpp index 34886b1f343..ec8ea81e9e4 100644 --- a/cpp/ql/test/library-tests/ir/ssa/ssa.cpp +++ b/cpp/ql/test/library-tests/ir/ssa/ssa.cpp @@ -311,3 +311,89 @@ class ThisAliasTest { this->x = arg; } }; + +void sink(char **); +void sink(char *); + +// This test case comes from DefaultTaintTracking. +void DoubleIndirectionEscapes(char *s) +{ + char buffer[1024]; + char *ptr1, **ptr2; + char *ptr3, **ptr4; + + ptr1 = buffer; + ptr2 = &ptr1; + memcpy(*ptr2, s, 1024); + + sink(buffer); // $ MISSING: ast,ir + sink(ptr1); // $ ast MISSING: ir + sink(ptr2); // $ SPURIOUS: ast + sink(*ptr2); // $ ast MISSING: ir +} + +int UnreachablePhiOperand(int x, int y) { + bool b = true; + int ret; + + if(b) { + ret = x; + } else { + ret = y; + } + + return ret; +} + +int UnreachablePhiOperand2(int x, int y, int z, bool b1) { + bool b2 = true; + int ret; + + if(b1) { + ret = x; + } else { + if(b2) { + ret = y; + } else { + ret = z; + } + } + + return ret; +} + +int DegeneratePhi(int x, int y, bool b1) { + bool b2 = true; + int ret1; + int ret2 = x; + + if(b1) { + ret1 = x; + } else { + if(b2) { + ret1 = x; + } else { + ret2 = y; + } + } + + return ret1 + ret2; +} + +int FusedBlockPhiOperand(int x, int y, int z, bool b1) { + bool b2 = true; + int ret; + + if(b1) { + ret = x; + } else { + if(b2) { + ret = y; + } else { + ret = z; + } + ; // creates a NoOp instruction with its own basic block + } + + return ret; +} \ No newline at end of file diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected index 8b6cf17dbbb..9b1ebd7fe6e 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected @@ -983,13 +983,13 @@ ssa.cpp: # 241| r241_2(glval) = FunctionAddress[g] : # 241| v241_3(void) = Call[g] : func:r241_2, this:r241_1 # 241| mu241_4(unknown) = ^CallSideEffect : ~m? -# 241| v241_5(void) = ^BufferReadSideEffect[-1] : &:r241_1, ~m? +# 241| v241_5(void) = ^IndirectReadSideEffect[-1] : &:r241_1, ~m? # 241| mu241_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r241_1 # 242| r242_1(glval) = VariableAddress[c] : # 242| r242_2(glval) = FunctionAddress[g] : # 242| v242_3(void) = Call[g] : func:r242_2, this:r242_1 # 242| mu242_4(unknown) = ^CallSideEffect : ~m? -# 242| v242_5(void) = ^BufferReadSideEffect[-1] : &:r242_1, ~m? +# 242| v242_5(void) = ^IndirectReadSideEffect[-1] : &:r242_1, ~m? # 242| mu242_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r242_1 # 243| r243_1(glval) = VariableAddress[c2] : # 243| mu243_2(Constructible) = Uninitialized[c2] : &:r243_1 @@ -1002,7 +1002,7 @@ ssa.cpp: # 244| r244_2(glval) = FunctionAddress[g] : # 244| v244_3(void) = Call[g] : func:r244_2, this:r244_1 # 244| mu244_4(unknown) = ^CallSideEffect : ~m? -# 244| v244_5(void) = ^BufferReadSideEffect[-1] : &:r244_1, ~m? +# 244| v244_5(void) = ^IndirectReadSideEffect[-1] : &:r244_1, ~m? # 244| mu244_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r244_1 # 245| v245_1(void) = NoOp : # 239| v239_4(void) = ReturnVoid : @@ -1376,3 +1376,322 @@ ssa.cpp: # 310| v310_11(void) = ReturnVoid : # 310| v310_12(void) = AliasedUse : ~m? # 310| v310_13(void) = ExitFunction : + +# 319| void DoubleIndirectionEscapes(char*) +# 319| Block 0 +# 319| v319_1(void) = EnterFunction : +# 319| mu319_2(unknown) = AliasedDefinition : +# 319| mu319_3(unknown) = InitializeNonLocal : +# 319| r319_4(glval) = VariableAddress[s] : +# 319| m319_5(char *) = InitializeParameter[s] : &:r319_4 +# 319| r319_6(char *) = Load[s] : &:r319_4, m319_5 +# 319| mu319_7(unknown) = InitializeIndirection[s] : &:r319_6 +# 321| r321_1(glval) = VariableAddress[buffer] : +# 321| mu321_2(char[1024]) = Uninitialized[buffer] : &:r321_1 +# 322| r322_1(glval) = VariableAddress[ptr1] : +# 322| mu322_2(char *) = Uninitialized[ptr1] : &:r322_1 +# 322| r322_3(glval) = VariableAddress[ptr2] : +# 322| m322_4(char **) = Uninitialized[ptr2] : &:r322_3 +# 323| r323_1(glval) = VariableAddress[ptr3] : +# 323| m323_2(char *) = Uninitialized[ptr3] : &:r323_1 +# 323| r323_3(glval) = VariableAddress[ptr4] : +# 323| m323_4(char **) = Uninitialized[ptr4] : &:r323_3 +# 325| r325_1(glval) = VariableAddress[buffer] : +# 325| r325_2(char *) = Convert : r325_1 +# 325| r325_3(glval) = VariableAddress[ptr1] : +# 325| mu325_4(char *) = Store[ptr1] : &:r325_3, r325_2 +# 326| r326_1(glval) = VariableAddress[ptr1] : +# 326| r326_2(char **) = CopyValue : r326_1 +# 326| r326_3(glval) = VariableAddress[ptr2] : +# 326| m326_4(char **) = Store[ptr2] : &:r326_3, r326_2 +# 327| r327_1(glval) = FunctionAddress[memcpy] : +# 327| r327_2(glval) = VariableAddress[ptr2] : +# 327| r327_3(char **) = Load[ptr2] : &:r327_2, m326_4 +# 327| r327_4(char *) = Load[?] : &:r327_3, ~m? +# 327| r327_5(void *) = Convert : r327_4 +# 327| r327_6(glval) = VariableAddress[s] : +# 327| r327_7(char *) = Load[s] : &:r327_6, m319_5 +# 327| r327_8(void *) = Convert : r327_7 +# 327| r327_9(int) = Constant[1024] : +# 327| r327_10(void *) = Call[memcpy] : func:r327_1, 0:r327_5, 1:r327_8, 2:r327_9 +# 327| v327_11(void) = ^SizedBufferReadSideEffect[1] : &:r327_8, r327_9, ~m? +# 327| mu327_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r327_5, r327_9 +# 329| r329_1(glval) = FunctionAddress[sink] : +# 329| r329_2(glval) = VariableAddress[buffer] : +# 329| r329_3(char *) = Convert : r329_2 +# 329| v329_4(void) = Call[sink] : func:r329_1, 0:r329_3 +# 329| mu329_5(unknown) = ^CallSideEffect : ~m? +# 329| v329_6(void) = ^BufferReadSideEffect[0] : &:r329_3, ~m? +# 329| mu329_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r329_3 +# 330| r330_1(glval) = FunctionAddress[sink] : +# 330| r330_2(glval) = VariableAddress[ptr1] : +# 330| r330_3(char *) = Load[ptr1] : &:r330_2, ~m? +# 330| v330_4(void) = Call[sink] : func:r330_1, 0:r330_3 +# 330| mu330_5(unknown) = ^CallSideEffect : ~m? +# 330| v330_6(void) = ^BufferReadSideEffect[0] : &:r330_3, ~m? +# 330| mu330_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r330_3 +# 331| r331_1(glval) = FunctionAddress[sink] : +# 331| r331_2(glval) = VariableAddress[ptr2] : +# 331| r331_3(char **) = Load[ptr2] : &:r331_2, m326_4 +# 331| v331_4(void) = Call[sink] : func:r331_1, 0:r331_3 +# 331| mu331_5(unknown) = ^CallSideEffect : ~m? +# 331| v331_6(void) = ^BufferReadSideEffect[0] : &:r331_3, ~m? +# 331| mu331_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r331_3 +# 332| r332_1(glval) = FunctionAddress[sink] : +# 332| r332_2(glval) = VariableAddress[ptr2] : +# 332| r332_3(char **) = Load[ptr2] : &:r332_2, m326_4 +# 332| r332_4(char *) = Load[?] : &:r332_3, ~m? +# 332| v332_5(void) = Call[sink] : func:r332_1, 0:r332_4 +# 332| mu332_6(unknown) = ^CallSideEffect : ~m? +# 332| v332_7(void) = ^BufferReadSideEffect[0] : &:r332_4, ~m? +# 332| mu332_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r332_4 +# 333| v333_1(void) = NoOp : +# 319| v319_8(void) = ReturnIndirection[s] : &:r319_6, ~m? +# 319| v319_9(void) = ReturnVoid : +# 319| v319_10(void) = AliasedUse : ~m? +# 319| v319_11(void) = ExitFunction : + +# 335| int UnreachablePhiOperand(int, int) +# 335| Block 0 +# 335| v335_1(void) = EnterFunction : +# 335| mu335_2(unknown) = AliasedDefinition : +# 335| mu335_3(unknown) = InitializeNonLocal : +# 335| r335_4(glval) = VariableAddress[x] : +# 335| m335_5(int) = InitializeParameter[x] : &:r335_4 +# 335| r335_6(glval) = VariableAddress[y] : +# 335| m335_7(int) = InitializeParameter[y] : &:r335_6 +# 336| r336_1(glval) = VariableAddress[b] : +# 336| r336_2(bool) = Constant[1] : +# 336| m336_3(bool) = Store[b] : &:r336_1, r336_2 +# 337| r337_1(glval) = VariableAddress[ret] : +# 337| m337_2(int) = Uninitialized[ret] : &:r337_1 +# 339| r339_1(glval) = VariableAddress[b] : +# 339| r339_2(bool) = Load[b] : &:r339_1, m336_3 +# 339| v339_3(void) = ConditionalBranch : r339_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 340| Block 1 +# 340| r340_1(glval) = VariableAddress[x] : +# 340| r340_2(int) = Load[x] : &:r340_1, m335_5 +# 340| r340_3(glval) = VariableAddress[ret] : +# 340| m340_4(int) = Store[ret] : &:r340_3, r340_2 +#-----| Goto -> Block 3 + +# 342| Block 2 +# 342| r342_1(glval) = VariableAddress[y] : +# 342| r342_2(int) = Load[y] : &:r342_1, m335_7 +# 342| r342_3(glval) = VariableAddress[ret] : +# 342| m342_4(int) = Store[ret] : &:r342_3, r342_2 +#-----| Goto -> Block 3 + +# 345| Block 3 +# 345| m345_1(int) = Phi : from 1:m340_4, from 2:m342_4 +# 345| r345_2(glval) = VariableAddress[#return] : +# 345| r345_3(glval) = VariableAddress[ret] : +# 345| r345_4(int) = Load[ret] : &:r345_3, m345_1 +# 345| m345_5(int) = Store[#return] : &:r345_2, r345_4 +# 335| r335_8(glval) = VariableAddress[#return] : +# 335| v335_9(void) = ReturnValue : &:r335_8, m345_5 +# 335| v335_10(void) = AliasedUse : ~m? +# 335| v335_11(void) = ExitFunction : + +# 348| int UnreachablePhiOperand2(int, int, int, bool) +# 348| Block 0 +# 348| v348_1(void) = EnterFunction : +# 348| mu348_2(unknown) = AliasedDefinition : +# 348| mu348_3(unknown) = InitializeNonLocal : +# 348| r348_4(glval) = VariableAddress[x] : +# 348| m348_5(int) = InitializeParameter[x] : &:r348_4 +# 348| r348_6(glval) = VariableAddress[y] : +# 348| m348_7(int) = InitializeParameter[y] : &:r348_6 +# 348| r348_8(glval) = VariableAddress[z] : +# 348| m348_9(int) = InitializeParameter[z] : &:r348_8 +# 348| r348_10(glval) = VariableAddress[b1] : +# 348| m348_11(bool) = InitializeParameter[b1] : &:r348_10 +# 349| r349_1(glval) = VariableAddress[b2] : +# 349| r349_2(bool) = Constant[1] : +# 349| m349_3(bool) = Store[b2] : &:r349_1, r349_2 +# 350| r350_1(glval) = VariableAddress[ret] : +# 350| m350_2(int) = Uninitialized[ret] : &:r350_1 +# 352| r352_1(glval) = VariableAddress[b1] : +# 352| r352_2(bool) = Load[b1] : &:r352_1, m348_11 +# 352| v352_3(void) = ConditionalBranch : r352_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 353| Block 1 +# 353| r353_1(glval) = VariableAddress[x] : +# 353| r353_2(int) = Load[x] : &:r353_1, m348_5 +# 353| r353_3(glval) = VariableAddress[ret] : +# 353| m353_4(int) = Store[ret] : &:r353_3, r353_2 +#-----| Goto -> Block 5 + +# 355| Block 2 +# 355| r355_1(glval) = VariableAddress[b2] : +# 355| r355_2(bool) = Load[b2] : &:r355_1, m349_3 +# 355| v355_3(void) = ConditionalBranch : r355_2 +#-----| False -> Block 4 +#-----| True -> Block 3 + +# 356| Block 3 +# 356| r356_1(glval) = VariableAddress[y] : +# 356| r356_2(int) = Load[y] : &:r356_1, m348_7 +# 356| r356_3(glval) = VariableAddress[ret] : +# 356| m356_4(int) = Store[ret] : &:r356_3, r356_2 +#-----| Goto -> Block 5 + +# 358| Block 4 +# 358| r358_1(glval) = VariableAddress[z] : +# 358| r358_2(int) = Load[z] : &:r358_1, m348_9 +# 358| r358_3(glval) = VariableAddress[ret] : +# 358| m358_4(int) = Store[ret] : &:r358_3, r358_2 +#-----| Goto -> Block 5 + +# 362| Block 5 +# 362| m362_1(int) = Phi : from 1:m353_4, from 3:m356_4, from 4:m358_4 +# 362| r362_2(glval) = VariableAddress[#return] : +# 362| r362_3(glval) = VariableAddress[ret] : +# 362| r362_4(int) = Load[ret] : &:r362_3, m362_1 +# 362| m362_5(int) = Store[#return] : &:r362_2, r362_4 +# 348| r348_12(glval) = VariableAddress[#return] : +# 348| v348_13(void) = ReturnValue : &:r348_12, m362_5 +# 348| v348_14(void) = AliasedUse : ~m? +# 348| v348_15(void) = ExitFunction : + +# 365| int DegeneratePhi(int, int, bool) +# 365| Block 0 +# 365| v365_1(void) = EnterFunction : +# 365| mu365_2(unknown) = AliasedDefinition : +# 365| mu365_3(unknown) = InitializeNonLocal : +# 365| r365_4(glval) = VariableAddress[x] : +# 365| m365_5(int) = InitializeParameter[x] : &:r365_4 +# 365| r365_6(glval) = VariableAddress[y] : +# 365| m365_7(int) = InitializeParameter[y] : &:r365_6 +# 365| r365_8(glval) = VariableAddress[b1] : +# 365| m365_9(bool) = InitializeParameter[b1] : &:r365_8 +# 366| r366_1(glval) = VariableAddress[b2] : +# 366| r366_2(bool) = Constant[1] : +# 366| m366_3(bool) = Store[b2] : &:r366_1, r366_2 +# 367| r367_1(glval) = VariableAddress[ret1] : +# 367| m367_2(int) = Uninitialized[ret1] : &:r367_1 +# 368| r368_1(glval) = VariableAddress[ret2] : +# 368| r368_2(glval) = VariableAddress[x] : +# 368| r368_3(int) = Load[x] : &:r368_2, m365_5 +# 368| m368_4(int) = Store[ret2] : &:r368_1, r368_3 +# 370| r370_1(glval) = VariableAddress[b1] : +# 370| r370_2(bool) = Load[b1] : &:r370_1, m365_9 +# 370| v370_3(void) = ConditionalBranch : r370_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 371| Block 1 +# 371| r371_1(glval) = VariableAddress[x] : +# 371| r371_2(int) = Load[x] : &:r371_1, m365_5 +# 371| r371_3(glval) = VariableAddress[ret1] : +# 371| m371_4(int) = Store[ret1] : &:r371_3, r371_2 +#-----| Goto -> Block 5 + +# 373| Block 2 +# 373| r373_1(glval) = VariableAddress[b2] : +# 373| r373_2(bool) = Load[b2] : &:r373_1, m366_3 +# 373| v373_3(void) = ConditionalBranch : r373_2 +#-----| False -> Block 4 +#-----| True -> Block 3 + +# 374| Block 3 +# 374| r374_1(glval) = VariableAddress[x] : +# 374| r374_2(int) = Load[x] : &:r374_1, m365_5 +# 374| r374_3(glval) = VariableAddress[ret1] : +# 374| m374_4(int) = Store[ret1] : &:r374_3, r374_2 +#-----| Goto -> Block 5 + +# 376| Block 4 +# 376| r376_1(glval) = VariableAddress[y] : +# 376| r376_2(int) = Load[y] : &:r376_1, m365_7 +# 376| r376_3(glval) = VariableAddress[ret2] : +# 376| m376_4(int) = Store[ret2] : &:r376_3, r376_2 +#-----| Goto -> Block 5 + +# 380| Block 5 +# 380| m380_1(int) = Phi : from 1:m368_4, from 3:m368_4, from 4:m376_4 +# 380| m380_2(int) = Phi : from 1:m371_4, from 3:m374_4, from 4:m367_2 +# 380| r380_3(glval) = VariableAddress[#return] : +# 380| r380_4(glval) = VariableAddress[ret1] : +# 380| r380_5(int) = Load[ret1] : &:r380_4, m380_2 +# 380| r380_6(glval) = VariableAddress[ret2] : +# 380| r380_7(int) = Load[ret2] : &:r380_6, m380_1 +# 380| r380_8(int) = Add : r380_5, r380_7 +# 380| m380_9(int) = Store[#return] : &:r380_3, r380_8 +# 365| r365_10(glval) = VariableAddress[#return] : +# 365| v365_11(void) = ReturnValue : &:r365_10, m380_9 +# 365| v365_12(void) = AliasedUse : ~m? +# 365| v365_13(void) = ExitFunction : + +# 383| int FusedBlockPhiOperand(int, int, int, bool) +# 383| Block 0 +# 383| v383_1(void) = EnterFunction : +# 383| mu383_2(unknown) = AliasedDefinition : +# 383| mu383_3(unknown) = InitializeNonLocal : +# 383| r383_4(glval) = VariableAddress[x] : +# 383| m383_5(int) = InitializeParameter[x] : &:r383_4 +# 383| r383_6(glval) = VariableAddress[y] : +# 383| m383_7(int) = InitializeParameter[y] : &:r383_6 +# 383| r383_8(glval) = VariableAddress[z] : +# 383| m383_9(int) = InitializeParameter[z] : &:r383_8 +# 383| r383_10(glval) = VariableAddress[b1] : +# 383| m383_11(bool) = InitializeParameter[b1] : &:r383_10 +# 384| r384_1(glval) = VariableAddress[b2] : +# 384| r384_2(bool) = Constant[1] : +# 384| m384_3(bool) = Store[b2] : &:r384_1, r384_2 +# 385| r385_1(glval) = VariableAddress[ret] : +# 385| m385_2(int) = Uninitialized[ret] : &:r385_1 +# 387| r387_1(glval) = VariableAddress[b1] : +# 387| r387_2(bool) = Load[b1] : &:r387_1, m383_11 +# 387| v387_3(void) = ConditionalBranch : r387_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 388| Block 1 +# 388| r388_1(glval) = VariableAddress[x] : +# 388| r388_2(int) = Load[x] : &:r388_1, m383_5 +# 388| r388_3(glval) = VariableAddress[ret] : +# 388| m388_4(int) = Store[ret] : &:r388_3, r388_2 +#-----| Goto -> Block 6 + +# 390| Block 2 +# 390| r390_1(glval) = VariableAddress[b2] : +# 390| r390_2(bool) = Load[b2] : &:r390_1, m384_3 +# 390| v390_3(void) = ConditionalBranch : r390_2 +#-----| False -> Block 4 +#-----| True -> Block 3 + +# 391| Block 3 +# 391| r391_1(glval) = VariableAddress[y] : +# 391| r391_2(int) = Load[y] : &:r391_1, m383_7 +# 391| r391_3(glval) = VariableAddress[ret] : +# 391| m391_4(int) = Store[ret] : &:r391_3, r391_2 +#-----| Goto -> Block 5 + +# 393| Block 4 +# 393| r393_1(glval) = VariableAddress[z] : +# 393| r393_2(int) = Load[z] : &:r393_1, m383_9 +# 393| r393_3(glval) = VariableAddress[ret] : +# 393| m393_4(int) = Store[ret] : &:r393_3, r393_2 +#-----| Goto -> Block 5 + +# 395| Block 5 +# 395| m395_1(int) = Phi : from 3:m391_4, from 4:m393_4 +# 395| v395_2(void) = NoOp : +#-----| Goto -> Block 6 + +# 398| Block 6 +# 398| m398_1(int) = Phi : from 1:m388_4, from 5:m395_1 +# 398| r398_2(glval) = VariableAddress[#return] : +# 398| r398_3(glval) = VariableAddress[ret] : +# 398| r398_4(int) = Load[ret] : &:r398_3, m398_1 +# 398| m398_5(int) = Store[#return] : &:r398_2, r398_4 +# 383| r383_12(glval) = VariableAddress[#return] : +# 383| v383_13(void) = ReturnValue : &:r383_12, m398_5 +# 383| v383_14(void) = AliasedUse : ~m? +# 383| v383_15(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected index 8b6cf17dbbb..9b1ebd7fe6e 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected @@ -983,13 +983,13 @@ ssa.cpp: # 241| r241_2(glval) = FunctionAddress[g] : # 241| v241_3(void) = Call[g] : func:r241_2, this:r241_1 # 241| mu241_4(unknown) = ^CallSideEffect : ~m? -# 241| v241_5(void) = ^BufferReadSideEffect[-1] : &:r241_1, ~m? +# 241| v241_5(void) = ^IndirectReadSideEffect[-1] : &:r241_1, ~m? # 241| mu241_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r241_1 # 242| r242_1(glval) = VariableAddress[c] : # 242| r242_2(glval) = FunctionAddress[g] : # 242| v242_3(void) = Call[g] : func:r242_2, this:r242_1 # 242| mu242_4(unknown) = ^CallSideEffect : ~m? -# 242| v242_5(void) = ^BufferReadSideEffect[-1] : &:r242_1, ~m? +# 242| v242_5(void) = ^IndirectReadSideEffect[-1] : &:r242_1, ~m? # 242| mu242_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r242_1 # 243| r243_1(glval) = VariableAddress[c2] : # 243| mu243_2(Constructible) = Uninitialized[c2] : &:r243_1 @@ -1002,7 +1002,7 @@ ssa.cpp: # 244| r244_2(glval) = FunctionAddress[g] : # 244| v244_3(void) = Call[g] : func:r244_2, this:r244_1 # 244| mu244_4(unknown) = ^CallSideEffect : ~m? -# 244| v244_5(void) = ^BufferReadSideEffect[-1] : &:r244_1, ~m? +# 244| v244_5(void) = ^IndirectReadSideEffect[-1] : &:r244_1, ~m? # 244| mu244_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r244_1 # 245| v245_1(void) = NoOp : # 239| v239_4(void) = ReturnVoid : @@ -1376,3 +1376,322 @@ ssa.cpp: # 310| v310_11(void) = ReturnVoid : # 310| v310_12(void) = AliasedUse : ~m? # 310| v310_13(void) = ExitFunction : + +# 319| void DoubleIndirectionEscapes(char*) +# 319| Block 0 +# 319| v319_1(void) = EnterFunction : +# 319| mu319_2(unknown) = AliasedDefinition : +# 319| mu319_3(unknown) = InitializeNonLocal : +# 319| r319_4(glval) = VariableAddress[s] : +# 319| m319_5(char *) = InitializeParameter[s] : &:r319_4 +# 319| r319_6(char *) = Load[s] : &:r319_4, m319_5 +# 319| mu319_7(unknown) = InitializeIndirection[s] : &:r319_6 +# 321| r321_1(glval) = VariableAddress[buffer] : +# 321| mu321_2(char[1024]) = Uninitialized[buffer] : &:r321_1 +# 322| r322_1(glval) = VariableAddress[ptr1] : +# 322| mu322_2(char *) = Uninitialized[ptr1] : &:r322_1 +# 322| r322_3(glval) = VariableAddress[ptr2] : +# 322| m322_4(char **) = Uninitialized[ptr2] : &:r322_3 +# 323| r323_1(glval) = VariableAddress[ptr3] : +# 323| m323_2(char *) = Uninitialized[ptr3] : &:r323_1 +# 323| r323_3(glval) = VariableAddress[ptr4] : +# 323| m323_4(char **) = Uninitialized[ptr4] : &:r323_3 +# 325| r325_1(glval) = VariableAddress[buffer] : +# 325| r325_2(char *) = Convert : r325_1 +# 325| r325_3(glval) = VariableAddress[ptr1] : +# 325| mu325_4(char *) = Store[ptr1] : &:r325_3, r325_2 +# 326| r326_1(glval) = VariableAddress[ptr1] : +# 326| r326_2(char **) = CopyValue : r326_1 +# 326| r326_3(glval) = VariableAddress[ptr2] : +# 326| m326_4(char **) = Store[ptr2] : &:r326_3, r326_2 +# 327| r327_1(glval) = FunctionAddress[memcpy] : +# 327| r327_2(glval) = VariableAddress[ptr2] : +# 327| r327_3(char **) = Load[ptr2] : &:r327_2, m326_4 +# 327| r327_4(char *) = Load[?] : &:r327_3, ~m? +# 327| r327_5(void *) = Convert : r327_4 +# 327| r327_6(glval) = VariableAddress[s] : +# 327| r327_7(char *) = Load[s] : &:r327_6, m319_5 +# 327| r327_8(void *) = Convert : r327_7 +# 327| r327_9(int) = Constant[1024] : +# 327| r327_10(void *) = Call[memcpy] : func:r327_1, 0:r327_5, 1:r327_8, 2:r327_9 +# 327| v327_11(void) = ^SizedBufferReadSideEffect[1] : &:r327_8, r327_9, ~m? +# 327| mu327_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r327_5, r327_9 +# 329| r329_1(glval) = FunctionAddress[sink] : +# 329| r329_2(glval) = VariableAddress[buffer] : +# 329| r329_3(char *) = Convert : r329_2 +# 329| v329_4(void) = Call[sink] : func:r329_1, 0:r329_3 +# 329| mu329_5(unknown) = ^CallSideEffect : ~m? +# 329| v329_6(void) = ^BufferReadSideEffect[0] : &:r329_3, ~m? +# 329| mu329_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r329_3 +# 330| r330_1(glval) = FunctionAddress[sink] : +# 330| r330_2(glval) = VariableAddress[ptr1] : +# 330| r330_3(char *) = Load[ptr1] : &:r330_2, ~m? +# 330| v330_4(void) = Call[sink] : func:r330_1, 0:r330_3 +# 330| mu330_5(unknown) = ^CallSideEffect : ~m? +# 330| v330_6(void) = ^BufferReadSideEffect[0] : &:r330_3, ~m? +# 330| mu330_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r330_3 +# 331| r331_1(glval) = FunctionAddress[sink] : +# 331| r331_2(glval) = VariableAddress[ptr2] : +# 331| r331_3(char **) = Load[ptr2] : &:r331_2, m326_4 +# 331| v331_4(void) = Call[sink] : func:r331_1, 0:r331_3 +# 331| mu331_5(unknown) = ^CallSideEffect : ~m? +# 331| v331_6(void) = ^BufferReadSideEffect[0] : &:r331_3, ~m? +# 331| mu331_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r331_3 +# 332| r332_1(glval) = FunctionAddress[sink] : +# 332| r332_2(glval) = VariableAddress[ptr2] : +# 332| r332_3(char **) = Load[ptr2] : &:r332_2, m326_4 +# 332| r332_4(char *) = Load[?] : &:r332_3, ~m? +# 332| v332_5(void) = Call[sink] : func:r332_1, 0:r332_4 +# 332| mu332_6(unknown) = ^CallSideEffect : ~m? +# 332| v332_7(void) = ^BufferReadSideEffect[0] : &:r332_4, ~m? +# 332| mu332_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r332_4 +# 333| v333_1(void) = NoOp : +# 319| v319_8(void) = ReturnIndirection[s] : &:r319_6, ~m? +# 319| v319_9(void) = ReturnVoid : +# 319| v319_10(void) = AliasedUse : ~m? +# 319| v319_11(void) = ExitFunction : + +# 335| int UnreachablePhiOperand(int, int) +# 335| Block 0 +# 335| v335_1(void) = EnterFunction : +# 335| mu335_2(unknown) = AliasedDefinition : +# 335| mu335_3(unknown) = InitializeNonLocal : +# 335| r335_4(glval) = VariableAddress[x] : +# 335| m335_5(int) = InitializeParameter[x] : &:r335_4 +# 335| r335_6(glval) = VariableAddress[y] : +# 335| m335_7(int) = InitializeParameter[y] : &:r335_6 +# 336| r336_1(glval) = VariableAddress[b] : +# 336| r336_2(bool) = Constant[1] : +# 336| m336_3(bool) = Store[b] : &:r336_1, r336_2 +# 337| r337_1(glval) = VariableAddress[ret] : +# 337| m337_2(int) = Uninitialized[ret] : &:r337_1 +# 339| r339_1(glval) = VariableAddress[b] : +# 339| r339_2(bool) = Load[b] : &:r339_1, m336_3 +# 339| v339_3(void) = ConditionalBranch : r339_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 340| Block 1 +# 340| r340_1(glval) = VariableAddress[x] : +# 340| r340_2(int) = Load[x] : &:r340_1, m335_5 +# 340| r340_3(glval) = VariableAddress[ret] : +# 340| m340_4(int) = Store[ret] : &:r340_3, r340_2 +#-----| Goto -> Block 3 + +# 342| Block 2 +# 342| r342_1(glval) = VariableAddress[y] : +# 342| r342_2(int) = Load[y] : &:r342_1, m335_7 +# 342| r342_3(glval) = VariableAddress[ret] : +# 342| m342_4(int) = Store[ret] : &:r342_3, r342_2 +#-----| Goto -> Block 3 + +# 345| Block 3 +# 345| m345_1(int) = Phi : from 1:m340_4, from 2:m342_4 +# 345| r345_2(glval) = VariableAddress[#return] : +# 345| r345_3(glval) = VariableAddress[ret] : +# 345| r345_4(int) = Load[ret] : &:r345_3, m345_1 +# 345| m345_5(int) = Store[#return] : &:r345_2, r345_4 +# 335| r335_8(glval) = VariableAddress[#return] : +# 335| v335_9(void) = ReturnValue : &:r335_8, m345_5 +# 335| v335_10(void) = AliasedUse : ~m? +# 335| v335_11(void) = ExitFunction : + +# 348| int UnreachablePhiOperand2(int, int, int, bool) +# 348| Block 0 +# 348| v348_1(void) = EnterFunction : +# 348| mu348_2(unknown) = AliasedDefinition : +# 348| mu348_3(unknown) = InitializeNonLocal : +# 348| r348_4(glval) = VariableAddress[x] : +# 348| m348_5(int) = InitializeParameter[x] : &:r348_4 +# 348| r348_6(glval) = VariableAddress[y] : +# 348| m348_7(int) = InitializeParameter[y] : &:r348_6 +# 348| r348_8(glval) = VariableAddress[z] : +# 348| m348_9(int) = InitializeParameter[z] : &:r348_8 +# 348| r348_10(glval) = VariableAddress[b1] : +# 348| m348_11(bool) = InitializeParameter[b1] : &:r348_10 +# 349| r349_1(glval) = VariableAddress[b2] : +# 349| r349_2(bool) = Constant[1] : +# 349| m349_3(bool) = Store[b2] : &:r349_1, r349_2 +# 350| r350_1(glval) = VariableAddress[ret] : +# 350| m350_2(int) = Uninitialized[ret] : &:r350_1 +# 352| r352_1(glval) = VariableAddress[b1] : +# 352| r352_2(bool) = Load[b1] : &:r352_1, m348_11 +# 352| v352_3(void) = ConditionalBranch : r352_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 353| Block 1 +# 353| r353_1(glval) = VariableAddress[x] : +# 353| r353_2(int) = Load[x] : &:r353_1, m348_5 +# 353| r353_3(glval) = VariableAddress[ret] : +# 353| m353_4(int) = Store[ret] : &:r353_3, r353_2 +#-----| Goto -> Block 5 + +# 355| Block 2 +# 355| r355_1(glval) = VariableAddress[b2] : +# 355| r355_2(bool) = Load[b2] : &:r355_1, m349_3 +# 355| v355_3(void) = ConditionalBranch : r355_2 +#-----| False -> Block 4 +#-----| True -> Block 3 + +# 356| Block 3 +# 356| r356_1(glval) = VariableAddress[y] : +# 356| r356_2(int) = Load[y] : &:r356_1, m348_7 +# 356| r356_3(glval) = VariableAddress[ret] : +# 356| m356_4(int) = Store[ret] : &:r356_3, r356_2 +#-----| Goto -> Block 5 + +# 358| Block 4 +# 358| r358_1(glval) = VariableAddress[z] : +# 358| r358_2(int) = Load[z] : &:r358_1, m348_9 +# 358| r358_3(glval) = VariableAddress[ret] : +# 358| m358_4(int) = Store[ret] : &:r358_3, r358_2 +#-----| Goto -> Block 5 + +# 362| Block 5 +# 362| m362_1(int) = Phi : from 1:m353_4, from 3:m356_4, from 4:m358_4 +# 362| r362_2(glval) = VariableAddress[#return] : +# 362| r362_3(glval) = VariableAddress[ret] : +# 362| r362_4(int) = Load[ret] : &:r362_3, m362_1 +# 362| m362_5(int) = Store[#return] : &:r362_2, r362_4 +# 348| r348_12(glval) = VariableAddress[#return] : +# 348| v348_13(void) = ReturnValue : &:r348_12, m362_5 +# 348| v348_14(void) = AliasedUse : ~m? +# 348| v348_15(void) = ExitFunction : + +# 365| int DegeneratePhi(int, int, bool) +# 365| Block 0 +# 365| v365_1(void) = EnterFunction : +# 365| mu365_2(unknown) = AliasedDefinition : +# 365| mu365_3(unknown) = InitializeNonLocal : +# 365| r365_4(glval) = VariableAddress[x] : +# 365| m365_5(int) = InitializeParameter[x] : &:r365_4 +# 365| r365_6(glval) = VariableAddress[y] : +# 365| m365_7(int) = InitializeParameter[y] : &:r365_6 +# 365| r365_8(glval) = VariableAddress[b1] : +# 365| m365_9(bool) = InitializeParameter[b1] : &:r365_8 +# 366| r366_1(glval) = VariableAddress[b2] : +# 366| r366_2(bool) = Constant[1] : +# 366| m366_3(bool) = Store[b2] : &:r366_1, r366_2 +# 367| r367_1(glval) = VariableAddress[ret1] : +# 367| m367_2(int) = Uninitialized[ret1] : &:r367_1 +# 368| r368_1(glval) = VariableAddress[ret2] : +# 368| r368_2(glval) = VariableAddress[x] : +# 368| r368_3(int) = Load[x] : &:r368_2, m365_5 +# 368| m368_4(int) = Store[ret2] : &:r368_1, r368_3 +# 370| r370_1(glval) = VariableAddress[b1] : +# 370| r370_2(bool) = Load[b1] : &:r370_1, m365_9 +# 370| v370_3(void) = ConditionalBranch : r370_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 371| Block 1 +# 371| r371_1(glval) = VariableAddress[x] : +# 371| r371_2(int) = Load[x] : &:r371_1, m365_5 +# 371| r371_3(glval) = VariableAddress[ret1] : +# 371| m371_4(int) = Store[ret1] : &:r371_3, r371_2 +#-----| Goto -> Block 5 + +# 373| Block 2 +# 373| r373_1(glval) = VariableAddress[b2] : +# 373| r373_2(bool) = Load[b2] : &:r373_1, m366_3 +# 373| v373_3(void) = ConditionalBranch : r373_2 +#-----| False -> Block 4 +#-----| True -> Block 3 + +# 374| Block 3 +# 374| r374_1(glval) = VariableAddress[x] : +# 374| r374_2(int) = Load[x] : &:r374_1, m365_5 +# 374| r374_3(glval) = VariableAddress[ret1] : +# 374| m374_4(int) = Store[ret1] : &:r374_3, r374_2 +#-----| Goto -> Block 5 + +# 376| Block 4 +# 376| r376_1(glval) = VariableAddress[y] : +# 376| r376_2(int) = Load[y] : &:r376_1, m365_7 +# 376| r376_3(glval) = VariableAddress[ret2] : +# 376| m376_4(int) = Store[ret2] : &:r376_3, r376_2 +#-----| Goto -> Block 5 + +# 380| Block 5 +# 380| m380_1(int) = Phi : from 1:m368_4, from 3:m368_4, from 4:m376_4 +# 380| m380_2(int) = Phi : from 1:m371_4, from 3:m374_4, from 4:m367_2 +# 380| r380_3(glval) = VariableAddress[#return] : +# 380| r380_4(glval) = VariableAddress[ret1] : +# 380| r380_5(int) = Load[ret1] : &:r380_4, m380_2 +# 380| r380_6(glval) = VariableAddress[ret2] : +# 380| r380_7(int) = Load[ret2] : &:r380_6, m380_1 +# 380| r380_8(int) = Add : r380_5, r380_7 +# 380| m380_9(int) = Store[#return] : &:r380_3, r380_8 +# 365| r365_10(glval) = VariableAddress[#return] : +# 365| v365_11(void) = ReturnValue : &:r365_10, m380_9 +# 365| v365_12(void) = AliasedUse : ~m? +# 365| v365_13(void) = ExitFunction : + +# 383| int FusedBlockPhiOperand(int, int, int, bool) +# 383| Block 0 +# 383| v383_1(void) = EnterFunction : +# 383| mu383_2(unknown) = AliasedDefinition : +# 383| mu383_3(unknown) = InitializeNonLocal : +# 383| r383_4(glval) = VariableAddress[x] : +# 383| m383_5(int) = InitializeParameter[x] : &:r383_4 +# 383| r383_6(glval) = VariableAddress[y] : +# 383| m383_7(int) = InitializeParameter[y] : &:r383_6 +# 383| r383_8(glval) = VariableAddress[z] : +# 383| m383_9(int) = InitializeParameter[z] : &:r383_8 +# 383| r383_10(glval) = VariableAddress[b1] : +# 383| m383_11(bool) = InitializeParameter[b1] : &:r383_10 +# 384| r384_1(glval) = VariableAddress[b2] : +# 384| r384_2(bool) = Constant[1] : +# 384| m384_3(bool) = Store[b2] : &:r384_1, r384_2 +# 385| r385_1(glval) = VariableAddress[ret] : +# 385| m385_2(int) = Uninitialized[ret] : &:r385_1 +# 387| r387_1(glval) = VariableAddress[b1] : +# 387| r387_2(bool) = Load[b1] : &:r387_1, m383_11 +# 387| v387_3(void) = ConditionalBranch : r387_2 +#-----| False -> Block 2 +#-----| True -> Block 1 + +# 388| Block 1 +# 388| r388_1(glval) = VariableAddress[x] : +# 388| r388_2(int) = Load[x] : &:r388_1, m383_5 +# 388| r388_3(glval) = VariableAddress[ret] : +# 388| m388_4(int) = Store[ret] : &:r388_3, r388_2 +#-----| Goto -> Block 6 + +# 390| Block 2 +# 390| r390_1(glval) = VariableAddress[b2] : +# 390| r390_2(bool) = Load[b2] : &:r390_1, m384_3 +# 390| v390_3(void) = ConditionalBranch : r390_2 +#-----| False -> Block 4 +#-----| True -> Block 3 + +# 391| Block 3 +# 391| r391_1(glval) = VariableAddress[y] : +# 391| r391_2(int) = Load[y] : &:r391_1, m383_7 +# 391| r391_3(glval) = VariableAddress[ret] : +# 391| m391_4(int) = Store[ret] : &:r391_3, r391_2 +#-----| Goto -> Block 5 + +# 393| Block 4 +# 393| r393_1(glval) = VariableAddress[z] : +# 393| r393_2(int) = Load[z] : &:r393_1, m383_9 +# 393| r393_3(glval) = VariableAddress[ret] : +# 393| m393_4(int) = Store[ret] : &:r393_3, r393_2 +#-----| Goto -> Block 5 + +# 395| Block 5 +# 395| m395_1(int) = Phi : from 3:m391_4, from 4:m393_4 +# 395| v395_2(void) = NoOp : +#-----| Goto -> Block 6 + +# 398| Block 6 +# 398| m398_1(int) = Phi : from 1:m388_4, from 5:m395_1 +# 398| r398_2(glval) = VariableAddress[#return] : +# 398| r398_3(glval) = VariableAddress[ret] : +# 398| r398_4(int) = Load[ret] : &:r398_3, m398_1 +# 398| m398_5(int) = Store[#return] : &:r398_2, r398_4 +# 383| r383_12(glval) = VariableAddress[#return] : +# 383| v383_13(void) = ReturnValue : &:r383_12, m398_5 +# 383| v383_14(void) = AliasedUse : ~m? +# 383| v383_15(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/locations/charloc/charloc.cpp b/cpp/ql/test/library-tests/locations/charloc/charloc.cpp new file mode 100644 index 00000000000..4f6595e465c --- /dev/null +++ b/cpp/ql/test/library-tests/locations/charloc/charloc.cpp @@ -0,0 +1,8 @@ + +void test() +{ + 1; + 2 + 3; + ( +4); +} diff --git a/cpp/ql/test/library-tests/locations/charloc/charloc.expected b/cpp/ql/test/library-tests/locations/charloc/charloc.expected new file mode 100644 index 00000000000..4ccec12906e --- /dev/null +++ b/cpp/ql/test/library-tests/locations/charloc/charloc.expected @@ -0,0 +1,6 @@ +| charloc.cpp | 1 | 47 | 47 | 1 | +| charloc.cpp | 2 | 58 | 58 | 2 | +| charloc.cpp | 3 | 62 | 62 | 3 | +| charloc.cpp | 4 | 78 | 78 | 4 | +| charloc.cpp | (...) | 77 | 79 | (...), 4 | +| charloc.cpp | ... + ... | 58 | 62 | ... + ..., 2, 3 | diff --git a/cpp/ql/test/library-tests/locations/charloc/charloc.ql b/cpp/ql/test/library-tests/locations/charloc/charloc.ql new file mode 100644 index 00000000000..9ac69c6055e --- /dev/null +++ b/cpp/ql/test/library-tests/locations/charloc/charloc.ql @@ -0,0 +1,8 @@ +import cpp + +from File f, Expr e, Location l, int start, int end +where + e.getLocation() = l and + l.charLoc(f, start, end) +select f.getBaseName(), e.toString(), start, end, + concat(Expr e2, Location l2 | e2.getLocation() = l2 and l.subsumes(l2) | e2.toString(), ", ") diff --git a/cpp/ql/test/library-tests/templates/instantiations_functions/elements.expected b/cpp/ql/test/library-tests/templates/instantiations_functions/elements.expected index 2a7165a8419..44d8ff0cb83 100644 --- a/cpp/ql/test/library-tests/templates/instantiations_functions/elements.expected +++ b/cpp/ql/test/library-tests/templates/instantiations_functions/elements.expected @@ -1,4 +1,6 @@ | file://:0:0:0:0 | | +| file://:0:0:0:0 | & | +| file://:0:0:0:0 | && | | file://:0:0:0:0 | (composite *)... | | file://:0:0:0:0 | (composite *)... | | file://:0:0:0:0 | (global namespace) | diff --git a/cpp/ql/test/library-tests/types/cstd_types/cstd_types.cpp b/cpp/ql/test/library-tests/types/cstd_types/cstd_types.cpp new file mode 100644 index 00000000000..1f5d262978e --- /dev/null +++ b/cpp/ql/test/library-tests/types/cstd_types/cstd_types.cpp @@ -0,0 +1,77 @@ +typedef signed char int8_t; +typedef short int16_t; +typedef int int32_t; +typedef long long int64_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +typedef signed char int_least8_t; +typedef short int_least16_t; +typedef int int_least32_t; +typedef long long int_least64_t; +typedef unsigned char uint_least8_t; +typedef unsigned short uint_least16_t; +typedef unsigned int uint_least32_t; +typedef unsigned long long uint_least64_t; + +typedef signed char int_fast8_t; +typedef int int_fast16_t; +typedef int int_fast32_t; +typedef long long int_fast64_t; +typedef unsigned char uint_fast8_t; +typedef unsigned int uint_fast16_t; +typedef unsigned int uint_fast32_t; +typedef unsigned long long uint_fast64_t; + +typedef long long intmax_t; +typedef unsigned long long uintmax_t; + +int8_t i8; +int16_t i16; +int32_t i32; +int64_t i64; +uint8_t ui8; +uint16_t ui16; +uint32_t ui32; +uint64_t ui64; +int_least8_t l8; +int_least16_t l16; +int_least32_t l32; +int_least64_t l64; +uint_least8_t ul8; +uint_least16_t ul16; +uint_least32_t ul32; +uint_least64_t ul64; +int_fast8_t if8; +int_fast16_t if16; +int_fast32_t if32; +int_fast64_t if64; +uint_fast8_t uf8; +uint_fast16_t uf16; +uint_fast32_t uf32; +uint_fast64_t uf64; +intmax_t im; +uintmax_t uim; + +enum E0 : int8_t { + e0 +}; + +enum class E1 : int8_t { + e1 +}; + +enum E2 { + e2 +}; + +enum class E3 { + e3 +}; + +E0 _e0; +E1 _e1; +E2 _e2; +E3 _e3; \ No newline at end of file diff --git a/cpp/ql/test/library-tests/types/cstd_types/cstd_types_fastestminimumwidth.expected b/cpp/ql/test/library-tests/types/cstd_types/cstd_types_fastestminimumwidth.expected new file mode 100644 index 00000000000..13fe25a4819 --- /dev/null +++ b/cpp/ql/test/library-tests/types/cstd_types/cstd_types_fastestminimumwidth.expected @@ -0,0 +1,8 @@ +| cstd_types.cpp:47:13:47:15 | if8 | CTypedefType, FastestMinimumWidthIntegralType, Int_fast8_t | +| cstd_types.cpp:48:14:48:17 | if16 | CTypedefType, FastestMinimumWidthIntegralType, Int_fast16_t | +| cstd_types.cpp:49:14:49:17 | if32 | CTypedefType, FastestMinimumWidthIntegralType, Int_fast32_t | +| cstd_types.cpp:50:14:50:17 | if64 | CTypedefType, FastestMinimumWidthIntegralType, Int_fast64_t | +| cstd_types.cpp:51:14:51:16 | uf8 | CTypedefType, FastestMinimumWidthIntegralType, UInt_fast8_t | +| cstd_types.cpp:52:15:52:18 | uf16 | CTypedefType, FastestMinimumWidthIntegralType, UInt_fast16_t | +| cstd_types.cpp:53:15:53:18 | uf32 | CTypedefType, FastestMinimumWidthIntegralType, UInt_fast32_t | +| cstd_types.cpp:54:15:54:18 | uf64 | CTypedefType, FastestMinimumWidthIntegralType, UInt_fast64_t | \ No newline at end of file diff --git a/cpp/ql/test/library-tests/types/cstd_types/cstd_types_fastestminimumwidth.ql b/cpp/ql/test/library-tests/types/cstd_types/cstd_types_fastestminimumwidth.ql new file mode 100644 index 00000000000..6600e69572e --- /dev/null +++ b/cpp/ql/test/library-tests/types/cstd_types/cstd_types_fastestminimumwidth.ql @@ -0,0 +1,5 @@ +import cpp + +from Variable v, FastestMinimumWidthIntegralType t +where v.getType() = t +select v, concat(t.getAQlClass(), ", ") diff --git a/cpp/ql/test/library-tests/types/cstd_types/cstd_types_fixedwidth.expected b/cpp/ql/test/library-tests/types/cstd_types/cstd_types_fixedwidth.expected new file mode 100644 index 00000000000..d1c64343636 --- /dev/null +++ b/cpp/ql/test/library-tests/types/cstd_types/cstd_types_fixedwidth.expected @@ -0,0 +1,8 @@ +| cstd_types.cpp:31:8:31:9 | i8 | CTypedefType, FixedWidthIntegralType, Int8_t | +| cstd_types.cpp:32:9:32:11 | i16 | CTypedefType, FixedWidthIntegralType, Int16_t | +| cstd_types.cpp:33:9:33:11 | i32 | CTypedefType, FixedWidthIntegralType, Int32_t | +| cstd_types.cpp:34:9:34:11 | i64 | CTypedefType, FixedWidthIntegralType, Int64_t | +| cstd_types.cpp:35:9:35:11 | ui8 | CTypedefType, FixedWidthIntegralType, UInt8_t | +| cstd_types.cpp:36:10:36:13 | ui16 | CTypedefType, FixedWidthIntegralType, UInt16_t | +| cstd_types.cpp:37:10:37:13 | ui32 | CTypedefType, FixedWidthIntegralType, UInt32_t | +| cstd_types.cpp:38:10:38:13 | ui64 | CTypedefType, FixedWidthIntegralType, UInt64_t | diff --git a/cpp/ql/test/library-tests/types/cstd_types/cstd_types_fixedwidth.ql b/cpp/ql/test/library-tests/types/cstd_types/cstd_types_fixedwidth.ql new file mode 100644 index 00000000000..18e9cf4469e --- /dev/null +++ b/cpp/ql/test/library-tests/types/cstd_types/cstd_types_fixedwidth.ql @@ -0,0 +1,5 @@ +import cpp + +from Variable v, FixedWidthIntegralType t +where v.getType() = t +select v, concat(t.getAQlClass(), ", ") diff --git a/cpp/ql/test/library-tests/types/cstd_types/cstd_types_fixedwidthenum.expected b/cpp/ql/test/library-tests/types/cstd_types/cstd_types_fixedwidthenum.expected new file mode 100644 index 00000000000..d9884b22b00 --- /dev/null +++ b/cpp/ql/test/library-tests/types/cstd_types/cstd_types_fixedwidthenum.expected @@ -0,0 +1,2 @@ +| cstd_types.cpp:74:4:74:6 | _e0 | Enum, FixedWidthEnumType | +| cstd_types.cpp:75:4:75:6 | _e1 | FixedWidthEnumType, ScopedEnum | diff --git a/cpp/ql/test/library-tests/types/cstd_types/cstd_types_fixedwidthenum.ql b/cpp/ql/test/library-tests/types/cstd_types/cstd_types_fixedwidthenum.ql new file mode 100644 index 00000000000..7e9cd7c2b54 --- /dev/null +++ b/cpp/ql/test/library-tests/types/cstd_types/cstd_types_fixedwidthenum.ql @@ -0,0 +1,5 @@ +import cpp + +from Variable v, FixedWidthEnumType t +where v.getType() = t +select v, concat(t.getAQlClass(), ", ") diff --git a/cpp/ql/test/library-tests/types/cstd_types/cstd_types_maximumwidth.expected b/cpp/ql/test/library-tests/types/cstd_types/cstd_types_maximumwidth.expected new file mode 100644 index 00000000000..0bf7779fcaf --- /dev/null +++ b/cpp/ql/test/library-tests/types/cstd_types/cstd_types_maximumwidth.expected @@ -0,0 +1,2 @@ +| cstd_types.cpp:55:10:55:11 | im | CTypedefType, Intmax_t, MaximumWidthIntegralType | +| cstd_types.cpp:56:11:56:13 | uim | CTypedefType, MaximumWidthIntegralType, Uintmax_t | diff --git a/cpp/ql/test/library-tests/types/cstd_types/cstd_types_maximumwidth.ql b/cpp/ql/test/library-tests/types/cstd_types/cstd_types_maximumwidth.ql new file mode 100644 index 00000000000..bfd5f88e54c --- /dev/null +++ b/cpp/ql/test/library-tests/types/cstd_types/cstd_types_maximumwidth.ql @@ -0,0 +1,5 @@ +import cpp + +from Variable v, MaximumWidthIntegralType t +where v.getType() = t +select v, concat(t.getAQlClass(), ", ") diff --git a/cpp/ql/test/library-tests/types/cstd_types/cstd_types_minimumwidth.expected b/cpp/ql/test/library-tests/types/cstd_types/cstd_types_minimumwidth.expected new file mode 100644 index 00000000000..2984f07be8c --- /dev/null +++ b/cpp/ql/test/library-tests/types/cstd_types/cstd_types_minimumwidth.expected @@ -0,0 +1,8 @@ +| cstd_types.cpp:39:15:39:16 | l8 | CTypedefType, Int_least8_t, MinimumWidthIntegralType | +| cstd_types.cpp:40:15:40:17 | l16 | CTypedefType, Int_least16_t, MinimumWidthIntegralType | +| cstd_types.cpp:41:15:41:17 | l32 | CTypedefType, Int_least32_t, MinimumWidthIntegralType | +| cstd_types.cpp:42:15:42:17 | l64 | CTypedefType, Int_least64_t, MinimumWidthIntegralType | +| cstd_types.cpp:43:15:43:17 | ul8 | CTypedefType, MinimumWidthIntegralType, UInt_least8_t | +| cstd_types.cpp:44:16:44:19 | ul16 | CTypedefType, MinimumWidthIntegralType, UInt_least16_t | +| cstd_types.cpp:45:16:45:19 | ul32 | CTypedefType, MinimumWidthIntegralType, UInt_least32_t | +| cstd_types.cpp:46:16:46:19 | ul64 | CTypedefType, MinimumWidthIntegralType, UInt_least64_t | diff --git a/cpp/ql/test/library-tests/types/cstd_types/cstd_types_minimumwidth.ql b/cpp/ql/test/library-tests/types/cstd_types/cstd_types_minimumwidth.ql new file mode 100644 index 00000000000..30f393b66cb --- /dev/null +++ b/cpp/ql/test/library-tests/types/cstd_types/cstd_types_minimumwidth.ql @@ -0,0 +1,5 @@ +import cpp + +from Variable v, MinimumWidthIntegralType t +where v.getType() = t +select v, concat(t.getAQlClass(), ", ") diff --git a/cpp/ql/test/library-tests/udl/udl.expected b/cpp/ql/test/library-tests/udl/udl.expected index fce69664a06..52c57beffc5 100644 --- a/cpp/ql/test/library-tests/udl/udl.expected +++ b/cpp/ql/test/library-tests/udl/udl.expected @@ -1,4 +1,4 @@ | udl.cpp:5:25:5:28 | Xy | udl.cpp:5:25:5:28 | initializer for xy | | udl.cpp:6:27:6:34 | XyXy | udl.cpp:6:27:6:34 | initializer for xyxy | -| udl.cpp:7:26:7:30 | X | udl.cpp:7:26:7:26 | call to operator "_Y | -| udl.cpp:8:29:8:38 | XX | udl.cpp:8:29:8:29 | call to operator "_Y | +| udl.cpp:7:26:7:30 | X | udl.cpp:7:26:7:26 | call to operator ""_Y | +| udl.cpp:8:29:8:38 | XX | udl.cpp:8:29:8:29 | call to operator ""_Y | diff --git a/cpp/ql/test/library-tests/unnamed/elements.expected b/cpp/ql/test/library-tests/unnamed/elements.expected index 37695f5e837..2b32306fbac 100644 --- a/cpp/ql/test/library-tests/unnamed/elements.expected +++ b/cpp/ql/test/library-tests/unnamed/elements.expected @@ -1,4 +1,6 @@ | file://:0:0:0:0 | | Other | +| file://:0:0:0:0 | & | Other | +| file://:0:0:0:0 | && | Other | | file://:0:0:0:0 | (global namespace) | Other | | file://:0:0:0:0 | (unnamed global/namespace variable) | Other | | file://:0:0:0:0 | _Complex __float128 | Other | diff --git a/cpp/ql/test/library-tests/variables/getanassignedvalue/getanassignedvalue.expected b/cpp/ql/test/library-tests/variables/getanassignedvalue/getanassignedvalue.expected index bfde183c480..89977601d47 100644 --- a/cpp/ql/test/library-tests/variables/getanassignedvalue/getanassignedvalue.expected +++ b/cpp/ql/test/library-tests/variables/getanassignedvalue/getanassignedvalue.expected @@ -1,4 +1,3 @@ -| file://:0:0:0:0 | abc | test.cpp:53:6:53:11 | chars1 | | file://:0:0:0:0 | {...} | test.cpp:38:22:38:22 | v | | test.cpp:4:9:4:11 | 10 | test.cpp:4:6:4:6 | v | | test.cpp:5:15:5:16 | & ... | test.cpp:5:7:5:11 | ptr_v | @@ -23,5 +22,6 @@ | test.cpp:48:18:48:18 | 4 | test.cpp:31:6:31:8 | num | | test.cpp:48:21:48:26 | Four | test.cpp:32:14:32:16 | str | | test.cpp:52:19:52:27 | {...} | test.cpp:52:5:52:11 | myArray | +| test.cpp:53:17:53:21 | abc | test.cpp:53:6:53:11 | chars1 | | test.cpp:54:17:54:31 | {...} | test.cpp:54:6:54:11 | chars2 | | test.cpp:55:16:55:20 | abc | test.cpp:55:7:55:12 | chars3 | diff --git a/cpp/ql/test/library-tests/variables/getanassignedvalue/test.cpp b/cpp/ql/test/library-tests/variables/getanassignedvalue/test.cpp index 60740acc424..490535775c6 100644 --- a/cpp/ql/test/library-tests/variables/getanassignedvalue/test.cpp +++ b/cpp/ql/test/library-tests/variables/getanassignedvalue/test.cpp @@ -50,6 +50,6 @@ myStruct2 v3 = {{4, "Four"}}; // assigments to `v3`, `ms2`, `num`, `str` // --- int myArray[10] = {1, 2, 3}; // assigment to `myArray` -char chars1[] = "abc"; // assignment to `chars1` (literal "abc" has no location) +char chars1[] = "abc"; // assignment to `chars1` char chars2[] = {'a', 'b', 'c'}; // assigment to `chars2` char *chars3 = "abc"; // assigment to `chars3` diff --git a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/AssignWhereCompareMeant/AssignWhereCompareMeant.expected b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/AssignWhereCompareMeant/AssignWhereCompareMeant.expected index 6c4dfaea0a7..a5a1a6ca0bd 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/AssignWhereCompareMeant/AssignWhereCompareMeant.expected +++ b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/AssignWhereCompareMeant/AssignWhereCompareMeant.expected @@ -19,3 +19,7 @@ | test.cpp:144:32:144:36 | ... = ... | Use of '=' where '==' may have been intended. | | test.cpp:150:32:150:36 | ... = ... | Use of '=' where '==' may have been intended. | | test.cpp:153:46:153:50 | ... = ... | Use of '=' where '==' may have been intended. | +| test.cpp:166:22:166:27 | ... = ... | Use of '=' where '==' may have been intended. | +| test.cpp:168:24:168:29 | ... = ... | Use of '=' where '==' may have been intended. | +| test.cpp:169:23:169:28 | ... = ... | Use of '=' where '==' may have been intended. | +| test.cpp:171:7:171:12 | ... = ... | Use of '=' where '==' may have been intended. | diff --git a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/AssignWhereCompareMeant/test.cpp b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/AssignWhereCompareMeant/test.cpp index 63cac141cc6..3cd18125467 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/AssignWhereCompareMeant/test.cpp +++ b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/AssignWhereCompareMeant/test.cpp @@ -153,3 +153,21 @@ void f3(int x, int y) { if((x == 10) || ((z == z) && (x == 1)) && (y = 2)) { // BAD } } + +bool use(int); + +void f4(int x, bool b) { + if((x = 10) && use(x)) {} // GOOD: This is likely just a short-hand way of writing an assignment + // followed by a boolean check. + if((x = 10) && b && use(x)) {} // GOOD: Same reason as above + if((x = 10) && use(x) && b) {} // GOOD: Same reason as above + if((x = 10) && (use(x) && b)) {} // GOOD: Same reason as above + + if(use(x) && b && (x = 10)) {} // BAD: The assignment is the last thing that happens in the comparison. + // This doesn't match the usual pattern. + if((use(x) && b) && (x = 10)) {} // BAD: Same reason as above + if(use(x) && (b && (x = 10))) {} // BAD: Same reason as above + + if((x = 10) || use(x)) {} // BAD: This doesn't follow the usual style of writing an assignment in + // a boolean check. +} diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp index 03b202817ca..1ce2558a34f 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp +++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp @@ -189,3 +189,30 @@ int *&conversionInFlow() { int *&pRef = p; // has conversion in the middle of data flow return pRef; // BAD [NOT DETECTED] } + +namespace std { + template + class shared_ptr { + public: + shared_ptr() noexcept; + explicit shared_ptr(T*); + shared_ptr(const shared_ptr&) noexcept; + template shared_ptr(const shared_ptr&) noexcept; + template shared_ptr(shared_ptr&&) noexcept; + + shared_ptr& operator=(const shared_ptr&) noexcept; + shared_ptr& operator=(shared_ptr&&) noexcept; + + T& operator*() const noexcept; + T* operator->() const noexcept; + + T* get() const noexcept; + }; +} + +auto make_read_port() +{ + auto port = std::shared_ptr(new int); + auto ptr = port.get(); + return ptr; // GOOD +} \ No newline at end of file diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/SuspiciousCallToStrncat/SuspiciousCallToStrncat.expected b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/SuspiciousCallToStrncat/SuspiciousCallToStrncat.expected index b4f8836dbe3..114cddb11a5 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/SuspiciousCallToStrncat/SuspiciousCallToStrncat.expected +++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/SuspiciousCallToStrncat/SuspiciousCallToStrncat.expected @@ -1 +1,5 @@ | test.c:24:2:24:8 | call to strncat | Potentially unsafe call to strncat. | +| test.c:45:3:45:9 | call to strncat | Potentially unsafe call to strncat. | +| test.c:67:3:67:9 | call to strncat | Potentially unsafe call to strncat. | +| test.c:75:3:75:9 | call to strncat | Potentially unsafe call to strncat. | +| test.c:76:3:76:9 | call to strncat | Potentially unsafe call to strncat. | diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/SuspiciousCallToStrncat/test.c b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/SuspiciousCallToStrncat/test.c index 3c41316aaf2..f166e5c5ab9 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/SuspiciousCallToStrncat/test.c +++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/SuspiciousCallToStrncat/test.c @@ -39,3 +39,46 @@ void bad1(char *s) { strncat(buf, ".", 1); // BAD [NOT DETECTED] -- Need to check if any space is left } +void strncat_test1(char *s) { + char buf[80]; + strncat(buf, s, sizeof(buf) - strlen(buf) - 1); // GOOD + strncat(buf, s, sizeof(buf) - strlen(buf)); // BAD +} + +void* malloc(size_t); + +void strncat_test2(char *s) { + int len = 80; + char* buf = (char *)malloc(len); + strncat(buf, s, len - strlen(buf) - 1); // GOOD + strncat(buf, s, len - strlen(buf)); // BAD [NOT DETECTED] +} + +struct buffers +{ + char array[50]; + char* pointer; +}; + +void strncat_test3(char* s, struct buffers* buffers) { + unsigned len_array = strlen(buffers->array); + unsigned max_size = sizeof(buffers->array); + unsigned free_size = max_size - len_array; + strncat(buffers->array, s, free_size); // BAD +} + +#define MAX_SIZE 80 + +void strncat_test4(char *s) { + char buf[MAX_SIZE]; + strncat(buf, s, MAX_SIZE - strlen(buf) - 1); // GOOD + strncat(buf, s, MAX_SIZE - strlen(buf)); // BAD + strncat(buf, "...", MAX_SIZE - strlen(buf)); // BAD +} + +void strncat_test5(char *s) { + int len = 80; + char* buf = (char *) malloc(len + 1); + strncat(buf, s, len - strlen(buf) - 1); // GOOD + strncat(buf, s, len - strlen(buf)); // GOOD +} 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 5b45af80d5b..c610e346bb1 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 @@ -26,27 +26,15 @@ edges | 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:62:10:62:15 | buffer indirection | -| test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | (const char *)... | -| test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | data | -| test.cpp:56:12:56:17 | buffer | test.cpp:63:10:63:13 | data indirection | | test.cpp:56:12:56:17 | fgets output argument | test.cpp:62:10:62:15 | (const char *)... | | test.cpp:56:12:56:17 | fgets output argument | test.cpp:62:10:62:15 | buffer | | test.cpp:56:12:56:17 | fgets output argument | test.cpp:62:10:62:15 | buffer indirection | -| test.cpp:56:12:56:17 | fgets output argument | test.cpp:63:10:63:13 | (const char *)... | -| test.cpp:56:12:56:17 | fgets output argument | test.cpp:63:10:63:13 | data | -| test.cpp:56:12:56:17 | fgets output argument | test.cpp:63:10:63:13 | data indirection | | test.cpp:76:12:76:17 | buffer | test.cpp:78:10:78:15 | (const char *)... | | test.cpp:76:12:76:17 | buffer | test.cpp:78:10:78:15 | buffer | | test.cpp:76:12:76:17 | buffer | test.cpp:78:10:78:15 | buffer indirection | -| test.cpp:76:12:76:17 | buffer | test.cpp:79:10:79:13 | (const char *)... | -| test.cpp:76:12:76:17 | buffer | test.cpp:79:10:79:13 | data | -| test.cpp:76:12:76:17 | buffer | test.cpp:79:10:79:13 | data indirection | | test.cpp:76:12:76:17 | fgets output argument | test.cpp:78:10:78:15 | (const char *)... | | 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:78:10:78:15 | buffer indirection | -| 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:76:12:76:17 | fgets output argument | test.cpp:79:10:79:13 | data indirection | | 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 | buffer | test.cpp:99:15:99:20 | buffer indirection | @@ -89,11 +77,6 @@ nodes | test.cpp:62:10:62:15 | buffer | semmle.label | buffer | | test.cpp:62:10:62:15 | buffer indirection | semmle.label | buffer indirection | | test.cpp:62:10:62:15 | buffer indirection | semmle.label | buffer indirection | -| test.cpp:63:10:63:13 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:63:10:63:13 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:63:10:63:13 | data | semmle.label | data | -| test.cpp:63:10:63:13 | data indirection | semmle.label | data indirection | -| test.cpp:63:10:63:13 | data indirection | semmle.label | data indirection | | test.cpp:76:12:76:17 | buffer | semmle.label | buffer | | test.cpp:76:12:76:17 | fgets output argument | semmle.label | fgets output argument | | test.cpp:78:10:78:15 | (const char *)... | semmle.label | (const char *)... | @@ -101,11 +84,6 @@ nodes | test.cpp:78:10:78:15 | buffer | semmle.label | buffer | | test.cpp:78:10:78:15 | buffer indirection | semmle.label | buffer indirection | | test.cpp:78:10:78:15 | buffer indirection | semmle.label | buffer indirection | -| 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:79:10:79:13 | data indirection | semmle.label | data indirection | -| test.cpp:79:10:79:13 | data indirection | semmle.label | data indirection | | 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 *)... | @@ -124,8 +102,6 @@ nodes | 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 | | test.cpp:62:10:62:15 | buffer | test.cpp:56:12:56:17 | buffer | test.cpp:62:10:62:15 | buffer | The value of this argument may come from $@ and is being passed to system | test.cpp:56:12:56:17 | buffer | buffer | -| 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-119/semmle/tests/tests.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/tests.cpp index c4ccdfad8b3..4f8bcc0fa59 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/tests.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/tests.cpp @@ -586,6 +586,23 @@ void test21(bool cond) if (ptr[-1] == 0) { return; } // GOOD: accesses buffer[1] } +void test22(bool b, const char* source) { + char buffer[16]; + int k; + for (k = 0; k <= 100; k++) { + if(k < 16) { + buffer[k] = 'x'; // GOOD + } + } + + char dest[128]; + int n = b ? 1024 : 132; + if (n >= 128) { + return; + } + memcpy(dest, source, n); // GOOD +} + int main(int argc, char *argv[]) { long long arr17[19]; @@ -609,6 +626,7 @@ int main(int argc, char *argv[]) test19(argc == 0); test20(); test21(argc == 0); + test22(argc == 0, argv[0]); return 0; } diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.c b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.c index 3d15905d82d..69a46dd3879 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.c +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.c @@ -86,13 +86,13 @@ int main(int argc, char **argv) { i3 = argv[1]; printf(i3); - // BAD: varOne is 1 so condition is true and it always goes inside the if + // BAD [FALSE NEGATIVE]: varOne is 1 so condition is true and it always goes inside the if char *i4; if (varOne) i4 = argv[1]; printf(i4); - // BAD: varZero is 0 so condition is true and it always goes inside the if + // BAD [FALSE NEGATIVE]: varZero is 0 so condition is true and it always goes inside the if char *i5; if (!varZero) i5 = argv[1]; diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected index 50ae940400a..8c2c89035e2 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected @@ -39,22 +39,6 @@ edges | ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 | | ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 indirection | | ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 indirection | -| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | (const char *)... | -| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | (const char *)... | -| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 | -| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 | -| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 | -| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 | -| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 indirection | -| ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 indirection | -| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | (const char *)... | -| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | (const char *)... | -| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 | -| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 | -| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 | -| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 | -| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 indirection | -| ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 indirection | | ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | (const char *)... | | ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | (const char *)... | | ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | i6 | @@ -133,24 +117,6 @@ nodes | ifs.c:87:9:87:10 | i3 | semmle.label | i3 | | ifs.c:87:9:87:10 | i3 indirection | semmle.label | i3 indirection | | ifs.c:87:9:87:10 | i3 indirection | semmle.label | i3 indirection | -| ifs.c:92:8:92:11 | argv | semmle.label | argv | -| ifs.c:92:8:92:11 | argv | semmle.label | argv | -| ifs.c:93:9:93:10 | (const char *)... | semmle.label | (const char *)... | -| ifs.c:93:9:93:10 | (const char *)... | semmle.label | (const char *)... | -| ifs.c:93:9:93:10 | i4 | semmle.label | i4 | -| ifs.c:93:9:93:10 | i4 | semmle.label | i4 | -| ifs.c:93:9:93:10 | i4 | semmle.label | i4 | -| ifs.c:93:9:93:10 | i4 indirection | semmle.label | i4 indirection | -| ifs.c:93:9:93:10 | i4 indirection | semmle.label | i4 indirection | -| ifs.c:98:8:98:11 | argv | semmle.label | argv | -| ifs.c:98:8:98:11 | argv | semmle.label | argv | -| ifs.c:99:9:99:10 | (const char *)... | semmle.label | (const char *)... | -| ifs.c:99:9:99:10 | (const char *)... | semmle.label | (const char *)... | -| ifs.c:99:9:99:10 | i5 | semmle.label | i5 | -| ifs.c:99:9:99:10 | i5 | semmle.label | i5 | -| ifs.c:99:9:99:10 | i5 | semmle.label | i5 | -| ifs.c:99:9:99:10 | i5 indirection | semmle.label | i5 indirection | -| ifs.c:99:9:99:10 | i5 indirection | semmle.label | i5 indirection | | ifs.c:105:8:105:11 | argv | semmle.label | argv | | ifs.c:105:8:105:11 | argv | semmle.label | argv | | ifs.c:106:9:106:10 | (const char *)... | semmle.label | (const char *)... | @@ -193,8 +159,6 @@ nodes | ifs.c:75:9:75:10 | i1 | ifs.c:74:8:74:11 | argv | ifs.c:75:9:75:10 | i1 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:74:8:74:11 | argv | argv | | ifs.c:81:9:81:10 | i2 | ifs.c:80:8:80:11 | argv | ifs.c:81:9:81:10 | i2 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:80:8:80:11 | argv | argv | | ifs.c:87:9:87:10 | i3 | ifs.c:86:8:86:11 | argv | ifs.c:87:9:87:10 | i3 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:86:8:86:11 | argv | argv | -| ifs.c:93:9:93:10 | i4 | ifs.c:92:8:92:11 | argv | ifs.c:93:9:93:10 | i4 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:92:8:92:11 | argv | argv | -| ifs.c:99:9:99:10 | i5 | ifs.c:98:8:98:11 | argv | ifs.c:99:9:99:10 | i5 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:98:8:98:11 | argv | argv | | ifs.c:106:9:106:10 | i6 | ifs.c:105:8:105:11 | argv | ifs.c:106:9:106:10 | i6 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:105:8:105:11 | argv | argv | | ifs.c:112:9:112:10 | i7 | ifs.c:111:8:111:11 | argv | ifs.c:112:9:112:10 | i7 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:111:8:111:11 | argv | argv | | ifs.c:118:9:118:10 | i8 | ifs.c:117:8:117:11 | argv | ifs.c:118:9:118:10 | i8 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | ifs.c:117:8:117:11 | argv | argv | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp new file mode 100644 index 00000000000..2cde5e689c7 --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test3.cpp @@ -0,0 +1,7 @@ +void test_issue_5850(unsigned char small, unsigned int large1) { + for(; small < static_cast(large1 - 1); small++) { } // GOOD +} + +void test_widening(unsigned char small, char large) { + for(; small < static_cast(static_cast(large) - 1); small++) { } // GOOD +} 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 25ff3162973..752e9165c07 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 @@ -1,228 +1,174 @@ edges -| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | (size_t)... | -| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | (size_t)... | -| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | -| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | -| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | -| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | -| test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | -| test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | -| test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | -| test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | -| test.cpp:39:21:39:24 | argv | test.cpp:45:38:45:63 | ... + ... | -| test.cpp:39:21:39:24 | argv | test.cpp:45:38:45:63 | ... + ... | -| test.cpp:39:21:39:24 | argv | test.cpp:45:38:45:63 | ... + ... | -| test.cpp:39:21:39:24 | argv | test.cpp:45:38:45:63 | ... + ... | -| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | (size_t)... | -| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | (size_t)... | -| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size | -| test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | -| test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | -| test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | -| test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | -| test.cpp:75:25:75:29 | start | test.cpp:79:18:79:28 | ... - ... | -| test.cpp:75:25:75:29 | start | test.cpp:79:18:79:28 | ... - ... | -| test.cpp:75:38:75:40 | end | test.cpp:79:18:79:28 | ... - ... | -| test.cpp:75:38:75:40 | end | test.cpp:79:18:79:28 | ... - ... | -| test.cpp:97:18:97:23 | buffer | test.cpp:100:4:100:15 | buffer | -| test.cpp:97:18:97:23 | buffer | test.cpp:100:17:100:22 | buffer indirection | -| test.cpp:97:18:97:23 | buffer | test.cpp:101:4:101:15 | ... + ... | -| test.cpp:97:18:97:23 | buffer | test.cpp:101:4:101:15 | buffer | -| test.cpp:97:18:97:23 | fread output argument | test.cpp:100:4:100:15 | buffer | -| test.cpp:97:18:97:23 | fread output argument | test.cpp:100:17:100:22 | buffer indirection | -| test.cpp:97:18:97:23 | fread output argument | test.cpp:101:4:101:15 | ... + ... | -| test.cpp:97:18:97:23 | fread output argument | test.cpp:101:4:101:15 | buffer | -| test.cpp:100:4:100:15 | buffer | test.cpp:100:17:100:22 | processData1 output argument | -| test.cpp:100:17:100:22 | buffer indirection | test.cpp:100:17:100:22 | processData1 output argument | -| test.cpp:100:17:100:22 | processData1 output argument | test.cpp:101:4:101:15 | ... + ... | -| test.cpp:100:17:100:22 | processData1 output argument | test.cpp:101:4:101:15 | buffer | -| test.cpp:101:4:101:15 | ... + ... | test.cpp:75:38:75:40 | end | -| test.cpp:101:4:101:15 | buffer | test.cpp:75:25:75:29 | start | -| test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... | -| test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... | -| test.cpp:123:18:123:31 | (const char *)... | test.cpp:127:24:127:41 | ... * ... | -| test.cpp:123:18:123:31 | (const char *)... | test.cpp:127:24:127:41 | ... * ... | -| test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... | -| test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... | -| test.cpp:132:19:132:32 | (const char *)... | test.cpp:134:10:134:27 | ... * ... | -| test.cpp:132:19:132:32 | (const char *)... | test.cpp:134:10:134:27 | ... * ... | -| test.cpp:138:19:138:24 | call to getenv | test.cpp:142:11:142:28 | ... * ... | -| test.cpp:138:19:138:24 | call to getenv | test.cpp:142:11:142:28 | ... * ... | -| test.cpp:138:19:138:32 | (const char *)... | test.cpp:142:11:142:28 | ... * ... | -| test.cpp:138:19:138:32 | (const char *)... | test.cpp:142:11:142:28 | ... * ... | -| test.cpp:201:9:201:42 | Store | test.cpp:231:9:231:24 | call to get_tainted_size | -| test.cpp:201:9:201:42 | Store | test.cpp:231:9:231:24 | call to get_tainted_size | -| test.cpp:201:14:201:19 | call to getenv | test.cpp:201:9:201:42 | Store | -| test.cpp:201:14:201:27 | (const char *)... | test.cpp:201:9:201:42 | Store | -| test.cpp:214:23:214:23 | s | test.cpp:215:21:215:21 | s | -| 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: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: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] | -| test.cpp:241:18:241:31 | (const char *)... | test.cpp:241:2:241:32 | Chi [array content] | -| test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... | -| test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... | -| test.cpp:249:20:249:33 | (const char *)... | test.cpp:253:11:253:29 | ... * ... | -| test.cpp:249:20:249:33 | (const char *)... | test.cpp:253:11:253:29 | ... * ... | -| test.cpp:279:17:279:20 | Chi | test.cpp:281:11:281:28 | ... * ... | -| test.cpp:279:17:279:20 | Chi | test.cpp:281:11:281:28 | ... * ... | -| test.cpp:279:17:279:20 | get_size output argument [array content] | test.cpp:279:17:279:20 | Chi | -| test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... | -| test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... | -| test.cpp:295:18:295:21 | get_size output argument [array content] | test.cpp:295:18:295:21 | Chi | -| test.cpp:301:19:301:24 | call to getenv | test.cpp:305:11:305:28 | ... * ... | -| test.cpp:301:19:301:24 | call to getenv | test.cpp:305:11:305:28 | ... * ... | -| test.cpp:301:19:301:32 | (const char *)... | test.cpp:305:11:305:28 | ... * ... | -| test.cpp:301:19:301:32 | (const char *)... | test.cpp:305:11:305:28 | ... * ... | -| test.cpp:309:19:309:24 | call to getenv | test.cpp:314:10:314:27 | ... * ... | -| test.cpp:309:19:309:24 | call to getenv | test.cpp:314:10:314:27 | ... * ... | -| test.cpp:309:19:309:32 | (const char *)... | test.cpp:314:10:314:27 | ... * ... | -| test.cpp:309:19:309:32 | (const char *)... | test.cpp:314:10:314:27 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | (size_t)... | +| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | (size_t)... | +| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | tainted | +| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | tainted | +| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | tainted | +| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | tainted | +| test.cpp:40:21:40:24 | argv | test.cpp:44:38:44:63 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:44:38:44:63 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:44:38:44:63 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:44:38:44:63 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:46:38:46:63 | ... + ... | +| test.cpp:40:21:40:24 | argv | test.cpp:46:38:46:63 | ... + ... | +| test.cpp:40:21:40:24 | argv | test.cpp:46:38:46:63 | ... + ... | +| test.cpp:40:21:40:24 | argv | test.cpp:46:38:46:63 | ... + ... | +| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | (size_t)... | +| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | (size_t)... | +| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:50:26:50:29 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:50:26:50:29 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:50:26:50:29 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:50:26:50:29 | size | +| test.cpp:40:21:40:24 | argv | test.cpp:53:35:53:60 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:53:35:53:60 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:53:35:53:60 | ... * ... | +| test.cpp:40:21:40:24 | argv | test.cpp:53:35:53:60 | ... * ... | +| test.cpp:124:18:124:23 | call to getenv | test.cpp:128:24:128:41 | ... * ... | +| test.cpp:124:18:124:23 | call to getenv | test.cpp:128:24:128:41 | ... * ... | +| test.cpp:124:18:124:31 | (const char *)... | test.cpp:128:24:128:41 | ... * ... | +| test.cpp:124:18:124:31 | (const char *)... | test.cpp:128:24:128:41 | ... * ... | +| test.cpp:133:19:133:24 | call to getenv | test.cpp:135:10:135:27 | ... * ... | +| test.cpp:133:19:133:24 | call to getenv | test.cpp:135:10:135:27 | ... * ... | +| test.cpp:133:19:133:32 | (const char *)... | test.cpp:135:10:135:27 | ... * ... | +| test.cpp:133:19:133:32 | (const char *)... | test.cpp:135:10:135:27 | ... * ... | +| test.cpp:148:20:148:25 | call to getenv | test.cpp:152:11:152:28 | ... * ... | +| test.cpp:148:20:148:25 | call to getenv | test.cpp:152:11:152:28 | ... * ... | +| test.cpp:148:20:148:33 | (const char *)... | test.cpp:152:11:152:28 | ... * ... | +| test.cpp:148:20:148:33 | (const char *)... | test.cpp:152:11:152:28 | ... * ... | +| test.cpp:211:9:211:42 | Store | test.cpp:241:9:241:24 | call to get_tainted_size | +| test.cpp:211:9:211:42 | Store | test.cpp:241:9:241:24 | call to get_tainted_size | +| test.cpp:211:14:211:19 | call to getenv | test.cpp:211:9:211:42 | Store | +| test.cpp:211:14:211:27 | (const char *)... | test.cpp:211:9:211:42 | Store | +| test.cpp:224:23:224:23 | s | test.cpp:225:21:225:21 | s | +| test.cpp:224:23:224:23 | s | test.cpp:225:21:225:21 | s | +| test.cpp:230:21:230:21 | s | test.cpp:231:21:231:21 | s | +| test.cpp:230:21:230:21 | s | test.cpp:231:21:231:21 | s | +| test.cpp:237:24:237:29 | call to getenv | test.cpp:239:9:239:18 | (size_t)... | +| test.cpp:237:24:237:29 | call to getenv | test.cpp:239:9:239:18 | local_size | +| test.cpp:237:24:237:29 | call to getenv | test.cpp:239:9:239:18 | local_size | +| test.cpp:237:24:237:29 | call to getenv | test.cpp:245:2:245:9 | local_size | +| test.cpp:237:24:237:29 | call to getenv | test.cpp:247:2:247:8 | local_size | +| test.cpp:237:24:237:37 | (const char *)... | test.cpp:239:9:239:18 | (size_t)... | +| test.cpp:237:24:237:37 | (const char *)... | test.cpp:239:9:239:18 | local_size | +| test.cpp:237:24:237:37 | (const char *)... | test.cpp:239:9:239:18 | local_size | +| test.cpp:237:24:237:37 | (const char *)... | test.cpp:245:2:245:9 | local_size | +| test.cpp:237:24:237:37 | (const char *)... | test.cpp:247:2:247:8 | local_size | +| test.cpp:245:2:245:9 | local_size | test.cpp:224:23:224:23 | s | +| test.cpp:247:2:247:8 | local_size | test.cpp:230:21:230:21 | s | +| test.cpp:251:2:251:32 | Chi [array content] | test.cpp:289:17:289:20 | get_size output argument [array content] | +| test.cpp:251:2:251:32 | Chi [array content] | test.cpp:305:18:305:21 | get_size output argument [array content] | +| test.cpp:251:18:251:23 | call to getenv | test.cpp:251:2:251:32 | Chi [array content] | +| test.cpp:251:18:251:31 | (const char *)... | test.cpp:251:2:251:32 | Chi [array content] | +| test.cpp:259:20:259:25 | call to getenv | test.cpp:263:11:263:29 | ... * ... | +| test.cpp:259:20:259:25 | call to getenv | test.cpp:263:11:263:29 | ... * ... | +| test.cpp:259:20:259:33 | (const char *)... | test.cpp:263:11:263:29 | ... * ... | +| test.cpp:259:20:259:33 | (const char *)... | test.cpp:263:11:263:29 | ... * ... | +| test.cpp:289:17:289:20 | Chi | test.cpp:291:11:291:28 | ... * ... | +| test.cpp:289:17:289:20 | Chi | test.cpp:291:11:291:28 | ... * ... | +| test.cpp:289:17:289:20 | get_size output argument [array content] | test.cpp:289:17:289:20 | Chi | +| test.cpp:305:18:305:21 | Chi | test.cpp:308:10:308:27 | ... * ... | +| test.cpp:305:18:305:21 | Chi | test.cpp:308:10:308:27 | ... * ... | +| test.cpp:305:18:305:21 | get_size output argument [array content] | test.cpp:305:18:305:21 | Chi | nodes -| test.cpp:39:21:39:24 | argv | semmle.label | argv | -| test.cpp:39:21:39:24 | argv | semmle.label | argv | -| test.cpp:42:38:42:44 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:42:38:42:44 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:42:38:42:44 | tainted | semmle.label | tainted | -| test.cpp:42:38:42:44 | tainted | semmle.label | tainted | -| test.cpp:42:38:42:44 | tainted | semmle.label | tainted | -| test.cpp:43:38:43:63 | ... * ... | semmle.label | ... * ... | -| test.cpp:43:38:43:63 | ... * ... | semmle.label | ... * ... | -| test.cpp:43:38:43:63 | ... * ... | semmle.label | ... * ... | -| test.cpp:45:38:45:63 | ... + ... | semmle.label | ... + ... | -| test.cpp:45:38:45:63 | ... + ... | semmle.label | ... + ... | -| test.cpp:45:38:45:63 | ... + ... | semmle.label | ... + ... | -| test.cpp:48:32:48:35 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:48:32:48:35 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:48:32:48:35 | size | semmle.label | size | -| test.cpp:48:32:48:35 | size | semmle.label | size | -| test.cpp:48:32:48:35 | size | semmle.label | size | -| test.cpp:49:26:49:29 | size | semmle.label | size | -| test.cpp:49:26:49:29 | size | semmle.label | size | -| test.cpp:49:26:49:29 | size | semmle.label | size | -| test.cpp:52:35:52:60 | ... * ... | semmle.label | ... * ... | -| test.cpp:52:35:52:60 | ... * ... | semmle.label | ... * ... | -| test.cpp:52:35:52:60 | ... * ... | semmle.label | ... * ... | -| test.cpp:64:25:64:30 | *buffer | semmle.label | *buffer | -| test.cpp:64:25:64:30 | *buffer | semmle.label | *buffer | -| test.cpp:64:25:64:30 | buffer | semmle.label | buffer | -| test.cpp:75:25:75:29 | start | semmle.label | start | -| test.cpp:75:38:75:40 | end | semmle.label | end | -| test.cpp:79:18:79:28 | ... - ... | semmle.label | ... - ... | -| test.cpp:79:18:79:28 | ... - ... | semmle.label | ... - ... | -| test.cpp:79:18:79:28 | ... - ... | semmle.label | ... - ... | -| test.cpp:97:18:97:23 | buffer | semmle.label | buffer | -| test.cpp:97:18:97:23 | fread output argument | semmle.label | fread output argument | -| test.cpp:100:4:100:15 | buffer | semmle.label | buffer | -| test.cpp:100:17:100:22 | buffer indirection | semmle.label | buffer indirection | -| test.cpp:100:17:100:22 | processData1 output argument | semmle.label | processData1 output argument | -| test.cpp:101:4:101:15 | ... + ... | semmle.label | ... + ... | -| test.cpp:101:4:101:15 | buffer | semmle.label | buffer | -| test.cpp:123:18:123:23 | call to getenv | semmle.label | call to getenv | -| test.cpp:123:18:123:31 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:127:24:127:41 | ... * ... | semmle.label | ... * ... | -| test.cpp:127:24:127:41 | ... * ... | semmle.label | ... * ... | -| test.cpp:127:24:127:41 | ... * ... | semmle.label | ... * ... | -| test.cpp:132:19:132:24 | call to getenv | semmle.label | call to getenv | -| test.cpp:132:19:132:32 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:134:10:134:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:134:10:134:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:134:10:134:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:138:19:138:24 | call to getenv | semmle.label | call to getenv | -| test.cpp:138:19:138:32 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:142:11:142:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:142:11:142:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:142:11:142:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:201:9:201:42 | Store | semmle.label | Store | -| test.cpp:201:14:201:19 | call to getenv | semmle.label | call to getenv | -| test.cpp:201:14:201:27 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:214:23:214:23 | s | semmle.label | s | -| test.cpp:215:21:215:21 | s | semmle.label | s | -| test.cpp:215:21:215:21 | s | semmle.label | s | -| test.cpp:215:21:215:21 | s | semmle.label | s | -| test.cpp:220:21:220:21 | s | semmle.label | s | -| test.cpp:221:21:221:21 | s | semmle.label | s | -| test.cpp:221:21:221:21 | s | semmle.label | s | -| test.cpp:221:21:221:21 | s | semmle.label | s | -| test.cpp:227:24:227:29 | call to getenv | semmle.label | call to getenv | -| test.cpp:227:24:227:37 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:229:9:229:18 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:229:9:229:18 | (size_t)... | semmle.label | (size_t)... | -| test.cpp:229:9:229:18 | local_size | semmle.label | local_size | -| test.cpp:229:9:229:18 | local_size | semmle.label | local_size | -| test.cpp:229:9:229:18 | local_size | semmle.label | local_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:231:9:231:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | -| 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 | -| test.cpp:241:18:241:31 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:249:20:249:25 | call to getenv | semmle.label | call to getenv | -| test.cpp:249:20:249:33 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:253:11:253:29 | ... * ... | semmle.label | ... * ... | -| test.cpp:253:11:253:29 | ... * ... | semmle.label | ... * ... | -| test.cpp:253:11:253:29 | ... * ... | semmle.label | ... * ... | -| test.cpp:279:17:279:20 | Chi | semmle.label | Chi | -| test.cpp:279:17:279:20 | get_size output argument [array content] | semmle.label | get_size output argument [array content] | -| test.cpp:281:11:281:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:281:11:281:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:281:11:281:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:295:18:295:21 | Chi | semmle.label | Chi | -| test.cpp:295:18:295:21 | get_size output argument [array content] | semmle.label | get_size output argument [array content] | -| test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:301:19:301:24 | call to getenv | semmle.label | call to getenv | -| test.cpp:301:19:301:32 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:305:11:305:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:305:11:305:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:305:11:305:28 | ... * ... | semmle.label | ... * ... | -| test.cpp:309:19:309:24 | call to getenv | semmle.label | call to getenv | -| test.cpp:309:19:309:32 | (const char *)... | semmle.label | (const char *)... | -| test.cpp:314:10:314:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:314:10:314:27 | ... * ... | semmle.label | ... * ... | -| test.cpp:314:10:314:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:40:21:40:24 | argv | semmle.label | argv | +| test.cpp:40:21:40:24 | argv | semmle.label | argv | +| test.cpp:43:38:43:44 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:43:38:43:44 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:43:38:43:44 | tainted | semmle.label | tainted | +| test.cpp:43:38:43:44 | tainted | semmle.label | tainted | +| test.cpp:43:38:43:44 | tainted | semmle.label | tainted | +| test.cpp:44:38:44:63 | ... * ... | semmle.label | ... * ... | +| test.cpp:44:38:44:63 | ... * ... | semmle.label | ... * ... | +| test.cpp:44:38:44:63 | ... * ... | semmle.label | ... * ... | +| test.cpp:46:38:46:63 | ... + ... | semmle.label | ... + ... | +| test.cpp:46:38:46:63 | ... + ... | semmle.label | ... + ... | +| test.cpp:46:38:46:63 | ... + ... | semmle.label | ... + ... | +| test.cpp:49:32:49:35 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:49:32:49:35 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:49:32:49:35 | size | semmle.label | size | +| test.cpp:49:32:49:35 | size | semmle.label | size | +| test.cpp:49:32:49:35 | size | semmle.label | size | +| test.cpp:50:26:50:29 | size | semmle.label | size | +| test.cpp:50:26:50:29 | size | semmle.label | size | +| test.cpp:50:26:50:29 | size | semmle.label | size | +| test.cpp:53:35:53:60 | ... * ... | semmle.label | ... * ... | +| test.cpp:53:35:53:60 | ... * ... | semmle.label | ... * ... | +| test.cpp:53:35:53:60 | ... * ... | semmle.label | ... * ... | +| test.cpp:124:18:124:23 | call to getenv | semmle.label | call to getenv | +| test.cpp:124:18:124:31 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:128:24:128:41 | ... * ... | semmle.label | ... * ... | +| test.cpp:128:24:128:41 | ... * ... | semmle.label | ... * ... | +| test.cpp:128:24:128:41 | ... * ... | semmle.label | ... * ... | +| test.cpp:133:19:133:24 | call to getenv | semmle.label | call to getenv | +| test.cpp:133:19:133:32 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:135:10:135:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:135:10:135:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:135:10:135:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:148:20:148:25 | call to getenv | semmle.label | call to getenv | +| test.cpp:148:20:148:33 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:152:11:152:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:152:11:152:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:152:11:152:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:211:9:211:42 | Store | semmle.label | Store | +| test.cpp:211:14:211:19 | call to getenv | semmle.label | call to getenv | +| test.cpp:211:14:211:27 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:224:23:224:23 | s | semmle.label | s | +| test.cpp:225:21:225:21 | s | semmle.label | s | +| test.cpp:225:21:225:21 | s | semmle.label | s | +| test.cpp:225:21:225:21 | s | semmle.label | s | +| test.cpp:230:21:230:21 | s | semmle.label | s | +| test.cpp:231:21:231:21 | s | semmle.label | s | +| test.cpp:231:21:231:21 | s | semmle.label | s | +| test.cpp:231:21:231:21 | s | semmle.label | s | +| test.cpp:237:24:237:29 | call to getenv | semmle.label | call to getenv | +| test.cpp:237:24:237:37 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:239:9:239:18 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:239:9:239:18 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:239:9:239:18 | local_size | semmle.label | local_size | +| test.cpp:239:9:239:18 | local_size | semmle.label | local_size | +| test.cpp:239:9:239:18 | local_size | semmle.label | local_size | +| test.cpp:241:9:241:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | +| test.cpp:241:9:241:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | +| test.cpp:241:9:241:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | +| test.cpp:245:2:245:9 | local_size | semmle.label | local_size | +| test.cpp:247:2:247:8 | local_size | semmle.label | local_size | +| test.cpp:251:2:251:32 | Chi [array content] | semmle.label | Chi [array content] | +| test.cpp:251:2:251:32 | ChiPartial | semmle.label | ChiPartial | +| test.cpp:251:18:251:23 | call to getenv | semmle.label | call to getenv | +| test.cpp:251:18:251:31 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:259:20:259:25 | call to getenv | semmle.label | call to getenv | +| test.cpp:259:20:259:33 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:263:11:263:29 | ... * ... | semmle.label | ... * ... | +| test.cpp:263:11:263:29 | ... * ... | semmle.label | ... * ... | +| test.cpp:263:11:263:29 | ... * ... | semmle.label | ... * ... | +| test.cpp:289:17:289:20 | Chi | semmle.label | Chi | +| test.cpp:289:17:289:20 | get_size output argument [array content] | semmle.label | get_size output argument [array content] | +| test.cpp:291:11:291:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:291:11:291:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:291:11:291:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:305:18:305:21 | Chi | semmle.label | Chi | +| test.cpp:305:18:305:21 | get_size output argument [array content] | semmle.label | get_size output argument [array content] | +| test.cpp:308:10:308:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:308:10:308:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:308:10:308:27 | ... * ... | semmle.label | ... * ... | #select -| test.cpp:42:31:42:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | -| test.cpp:43:31:43:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | -| test.cpp:45:31:45:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:45:38:45:63 | ... + ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | -| test.cpp:48:25:48:30 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | -| test.cpp:49:17:49:30 | new[] | test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | -| test.cpp:52:21:52:27 | call to realloc | test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | -| test.cpp:79:9:79:29 | new[] | test.cpp:97:18:97:23 | buffer | test.cpp:79:18:79:28 | ... - ... | This allocation size is derived from $@ and might overflow | test.cpp:97:18:97:23 | buffer | user input (fread) | -| test.cpp:127:17:127:22 | call to malloc | test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:123:18:123:23 | call to getenv | user input (getenv) | -| test.cpp:134:3:134:8 | call to malloc | test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:132:19:132:24 | call to getenv | user input (getenv) | -| test.cpp:142:4:142:9 | call to malloc | test.cpp:138:19:138:24 | call to getenv | test.cpp:142:11:142:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:138:19:138:24 | call to getenv | user input (getenv) | -| test.cpp:215:14:215:19 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:215:21:215:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) | -| test.cpp:221:14:221:19 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:221:21:221:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) | -| test.cpp:229:2:229:7 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | local_size | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) | -| test.cpp:231:2:231:7 | call to malloc | test.cpp:201:14:201:19 | call to getenv | test.cpp:231:9:231:24 | call to get_tainted_size | This allocation size is derived from $@ and might overflow | test.cpp:201:14:201:19 | call to getenv | user input (getenv) | -| test.cpp:253:4:253:9 | call to malloc | test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:249:20:249:25 | call to getenv | user input (getenv) | -| test.cpp:281:4:281:9 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:281:11:281:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) | -| test.cpp:298:3:298:8 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:298:10:298:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) | -| test.cpp:305:4:305:9 | call to malloc | test.cpp:301:19:301:24 | call to getenv | test.cpp:305:11:305:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:301:19:301:24 | call to getenv | user input (getenv) | -| test.cpp:314:3:314:8 | call to malloc | test.cpp:309:19:309:24 | call to getenv | test.cpp:314:10:314:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:309:19:309:24 | call to getenv | user input (getenv) | +| test.cpp:43:31:43:36 | call to malloc | test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | tainted | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) | +| test.cpp:44:31:44:36 | call to malloc | test.cpp:40:21:40:24 | argv | test.cpp:44:38:44:63 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) | +| test.cpp:46:31:46:36 | call to malloc | test.cpp:40:21:40:24 | argv | test.cpp:46:38:46:63 | ... + ... | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) | +| test.cpp:49:25:49:30 | call to malloc | test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | size | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) | +| test.cpp:50:17:50:30 | new[] | test.cpp:40:21:40:24 | argv | test.cpp:50:26:50:29 | size | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) | +| test.cpp:53:21:53:27 | call to realloc | test.cpp:40:21:40:24 | argv | test.cpp:53:35:53:60 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) | +| test.cpp:128:17:128:22 | call to malloc | test.cpp:124:18:124:23 | call to getenv | test.cpp:128:24:128:41 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:124:18:124:23 | call to getenv | user input (getenv) | +| test.cpp:135:3:135:8 | call to malloc | test.cpp:133:19:133:24 | call to getenv | test.cpp:135:10:135:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:133:19:133:24 | call to getenv | user input (getenv) | +| test.cpp:152:4:152:9 | call to malloc | test.cpp:148:20:148:25 | call to getenv | test.cpp:152:11:152:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:148:20:148:25 | call to getenv | user input (getenv) | +| test.cpp:225:14:225:19 | call to malloc | test.cpp:237:24:237:29 | call to getenv | test.cpp:225:21:225:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:237:24:237:29 | call to getenv | user input (getenv) | +| test.cpp:231:14:231:19 | call to malloc | test.cpp:237:24:237:29 | call to getenv | test.cpp:231:21:231:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:237:24:237:29 | call to getenv | user input (getenv) | +| test.cpp:239:2:239:7 | call to malloc | test.cpp:237:24:237:29 | call to getenv | test.cpp:239:9:239:18 | local_size | This allocation size is derived from $@ and might overflow | test.cpp:237:24:237:29 | call to getenv | user input (getenv) | +| test.cpp:241:2:241:7 | call to malloc | test.cpp:211:14:211:19 | call to getenv | test.cpp:241:9:241:24 | call to get_tainted_size | This allocation size is derived from $@ and might overflow | test.cpp:211:14:211:19 | call to getenv | user input (getenv) | +| test.cpp:263:4:263:9 | call to malloc | test.cpp:259:20:259:25 | call to getenv | test.cpp:263:11:263:29 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:259:20:259:25 | call to getenv | user input (getenv) | +| test.cpp:291:4:291:9 | call to malloc | test.cpp:251:18:251:23 | call to getenv | test.cpp:291:11:291:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:251:18:251:23 | call to getenv | user input (getenv) | +| test.cpp:308:3:308:8 | call to malloc | test.cpp:251:18:251:23 | call to getenv | test.cpp:308:10:308:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:251:18:251:23 | call to getenv | user input (getenv) | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp index 943bc3b1214..b11a136ed24 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp @@ -7,6 +7,7 @@ void *malloc(size_t size); void *realloc(void *ptr, size_t size); void free(void *ptr); int atoi(const char *nptr); +long atol(const char *nptr); struct MyStruct { char data[256]; @@ -76,7 +77,7 @@ void processData2(char *start, char *end) { char *copy; - copy = new char[end - start]; // GOOD [FALSE POSITIVE] + copy = new char[end - start]; // GOOD // ... @@ -137,6 +138,15 @@ void more_bounded_tests() { { int size = atoi(getenv("USER")); + if (size > 0) + { + malloc(size * sizeof(int)); // GOOD (overflow not possible) + } + } + + { + long size = atol(getenv("USER")); + if (size > 0) { malloc(size * sizeof(int)); // BAD @@ -302,7 +312,7 @@ void equality_cases() { if ((size == 50) || (size == 100)) { - malloc(size * sizeof(int)); // GOOD [FALSE POSITIVE] + malloc(size * sizeof(int)); // GOOD } } { @@ -311,6 +321,15 @@ void equality_cases() { if (size != 50 && size != 100) return; - malloc(size * sizeof(int)); // GOOD [FALSE POSITIVE] + malloc(size * sizeof(int)); // GOOD } } + +char * strstr(char *, const char *); + +void ptr_diff_case() { + char* user = getenv("USER"); + char* admin_begin_pos = strstr(user, "ADMIN"); + int offset = admin_begin_pos ? user - admin_begin_pos : 0; + malloc(offset); // GOOD +} \ No newline at end of file diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/ArithmeticWithExtremeValues.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/ArithmeticWithExtremeValues.expected index a46371f36b6..cbaf5d63079 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/ArithmeticWithExtremeValues.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/ArithmeticWithExtremeValues.expected @@ -3,6 +3,4 @@ | test.c:50:3:50:5 | sc3 | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:49:9:49:16 | 127 | Extreme value | | test.c:59:3:59:5 | sc6 | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:58:9:58:16 | 127 | Extreme value | | test.c:63:3:63:5 | sc8 | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:62:9:62:16 | - ... | Extreme value | -| test.c:75:3:75:5 | sc1 | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:74:9:74:16 | 127 | Extreme value | -| test.c:76:3:76:5 | sc1 | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:74:9:74:16 | 127 | Extreme value | | test.c:124:9:124:9 | x | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:118:17:118:23 | 2147483647 | Extreme value | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/test.c b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/test.c index 8c40d984ee0..8760641c8e2 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/test.c +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/test.c @@ -72,8 +72,8 @@ void test_negatives() { signed char sc1, sc2, sc3, sc4, sc5, sc6, sc7, sc8; sc1 = CHAR_MAX; - sc1 += 0; // GOOD [FALSE POSITIVE] - sc1 += -1; // GOOD [FALSE POSITIVE] + sc1 += 0; // GOOD + sc1 += -1; // GOOD sc2 = CHAR_MIN; sc2 += -1; // BAD [NOT DETECTED] sc3 = CHAR_MIN; diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected index 8bb25025b86..bdf00e0a5df 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected @@ -1,8 +1,5 @@ | test2.cpp:14:11:14:11 | v | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test2.cpp:25:22:25:23 | & ... | User-provided value | | test2.cpp:14:11:14:11 | v | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test2.cpp:25:22:25:23 | & ... | User-provided value | -| test3.c:15:10:15:10 | x | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test3.c:11:15:11:18 | argv | User-provided value | -| test3.c:15:14:15:14 | y | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test3.c:11:15:11:18 | argv | User-provided value | -| test3.c:15:18:15:18 | z | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test3.c:11:15:11:18 | argv | User-provided value | | test5.cpp:17:6:17:18 | call to getTaintedInt | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | | test5.cpp:19:6:19:6 | y | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value | | test5.cpp:19:6:19:6 | y | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test5.cpp:9:7:9:9 | buf | User-provided value | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml new file mode 100644 index 00000000000..6cae1aa77cb --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml @@ -0,0 +1,6 @@ +# This directory has its own qlpack for reasons detailed in commit 2550788598010fa2117274607c9d58f64f997f34 +name: codeql-cpp-tests-cwe-190-tainted +version: 0.0.0 +libraryPathDependencies: codeql-cpp +extractor: cpp +tests: . diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/queries.xml b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/queries.xml deleted file mode 100644 index 99f4a7278c2..00000000000 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/queries.xml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp index 527c603d1b8..2ee675be6b5 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test5.cpp @@ -18,3 +18,25 @@ void useTaintedInt() y = getTaintedInt(); y = y * 1024; // BAD: arithmetic on a tainted value } + +typedef long long int intmax_t; + +intmax_t imaxabs(intmax_t j); + +void useTaintedIntWithGuard() { + int tainted = getTaintedInt(); + + if(imaxabs(tainted) <= 100) { + int product = tainted * tainted; // GOOD: can't underflow/overflow + } +} + +#define INTMAX_MIN (-0x7fffffffffffffff - 1) + +void useTaintedIntWithGuardIntMaxMin() { + intmax_t tainted = getTaintedInt(); + + if(imaxabs(tainted) <= INTMAX_MIN) { + int product = tainted * tainted; // BAD: imaxabs(INTMAX_MIN) == INTMAX_MIN [NOT DETECTED] + } +} \ No newline at end of file diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected index ca8dd38fc3b..097efb73b9f 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/ArithmeticUncontrolled.expected @@ -7,30 +7,10 @@ edges | test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r | | test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r | | test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r | -| test.c:39:13:39:21 | ... % ... | test.c:40:5:40:5 | r | -| test.c:39:13:39:21 | ... % ... | test.c:40:5:40:5 | r | -| test.c:39:13:39:21 | ... % ... | test.c:40:5:40:5 | r | -| test.c:39:13:39:21 | ... % ... | test.c:40:5:40:5 | r | | test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r | | test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r | | test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r | | test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r | -| test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r | -| test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r | -| test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r | -| test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:61:5:61:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:61:5:61:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:61:5:61:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:61:5:61:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:62:5:62:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:62:5:62:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:62:5:62:5 | r | -| test.c:60:13:60:16 | call to rand | test.c:62:5:62:5 | r | -| test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r | -| test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r | -| test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r | -| test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r | | test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r | | test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r | | test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r | @@ -67,34 +47,11 @@ nodes | test.c:35:5:35:5 | r | semmle.label | r | | test.c:35:5:35:5 | r | semmle.label | r | | test.c:35:5:35:5 | r | semmle.label | r | -| test.c:39:13:39:21 | ... % ... | semmle.label | ... % ... | -| test.c:39:13:39:21 | ... % ... | semmle.label | ... % ... | -| test.c:40:5:40:5 | r | semmle.label | r | -| test.c:40:5:40:5 | r | semmle.label | r | -| test.c:40:5:40:5 | r | semmle.label | r | | test.c:44:13:44:16 | call to rand | semmle.label | call to rand | | test.c:44:13:44:16 | call to rand | semmle.label | call to rand | | test.c:45:5:45:5 | r | semmle.label | r | | test.c:45:5:45:5 | r | semmle.label | r | | test.c:45:5:45:5 | r | semmle.label | r | -| test.c:54:13:54:16 | call to rand | semmle.label | call to rand | -| test.c:54:13:54:16 | call to rand | semmle.label | call to rand | -| test.c:56:5:56:5 | r | semmle.label | r | -| test.c:56:5:56:5 | r | semmle.label | r | -| test.c:56:5:56:5 | r | semmle.label | r | -| test.c:60:13:60:16 | call to rand | semmle.label | call to rand | -| test.c:60:13:60:16 | call to rand | semmle.label | call to rand | -| test.c:61:5:61:5 | r | semmle.label | r | -| test.c:61:5:61:5 | r | semmle.label | r | -| test.c:61:5:61:5 | r | semmle.label | r | -| test.c:62:5:62:5 | r | semmle.label | r | -| test.c:62:5:62:5 | r | semmle.label | r | -| test.c:62:5:62:5 | r | semmle.label | r | -| test.c:66:13:66:16 | call to rand | semmle.label | call to rand | -| test.c:66:13:66:16 | call to rand | semmle.label | call to rand | -| test.c:67:5:67:5 | r | semmle.label | r | -| test.c:67:5:67:5 | r | semmle.label | r | -| test.c:67:5:67:5 | r | semmle.label | r | | test.c:75:13:75:19 | ... ^ ... | semmle.label | ... ^ ... | | test.c:75:13:75:19 | ... ^ ... | semmle.label | ... ^ ... | | test.c:77:9:77:9 | r | semmle.label | r | @@ -133,10 +90,7 @@ nodes #select | test.c:21:17:21:17 | r | test.c:18:13:18:16 | call to rand | test.c:21:17:21:17 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:18:13:18:16 | call to rand | Uncontrolled value | | test.c:35:5:35:5 | r | test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:34:13:34:18 | call to rand | Uncontrolled value | -| test.c:40:5:40:5 | r | test.c:39:13:39:21 | ... % ... | test.c:40:5:40:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:39:13:39:21 | ... % ... | Uncontrolled value | | test.c:45:5:45:5 | r | test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:44:13:44:16 | call to rand | Uncontrolled value | -| test.c:56:5:56:5 | r | test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:54:13:54:16 | call to rand | Uncontrolled value | -| test.c:67:5:67:5 | r | test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:66:13:66:16 | call to rand | Uncontrolled value | | test.c:77:9:77:9 | r | test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:75:13:75:19 | ... ^ ... | Uncontrolled value | | test.c:100:5:100:5 | r | test.c:99:14:99:19 | call to rand | test.c:100:5:100:5 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:99:14:99:19 | call to rand | Uncontrolled value | | test.cpp:25:7:25:7 | r | test.cpp:8:9:8:12 | call to rand | test.cpp:25:7:25:7 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:8:9:8:12 | call to rand | Uncontrolled value | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/test.c b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/test.c index 2b67b499a3c..61f39a8e851 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/test.c +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/test.c @@ -37,7 +37,7 @@ void randomTester() { { int r = RANDN(100); - r += 100; // GOOD: The return from RANDN is bounded [FALSE POSITIVE] + r += 100; // GOOD: The return from RANDN is bounded } { @@ -53,7 +53,7 @@ void randomTester() { { int r = rand(); r = r / 10; - r += 100; // GOOD [FALSE POSITIVE] + r += 100; // GOOD } { @@ -64,7 +64,7 @@ void randomTester() { { int r = rand() & 0xFF; - r += 100; // GOOD [FALSE POSITIVE] + r += 100; // GOOD } { diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/test.cpp index d3dc3352a29..78a285fffaf 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/uncontrolled/test.cpp @@ -37,3 +37,14 @@ void randomTester2() r = r + 100; // BAD } } + +int rand(int min, int max); +unsigned rand(int max); + +void test_with_bounded_randomness() { + int r = rand(0, 10); + r++; // GOOD + + unsigned unsigned_r = rand(10); + unsigned_r++; // GOOD +} \ No newline at end of file diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected index 2d28795d126..85938f15499 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/UnsignedDifferenceExpressionComparedZero.expected @@ -1,10 +1,15 @@ | test.cpp:6:5:6:13 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:10:8:10:24 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:15:9:15:25 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:32:12:32:20 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:39:12:39:20 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:47:5:47:13 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:55:5:55:13 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:62:5:62:13 | ... > ... | Unsigned subtraction can never be negative. | -| test.cpp:69:5:69:13 | ... > ... | Unsigned subtraction can never be negative. | | test.cpp:75:8:75:16 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:101:6:101:14 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:128:6:128:14 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:137:6:137:14 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:146:7:146:15 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:152:7:152:15 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:182:6:182:14 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:208:6:208:14 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:252:10:252:18 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:266:10:266:24 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:276:11:276:19 | ... > ... | Unsigned subtraction can never be negative. | +| test.cpp:288:10:288:18 | ... > ... | Unsigned subtraction can never be negative. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp index 11de69e8c62..1a6aaa618e5 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero/test.cpp @@ -12,7 +12,7 @@ void test(unsigned x, unsigned y, bool unknown) { } if(total <= limit) { - while(limit - total > 0) { // GOOD [FALSE POSITIVE] + while(limit - total > 0) { // GOOD total += getAnInt(); if(total > limit) break; } @@ -29,14 +29,14 @@ void test(unsigned x, unsigned y, bool unknown) { } else { y = x; } - bool b1 = x - y > 0; // GOOD [FALSE POSITIVE] + bool b1 = x - y > 0; // GOOD x = getAnInt(); y = getAnInt(); if(y > x) { y = x - 1; } - bool b2 = x - y > 0; // GOOD [FALSE POSITIVE] + bool b2 = x - y > 0; // GOOD int N = getAnInt(); y = x; @@ -44,7 +44,7 @@ void test(unsigned x, unsigned y, bool unknown) { if(unknown) { y--; } } - if(x - y > 0) { } // GOOD [FALSE POSITIVE] + if(x - y > 0) { } // GOOD x = y; while(cond()) { @@ -52,7 +52,7 @@ void test(unsigned x, unsigned y, bool unknown) { y--; } - if(x - y > 0) { } // GOOD [FALSE POSITIVE] + if(x - y > 0) { } // GOOD y = 0; for(int i = 0; i < x; ++i) { @@ -66,7 +66,7 @@ void test(unsigned x, unsigned y, bool unknown) { if(unknown) { x++; } } - if(x - y > 0) { } // GOOD [FALSE POSITIVE] + if(x - y > 0) { } // GOOD int n = getAnInt(); if (n > x - y) { n = x - y; } @@ -74,4 +74,227 @@ void test(unsigned x, unsigned y, bool unknown) { y += n; // NOTE: `n` is at most `x - y` at this point. if (x - y > 0) {} // GOOD [FALSE POSITIVE] } -} \ No newline at end of file +} + +void test2() { + unsigned int a = getAnInt(); + unsigned int b = a; + + if (a - b > 0) { // GOOD (as a = b) + // ... + } +} + +void test3() { + unsigned int a = getAnInt(); + unsigned int b = a - 1; + + if (a - b > 0) { // GOOD (as a >= b) + // ... + } +} + +void test4() { + unsigned int a = getAnInt(); + unsigned int b = a + 1; + + if (a - b > 0) { // BAD + // ... + } +} + +void test5() { + unsigned int b = getAnInt(); + unsigned int a = b; + + if (a - b > 0) { // GOOD (as a = b) + // ... + } +} + +void test6() { + unsigned int b = getAnInt(); + unsigned int a = b + 1; + + if (a - b > 0) { // GOOD (as a >= b) + // ... + } +} + +void test7() { + unsigned int b = getAnInt(); + unsigned int a = b - 1; + + if (a - b > 0) { // BAD + // ... + } +} + +void test8() { + unsigned int a = getAnInt(); + unsigned int b = getAnInt(); + + if (a - b > 0) { // BAD + // ... + } + + if (a >= b) { // GOOD + if (a - b > 0) { // GOOD (as a >= b) + // ... + } + } else { + if (a - b > 0) { // BAD + // ... + } + } + + if (b >= a) { // GOOD + if (a - b > 0) { // BAD + // ... + } + } else { + if (a - b > 0) { // GOOD (as a > b) + // ... + } + } + + while (a >= b) { // GOOD + if (a - b > 0) { // GOOD (as a >= b) + // ... + } + } + + if (a < b) return; + + if (a - b > 0) { // GOOD (as a >= b) + // ... + } +} + +void test9() { + unsigned int a = getAnInt(); + unsigned int b = getAnInt(); + + if (a < b) { + b = 0; + } + + if (a - b > 0) { // GOOD (as a >= b) [FALSE POSITIVE] + // ... + } +} + +void test10() { + unsigned int a = getAnInt(); + unsigned int b = getAnInt(); + + if (a < b) { + a = b; + } + + if (a - b > 0) { // GOOD (as a >= b) + // ... + } +} + +void test11() { + unsigned int a = getAnInt(); + unsigned int b = getAnInt(); + + if (a < b) return; + + b = getAnInt(); + + if (a - b > 0) { // BAD + // ... + } +} + +void test12() { + unsigned int a = getAnInt(); + unsigned int b = getAnInt(); + unsigned int c; + + if ((b <= c) && (c <= a)) { + if (a - b > 0) { // GOOD (as b <= a) + // ... + } + } + + if (b <= c) { + if (c <= a) { + if (a - b > 0) { // GOOD (as b <= a) + // ... + } + } + } +} + +int test13() { + unsigned int a = getAnInt(); + unsigned int b = getAnInt(); + + if (b != 0) { + return 0; + } // b = 0 + + return (a - b > 0); // GOOD (as b = 0) +} + +int test14() { + unsigned int a = getAnInt(); + unsigned int b = getAnInt(); + + if (!b) { + return 0; + } // b != 0 + + return (a - b > 0); // BAD +} + +struct Numbers +{ + unsigned int a, b; +}; + +int test15(Numbers *n) { + + if (!n) { + return 0; + } + + return (n->a - n->b > 0); // BAD +} + +int test16() { + unsigned int a = getAnInt(); + unsigned int b = getAnInt(); + + if (!b) { + return 0; + } else { + return (a - b > 0); // BAD + } +} + +int test17() { + unsigned int a = getAnInt(); + unsigned int b = getAnInt(); + + if (b == 0) { + return 0; + } // b != 0 + + return (a - b > 0); // BAD +} + +int test18() { + unsigned int a = getAnInt(); + unsigned int b = getAnInt(); + + if (b) { + return 0; + } // b == 0 + + return (a - b > 0); // GOOD (as b = 0) +} diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected new file mode 100644 index 00000000000..d27d5c4bbca --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.expected @@ -0,0 +1,25 @@ +| test2.cpp:49:4:49:24 | call to my_des_implementation | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test2.cpp:49:4:49:24 | call to my_des_implementation | call to my_des_implementation | +| test2.cpp:49:4:49:24 | call to my_des_implementation | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test2.cpp:62:33:62:40 | ALGO_DES | invocation of macro ALGO_DES | +| test2.cpp:49:4:49:24 | call to my_des_implementation | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test2.cpp:124:4:124:24 | call to my_des_implementation | call to my_des_implementation | +| test2.cpp:49:4:49:24 | call to my_des_implementation | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test2.cpp:144:27:144:29 | DES | access of enum constant DES | +| test2.cpp:49:4:49:24 | call to my_des_implementation | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test2.cpp:172:28:172:35 | ALGO_DES | invocation of macro ALGO_DES | +| test2.cpp:49:4:49:24 | call to my_des_implementation | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test2.cpp:175:28:175:34 | USE_DES | access of enum constant USE_DES | +| test2.cpp:49:4:49:24 | call to my_des_implementation | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test2.cpp:182:38:182:45 | ALGO_DES | invocation of macro ALGO_DES | +| test2.cpp:49:4:49:24 | call to my_des_implementation | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test2.cpp:185:38:185:44 | USE_DES | access of enum constant USE_DES | +| test2.cpp:49:4:49:24 | call to my_des_implementation | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test2.cpp:238:2:238:20 | call to encrypt | call to encrypt | +| test2.cpp:49:4:49:24 | call to my_des_implementation | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test2.cpp:245:5:245:11 | call to encrypt | call to encrypt | +| test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | invocation of macro ENCRYPT_WITH_DES | +| test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test.cpp:39:2:39:31 | ENCRYPT_WITH_RC2(data,amount) | invocation of macro ENCRYPT_WITH_RC2 | +| test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test.cpp:41:2:41:32 | ENCRYPT_WITH_3DES(data,amount) | invocation of macro ENCRYPT_WITH_3DES | +| test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test.cpp:42:2:42:38 | ENCRYPT_WITH_TRIPLE_DES(data,amount) | invocation of macro ENCRYPT_WITH_TRIPLE_DES | +| test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test.cpp:51:2:51:32 | DES_DO_ENCRYPTION(data,amount) | invocation of macro DES_DO_ENCRYPTION | +| test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test.cpp:52:2:52:31 | RUN_DES_ENCODING(data,amount) | invocation of macro RUN_DES_ENCODING | +| test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test.cpp:53:2:53:25 | DES_ENCODE(data,amount) | invocation of macro DES_ENCODE | +| test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test.cpp:54:2:54:26 | DES_SET_KEY(data,amount) | invocation of macro DES_SET_KEY | +| test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test.cpp:88:2:88:11 | call to encryptDES | call to encryptDES | +| test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test.cpp:89:2:89:11 | call to encryptRC2 | call to encryptRC2 | +| test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test.cpp:91:2:91:12 | call to encrypt3DES | call to encrypt3DES | +| test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test.cpp:92:2:92:17 | call to encryptTripleDES | call to encryptTripleDES | +| test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test.cpp:101:2:101:15 | call to do_des_encrypt | call to do_des_encrypt | +| test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test.cpp:102:2:102:12 | call to DES_Set_Key | call to DES_Set_Key | +| test.cpp:38:2:38:31 | ENCRYPT_WITH_DES(data,amount) | This file makes use of a broken or weak cryptographic algorithm (specified by $@). | test.cpp:121:2:121:24 | INIT_ENCRYPT_WITH_DES() | invocation of macro INIT_ENCRYPT_WITH_DES | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.qlref new file mode 100644 index 00000000000..8424dee1a9b --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/BrokenCryptoAlgorithm.qlref @@ -0,0 +1 @@ +Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql \ No newline at end of file diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp new file mode 100644 index 00000000000..8af9868f1ee --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test.cpp @@ -0,0 +1,125 @@ + +typedef unsigned long size_t; + +// --- simple encryption macro invocations --- + +void my_implementation1(void *data, size_t amount); +void my_implementation2(void *data, size_t amount); +void my_implementation3(void *data, size_t amount); +void my_implementation4(void *data, size_t amount); +void my_implementation5(void *data, size_t amount); +void my_implementation6(const char *str); + +#define ENCRYPT_WITH_DES(data, amount) my_implementation1(data, amount) +#define ENCRYPT_WITH_RC2(data, amount) my_implementation2(data, amount) +#define ENCRYPT_WITH_AES(data, amount) my_implementation3(data, amount) +#define ENCRYPT_WITH_3DES(data, amount) my_implementation4(data, amount) +#define ENCRYPT_WITH_TRIPLE_DES(data, amount) my_implementation4(data, amount) +#define ENCRYPT_WITH_RC20(data, amount) my_implementation5(data, amount) +#define ENCRYPT_WITH_DES_REMOVED(data, amount) + +#define DESENCRYPT(data, amount) my_implementation1(data, amount) +#define RC2ENCRYPT(data, amount) my_implementation2(data, amount) +#define AESENCRYPT(data, amount) my_implementation3(data, amount) +#define DES3ENCRYPT(data, amount) my_implementation4(data, amount) + +#define DES_DO_ENCRYPTION(data, amount) my_implementation1(data, amount) +#define RUN_DES_ENCODING(data, amount) my_implementation1(data, amount) +#define DES_ENCODE(data, amount) my_implementation1(data, amount) +#define DES_SET_KEY(data, amount) my_implementation1(data, amount) + +#define DES(str) my_implementation6(str) +#define DESMOND(str) my_implementation6(str) +#define ANODES(str) my_implementation6(str) +#define SORT_ORDER_DES (1) + +void test_macros(void *data, size_t amount, const char *str) +{ + ENCRYPT_WITH_DES(data, amount); // BAD + ENCRYPT_WITH_RC2(data, amount); // BAD + ENCRYPT_WITH_AES(data, amount); // GOOD (good algorithm) + ENCRYPT_WITH_3DES(data, amount); // BAD + ENCRYPT_WITH_TRIPLE_DES(data, amount); // BAD + ENCRYPT_WITH_RC20(data, amount); // GOOD (if there ever is an RC20 algorithm, we have no reason to believe it's weak) + ENCRYPT_WITH_DES_REMOVED(data, amount); // GOOD (implementation has been deleted) + + DESENCRYPT(data, amount); // BAD [NOT DETECTED] + RC2ENCRYPT(data, amount); // BAD [NOT DETECTED] + AESENCRYPT(data, amount); // GOOD (good algorithm) + DES3ENCRYPT(data, amount); // BAD [NOT DETECTED] + + DES_DO_ENCRYPTION(data, amount); // BAD + RUN_DES_ENCODING(data, amount); // BAD + DES_ENCODE(data, amount); // BAD + DES_SET_KEY(data, amount); // BAD + + DES(str); // GOOD (probably nothing to do with encryption) + DESMOND(str); // GOOD (probably nothing to do with encryption) + ANODES(str); // GOOD (probably nothing to do with encryption) + int ord = SORT_ORDER_DES; // GOOD (probably nothing to do with encryption) +} + +// --- simple encryption function calls --- + +void encryptDES(void *data, size_t amount); +void encryptRC2(void *data, size_t amount); +void encryptAES(void *data, size_t amount); +void encrypt3DES(void *data, size_t amount); +void encryptTripleDES(void *data, size_t amount); + +void DESEncrypt(void *data, size_t amount); +void RC2Encrypt(void *data, size_t amount); +void AESEncrypt(void *data, size_t amount); +void DES3Encrypt(void *data, size_t amount); + +void DoDESEncryption(void *data, size_t amount); +void encryptDes(void *data, size_t amount); +void do_des_encrypt(void *data, size_t amount); +void DES_Set_Key(const char *key); +void DESSetKey(const char *key); + +int Des(); +void Desmond(const char *str); +void Anodes(int i); +void ConDes(); + +void test_functions(void *data, size_t amount, const char *str) +{ + encryptDES(data, amount); // BAD + encryptRC2(data, amount); // BAD + encryptAES(data, amount); // GOOD (good algorithm) + encrypt3DES(data, amount); // BAD + encryptTripleDES(data, amount); // BAD + + DESEncrypt(data, amount); // BAD [NOT DETECTED] + RC2Encrypt(data, amount); // BAD [NOT DETECTED] + AESEncrypt(data, amount); // GOOD (good algorithm) + DES3Encrypt(data, amount); // BAD [NOT DETECTED] + + DoDESEncryption(data, amount); // BAD [NOT DETECTED] + encryptDes(data, amount); // BAD [NOT DETECTED] + do_des_encrypt(data, amount); // BAD + DES_Set_Key(str); // BAD + DESSetKey(str); // BAD [NOT DETECTED] + + Des(); // GOOD (probably nothing to do with encryption) + Desmond(str); // GOOD (probably nothing to do with encryption) + Anodes(1); // GOOD (probably nothing to do with encryption) + ConDes(); // GOOD (probably nothing to do with encryption) +} + +// --- macros for functions with no arguments --- + +void my_implementation7(); +void my_implementation8(); + +#define INIT_ENCRYPT_WITH_DES() my_implementation7() +#define INIT_ENCRYPT_WITH_AES() my_implementation8() + +void test_macros2() +{ + INIT_ENCRYPT_WITH_DES(); // BAD + INIT_ENCRYPT_WITH_AES(); // GOOD (good algorithm) + + // ... +} \ No newline at end of file diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp new file mode 100644 index 00000000000..66d6f283ba3 --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-327/test2.cpp @@ -0,0 +1,262 @@ + +typedef unsigned long size_t; + +int strcmp(const char *s1, const char *s2); +void abort(void); + +struct keytype +{ + char data[16]; +}; + +void my_des_implementation(char *data, size_t amount, keytype key); +void my_rc2_implementation(char *data, size_t amount, keytype key); +void my_aes_implementation(char *data, size_t amount, keytype key); +void my_3des_implementation(char *data, size_t amount, keytype key); + +typedef void (*implementation_fn_ptr)(char *data, size_t amount, keytype key); + +// --- more involved C-style example --- + +#define ALGO_DES (1) +#define ALGO_AES (2) + +int all_algos[] = { + ALGO_DES, + ALGO_AES +}; + +void encrypt_good(char *data, size_t amount, keytype key, int algo) +{ + switch (algo) + { + case ALGO_DES: + abort(); + + case ALGO_AES: + { + my_aes_implementation(data, amount, key); // GOOD + } break; + } +}; + +void encrypt_bad(char *data, size_t amount, keytype key, int algo) +{ + switch (algo) + { + case ALGO_DES: + { + my_des_implementation(data, amount, key); // BAD + } break; + + case ALGO_AES: + { + my_aes_implementation(data, amount, key); // GOOD + } break; + } +}; + +void do_encrypts(char *data, size_t amount, keytype key) +{ + encrypt_good(data, amount, key, ALGO_AES); // GOOD + encrypt_bad(data, amount, key, ALGO_DES); // BAD +} + +// --- more involved CPP-style example --- + +enum algorithm +{ + DES, + AES +}; + +algorithm all_algorithms[] = { + DES, + AES +}; + +class MyGoodEncryptor +{ +public: + MyGoodEncryptor(keytype _key, algorithm _algo) : key(_key), algo(_algo) {}; + + void encrypt(char *data, size_t amount); + +private: + keytype key; + algorithm algo; +}; + +void MyGoodEncryptor :: encrypt(char *data, size_t amount) +{ + switch (algo) + { + case DES: + { + throw "DES is not a good choice of encryption algorithm!"; + } break; + + case AES: + { + my_aes_implementation(data, amount, key); // GOOD + } break; + } +} + +class MyBadEncryptor +{ +public: + MyBadEncryptor(keytype _key, algorithm _algo) : key(_key), algo(_algo) {}; + + void encrypt(char *data, size_t amount); + +private: + keytype key; + algorithm algo; +}; + +void MyBadEncryptor :: encrypt(char *data, size_t amount) +{ + switch (algo) + { + case DES: + { + my_des_implementation(data, amount, key); // BAD + } break; + + case AES: + { + my_aes_implementation(data, amount, key); // GOOD + } break; + } +} + +void do_class_encrypts(char *data, size_t amount, keytype key) +{ + { + MyGoodEncryptor mge(key, AES); // GOOD + + mge.encrypt(data, amount); + + } + + { + MyBadEncryptor mbe(key, DES); // BAD + + mbe.encrypt(data, amount); + } +} + +// --- unseen implementation --- + +enum use_algorithm +{ + USE_DES, + USE_AES +}; + +void set_encryption_algorithm1(int algorithm); +void set_encryption_algorithm2(use_algorithm algorithm); +void set_encryption_algorithm3(const char *algorithm_str); + +void encryption_with1(char *data, size_t amount, keytype key, int algorithm); +void encryption_with2(char *data, size_t amount, keytype key, use_algorithm algorithm); +void encryption_with3(char *data, size_t amount, keytype key, const char *algorithm_str); + +int get_algorithm1(); +use_algorithm get_algorithm2(); +const char *get_algorithm3(); + +void do_unseen_encrypts(char *data, size_t amount, keytype key) +{ + set_encryption_algorithm1(ALGO_DES); // BAD + set_encryption_algorithm1(ALGO_AES); // GOOD + + set_encryption_algorithm2(USE_DES); // BAD + set_encryption_algorithm2(USE_AES); // GOOD + + set_encryption_algorithm3("DES"); // BAD [NOT DETECTED] + set_encryption_algorithm3("AES"); // GOOD + set_encryption_algorithm3("AES-256"); // GOOD + + encryption_with1(data, amount, key, ALGO_DES); // BAD + encryption_with1(data, amount, key, ALGO_AES); // GOOD + + encryption_with2(data, amount, key, USE_DES); // BAD + encryption_with2(data, amount, key, USE_AES); // GOOD + + encryption_with3(data, amount, key, "DES"); // BAD [NOT DETECTED] + encryption_with3(data, amount, key, "AES"); // GOOD + encryption_with3(data, amount, key, "AES-256"); // GOOD + + if (get_algorithm1() == ALGO_DES) // GOOD + { + throw "DES is not a good choice of encryption algorithm!"; + } + if (get_algorithm2() == USE_DES) // GOOD + { + throw "DES is not a good choice of encryption algorithm!"; + } + if (strcmp(get_algorithm3(), "DES") == 0) // GOOD + { + throw "DES is not a good choice of encryption algorithm!"; + } +} + +// --- classes --- + +class desEncrypt +{ +public: + static void encrypt(const char *data); + static void doSomethingElse(); +}; + +class aes256Encrypt +{ +public: + static void encrypt(const char *data); + static void doSomethingElse(); +}; + +class desCipher +{ +public: + void encrypt(const char *data); + void doSomethingElse(); +}; + +class aesCipher +{ +public: + void encrypt(const char *data); + void doSomethingElse(); +}; + +void do_classes(const char *data) +{ + desEncrypt::encrypt(data); // BAD + aes256Encrypt::encrypt(data); // GOOD + desEncrypt::doSomethingElse(); // GOOD + aes256Encrypt::doSomethingElse(); // GOOD + + desCipher dc; + aesCipher ac; + dc.encrypt(data); // BAD + ac.encrypt(data); // GOOD + dc.doSomethingElse(); // GOOD + ac.doSomethingElse(); // GOOD +} + +// --- function pointer --- + +void do_fn_ptr(char *data, size_t amount, keytype key) +{ + implementation_fn_ptr impl; + + impl = &my_des_implementation; // BAD [NOT DETECTED] + impl(data, amount, key); + + impl = &my_aes_implementation; // GOOD + impl(data, amount, key); +} diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.expected new file mode 100644 index 00000000000..4720e02b381 --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.expected @@ -0,0 +1,18 @@ +| test.cpp:21:9:21:15 | new | This allocation cannot return null. $@ is unnecessary. | test.cpp:21:9:21:15 | new | This check | +| test.cpp:29:13:29:24 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:30:7:30:13 | ... == ... | This check | +| test.cpp:33:13:33:24 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:34:8:34:9 | p2 | This check | +| test.cpp:37:13:37:24 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:38:7:38:16 | ... == ... | This check | +| test.cpp:41:13:41:24 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:42:7:42:19 | ... == ... | This check | +| test.cpp:45:13:45:24 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:46:7:46:8 | p5 | This check | +| test.cpp:49:8:49:19 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:50:7:50:13 | ... == ... | This check | +| test.cpp:53:8:53:19 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:54:8:54:9 | p7 | This check | +| test.cpp:58:8:58:19 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:59:7:59:16 | ... == ... | This check | +| test.cpp:63:8:63:19 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:64:7:64:19 | ... != ... | This check | +| test.cpp:69:9:69:20 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:70:7:70:14 | ... != ... | This check | +| test.cpp:75:11:75:22 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:76:13:76:15 | p11 | This check | +| test.cpp:92:5:92:31 | new[] | This allocation cannot throw. $@ is unnecessary. | test.cpp:97:36:98:3 | { ... } | This catch block | +| test.cpp:93:15:93:41 | new[] | This allocation cannot throw. $@ is unnecessary. | test.cpp:97:36:98:3 | { ... } | This catch block | +| test.cpp:96:10:96:36 | new[] | This allocation cannot throw. $@ is unnecessary. | test.cpp:97:36:98:3 | { ... } | This catch block | +| test.cpp:151:9:151:24 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:152:15:152:18 | { ... } | This catch block | +| test.cpp:199:15:199:35 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:201:16:201:19 | { ... } | This catch block | +| test.cpp:212:14:212:34 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:213:34:213:36 | { ... } | This catch block | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.qlref new file mode 100644 index 00000000000..fe4bb214bb4 --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.qlref @@ -0,0 +1 @@ +Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-570/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-570/test.cpp new file mode 100644 index 00000000000..5b3425029e9 --- /dev/null +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-570/test.cpp @@ -0,0 +1,227 @@ +#define NULL ((void *)0) + +namespace std { +struct nothrow_t {}; +typedef unsigned long size_t; + +class exception {}; +class bad_alloc : public exception {}; + +extern const std::nothrow_t nothrow; +} // namespace std + +using namespace std; + +void *operator new(std::size_t); +void *operator new[](std::size_t); +void *operator new(std::size_t, const std::nothrow_t &) noexcept; +void *operator new[](std::size_t, const std::nothrow_t &) noexcept; + +void bad_new_in_condition() { + if (!(new int)) { // BAD + return; + } +} + +void foo(int**); + +void bad_new_missing_exception_handling() { + int *p1 = new int[100]; // BAD + if (p1 == 0) + return; + + int *p2 = new int[100]; // BAD + if (!p2) + return; + + int *p3 = new int[100]; // BAD + if (p3 == NULL) + return; + + int *p4 = new int[100]; // BAD + if (p4 == nullptr) + return; + + int *p5 = new int[100]; // BAD + if (p5) {} else return; + + int *p6; + p6 = new int[100]; // BAD + if (p6 == 0) return; + + int *p7; + p7 = new int[100]; // BAD + if (!p7) + return; + + int *p8; + p8 = new int[100]; // BAD + if (p8 == NULL) + return; + + int *p9; + p9 = new int[100]; // BAD + if (p9 != nullptr) { + } else + return; + + int *p10; + p10 = new int[100]; // BAD + if (p10 != 0) { + } + + int *p11; + do { + p11 = new int[100]; // BAD + } while (!p11); + + int* p12 = new int[100]; + foo(&p12); + if(p12) {} else return; // GOOD: p12 is probably modified in foo, so it's + // not the return value of the new that's checked. + + int* p13 = new int[100]; + foo(&p13); + if(!p13) { + return; + } else { }; // GOOD: same as above. +} + +void bad_new_nothrow_in_exception_body() { + try { + new (std::nothrow) int[100]; // BAD + int *p1 = new (std::nothrow) int[100]; // BAD + + int *p2; + p2 = new (std::nothrow) int[100]; // BAD + } catch (const std::bad_alloc &) { + } +} + +void good_new_has_exception_handling() { + try { + int *p1 = new int[100]; // GOOD + } catch (...) { + } +} + +void good_new_handles_nullptr() { + int *p1 = new (std::nothrow) int[100]; // GOOD + if (p1 == nullptr) + return; + + int *p2; + p2 = new (std::nothrow) int[100]; // GOOD + if (p2 == nullptr) + return; + + int *p3; + p3 = new (std::nothrow) int[100]; // GOOD + if (p3 != nullptr) { + } + + int *p4; + p4 = new (std::nothrow) int[100]; // GOOD + if (p4) { + } else + return; + + int *p5; + p5 = new (std::nothrow) int[100]; // GOOD + if (p5 != nullptr) { + } else + return; + + if (new (std::nothrow) int[100] == nullptr) + return; // GOOD +} + +void* operator new(std::size_t count, void*) noexcept; +void* operator new[](std::size_t count, void*) noexcept; + +struct Foo { + Foo() noexcept; + Foo(int); + + operator bool(); +}; + +void bad_placement_new_with_exception_handling() { + char buffer[1024]; + try { new (buffer) Foo; } // BAD + catch (...) { } +} + +void good_placement_new_with_exception_handling() { + char buffer[1024]; + try { new (buffer) Foo(42); } // GOOD: Foo constructor might throw + catch (...) { } +} + +int unknown_value_without_exceptions() noexcept; + +void may_throw() { + if(unknown_value_without_exceptions()) { + throw "bad luck exception!"; + } +} + +void unknown_code_that_may_throw(int*); +void unknown_code_that_will_not_throw(int*) noexcept; + +void calls_throwing_code(int* p) { + if(unknown_value_without_exceptions()) unknown_code_that_may_throw(p); +} + +void calls_non_throwing(int* p) { + if (unknown_value_without_exceptions()) unknown_code_that_will_not_throw(p); +} + +void good_new_with_throwing_call() { + try { + int* p1 = new(std::nothrow) int; // GOOD + may_throw(); + } catch(...) { } + + try { + int* p2 = new(std::nothrow) int; // GOOD + Foo f(10); + } catch(...) { } + + try { + int* p3 = new(std::nothrow) int; // GOOD + calls_throwing_code(p3); + } catch(...) { } +} + +void bad_new_with_nonthrowing_call() { + try { + int* p1 = new(std::nothrow) int; // BAD + calls_non_throwing(p1); + } catch(...) { } + + try { + int* p2 = new(std::nothrow) int; // GOOD: boolean conversion constructor might throw + Foo f; + if(f) { } + } catch(...) { } +} + +void bad_new_catch_baseclass_of_bad_alloc() { + try { + int* p = new(std::nothrow) int; // BAD + } catch(const std::exception&) { } +} + +void good_new_catch_exception_in_assignment() { + int* p; + try { + p = new int; // GOOD + } catch(const std::bad_alloc&) { } +} + +void good_new_catch_exception_in_conversion() { + try { + long* p = (long*) new int; // GOOD + } catch(const std::bad_alloc&) { } +} \ No newline at end of file diff --git a/cpp/ql/test/query-tests/Summary/LinesOfCode.expected b/cpp/ql/test/query-tests/Summary/LinesOfCode.expected index a75c288c151..8628859feb7 100644 --- a/cpp/ql/test/query-tests/Summary/LinesOfCode.expected +++ b/cpp/ql/test/query-tests/Summary/LinesOfCode.expected @@ -1 +1 @@ -| 93 | +| 96 | diff --git a/cpp/ql/test/query-tests/Summary/LinesOfUserCode.expected b/cpp/ql/test/query-tests/Summary/LinesOfUserCode.expected new file mode 100644 index 00000000000..a75c288c151 --- /dev/null +++ b/cpp/ql/test/query-tests/Summary/LinesOfUserCode.expected @@ -0,0 +1 @@ +| 93 | diff --git a/cpp/ql/test/query-tests/Summary/LinesOfUserCode.qlref b/cpp/ql/test/query-tests/Summary/LinesOfUserCode.qlref new file mode 100644 index 00000000000..baaa947e6af --- /dev/null +++ b/cpp/ql/test/query-tests/Summary/LinesOfUserCode.qlref @@ -0,0 +1 @@ +Summary/LinesOfUserCode.ql diff --git a/cpp/ql/test/query-tests/Summary/generated-file.cpp b/cpp/ql/test/query-tests/Summary/generated-file.cpp new file mode 100644 index 00000000000..e15ecb227b9 --- /dev/null +++ b/cpp/ql/test/query-tests/Summary/generated-file.cpp @@ -0,0 +1,12 @@ +/** + * This file is generated by abc.xyz. Do not edit! + * + * (except that this isn't really a generated file, but the above is the typical sort of comment + * you see at the beginning of a true generated file). + */ + +int generated_function() { + // ... + + return 1; +} diff --git a/cpp/ql/test/query-tests/definitions/locationInfo.ql b/cpp/ql/test/query-tests/definitions/locationInfo.ql index 1f747b10027..7c8bf331d1c 100644 --- a/cpp/ql/test/query-tests/definitions/locationInfo.ql +++ b/cpp/ql/test/query-tests/definitions/locationInfo.ql @@ -11,7 +11,9 @@ class Link extends Top { * Gets the length of the longest line in file `f`. */ pragma[nomagic] -private int maxCols(File f) { result = max(Location l | l.getFile() = f | l.getEndColumn()) } +private int maxCols(File f) { + result = max(Location l | l.getFile() = f | l.getStartColumn().maximum(l.getEndColumn())) +} /** * Gets the location of an element that has a link-to-definition (in a similar manner to diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs index 7b9f2e78881..197edc2c162 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs @@ -415,7 +415,7 @@ namespace Semmle.Autobuild.CSharp.Tests actions.RunProcess["cmd.exe /C dotnet --info"] = 0; actions.RunProcess[@"cmd.exe /C dotnet clean C:\Project\test.csproj"] = 0; actions.RunProcess[@"cmd.exe /C dotnet restore C:\Project\test.csproj"] = 0; - actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto dotnet build --no-incremental C:\Project\test.csproj"] = 0; + actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project\test.csproj"] = 0; actions.FileExists["csharp.log"] = true; actions.FileExists[@"C:\Project\test.csproj"] = true; actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; @@ -439,9 +439,6 @@ namespace Semmle.Autobuild.CSharp.Tests [Fact] public void TestLinuxCSharpAutoBuilder() { - actions.RunProcess["dotnet --list-runtimes"] = 0; - actions.RunProcessOut["dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] -Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]"; actions.RunProcess["dotnet --info"] = 0; actions.RunProcess[@"dotnet clean C:\Project/test.csproj"] = 0; actions.RunProcess[@"dotnet restore C:\Project/test.csproj"] = 0; @@ -463,7 +460,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.LoadXml[@"C:\Project/test.csproj"] = xml; var autobuilder = CreateAutoBuilder(false); - TestAutobuilderScript(autobuilder, 0, 5); + TestAutobuilderScript(autobuilder, 0, 4); } [Fact] @@ -603,8 +600,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap [Fact] public void TestLinuxBuildCommand() { - actions.RunProcess["dotnet --list-runtimes"] = 1; - actions.RunProcessOut["dotnet --list-runtimes"] = ""; actions.RunProcess[@"C:\odasa/tools/odasa index --auto ""./build.sh --skip-tests"""] = 0; actions.FileExists["csharp.log"] = true; actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; @@ -615,7 +610,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap SkipVsWhere(); var autobuilder = CreateAutoBuilder(false, buildCommand: "./build.sh --skip-tests"); - TestAutobuilderScript(autobuilder, 0, 2); + TestAutobuilderScript(autobuilder, 0, 1); } [Fact] @@ -626,14 +621,12 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; actions.RunProcess[@"/bin/chmod u+x C:\Project/build/build.sh"] = 0; - actions.RunProcess["dotnet --list-runtimes"] = 1; - actions.RunProcessOut["dotnet --list-runtimes"] = ""; actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/build/build.sh"] = 0; actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto C:\Project/build/build.sh"] = @"C:\Project/build"; actions.FileExists["csharp.log"] = true; var autobuilder = CreateAutoBuilder(false); - TestAutobuilderScript(autobuilder, 0, 3); + TestAutobuilderScript(autobuilder, 0, 2); } [Fact] @@ -645,14 +638,12 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; actions.RunProcess[@"/bin/chmod u+x C:\Project/build.sh"] = 0; - actions.RunProcess["dotnet --list-runtimes"] = 1; - actions.RunProcessOut["dotnet --list-runtimes"] = ""; actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = 0; actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = @"C:\Project"; actions.FileExists["csharp.log"] = false; var autobuilder = CreateAutoBuilder(false); - TestAutobuilderScript(autobuilder, 1, 3); + TestAutobuilderScript(autobuilder, 1, 2); } [Fact] @@ -664,14 +655,12 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; actions.RunProcess[@"/bin/chmod u+x C:\Project/build.sh"] = 0; - actions.RunProcess["dotnet --list-runtimes"] = 1; - actions.RunProcessOut["dotnet --list-runtimes"] = ""; actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = 5; actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = @"C:\Project"; actions.FileExists["csharp.log"] = true; var autobuilder = CreateAutoBuilder(false); - TestAutobuilderScript(autobuilder, 1, 3); + TestAutobuilderScript(autobuilder, 1, 2); } [Fact] @@ -872,9 +861,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap [Fact] public void TestSkipNugetDotnet() { - actions.RunProcess["dotnet --list-runtimes"] = 0; - actions.RunProcessOut["dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] -Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]"; actions.RunProcess["dotnet --info"] = 0; actions.RunProcess[@"dotnet clean C:\Project/test.csproj"] = 0; actions.RunProcess[@"dotnet restore C:\Project/test.csproj"] = 0; @@ -896,7 +882,7 @@ Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.LoadXml[@"C:\Project/test.csproj"] = xml; var autobuilder = CreateAutoBuilder(false, dotnetArguments: "--no-restore"); // nugetRestore=false does not work for now. - TestAutobuilderScript(autobuilder, 0, 5); + TestAutobuilderScript(autobuilder, 0, 4); } [Fact] @@ -907,13 +893,10 @@ Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.RunProcess[@"chmod u+x dotnet-install.sh"] = 0; actions.RunProcess[@"./dotnet-install.sh --channel release --version 2.1.3 --install-dir C:\Project/.dotnet"] = 0; actions.RunProcess[@"rm dotnet-install.sh"] = 0; - actions.RunProcess[@"C:\Project/.dotnet/dotnet --list-runtimes"] = 0; - actions.RunProcessOut[@"C:\Project/.dotnet/dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] -Microsoft.NETCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]"; actions.RunProcess[@"C:\Project/.dotnet/dotnet --info"] = 0; actions.RunProcess[@"C:\Project/.dotnet/dotnet clean C:\Project/test.csproj"] = 0; actions.RunProcess[@"C:\Project/.dotnet/dotnet restore C:\Project/test.csproj"] = 0; - actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/.dotnet/dotnet build --no-incremental C:\Project/test.csproj"] = 0; + actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/.dotnet/dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project/test.csproj"] = 0; actions.FileExists["csharp.log"] = true; actions.FileExists["test.csproj"] = true; actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; @@ -933,7 +916,7 @@ Microsoft.NETCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.DownloadFiles.Add(("https://dot.net/v1/dotnet-install.sh", "dotnet-install.sh")); var autobuilder = CreateAutoBuilder(false, dotnetVersion: "2.1.3"); - TestAutobuilderScript(autobuilder, 0, 9); + TestAutobuilderScript(autobuilder, 0, 8); } [Fact] @@ -945,11 +928,6 @@ Microsoft.NETCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.RunProcess[@"chmod u+x dotnet-install.sh"] = 0; actions.RunProcess[@"./dotnet-install.sh --channel release --version 2.1.3 --install-dir C:\Project/.dotnet"] = 0; actions.RunProcess[@"rm dotnet-install.sh"] = 0; - actions.RunProcess[@"C:\Project/.dotnet/dotnet --list-runtimes"] = 0; - actions.RunProcessOut[@"C:\Project/.dotnet/dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] -Microsoft.AspNetCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] -Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] -Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]"; actions.RunProcess[@"C:\Project/.dotnet/dotnet --info"] = 0; actions.RunProcess[@"C:\Project/.dotnet/dotnet clean C:\Project/test.csproj"] = 0; actions.RunProcess[@"C:\Project/.dotnet/dotnet restore C:\Project/test.csproj"] = 0; @@ -973,20 +951,18 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.DownloadFiles.Add(("https://dot.net/v1/dotnet-install.sh", "dotnet-install.sh")); var autobuilder = CreateAutoBuilder(false, dotnetVersion: "2.1.3"); - TestAutobuilderScript(autobuilder, 0, 9); + TestAutobuilderScript(autobuilder, 0, 8); } - [Fact] - public void TestDotnetVersionWindows() + private void TestDotnetVersionWindows(Action action, int commandsRun) { actions.RunProcess["cmd.exe /C dotnet --list-sdks"] = 0; actions.RunProcessOut["cmd.exe /C dotnet --list-sdks"] = "2.1.3 [C:\\Program Files\\dotnet\\sdks]\n2.1.4 [C:\\Program Files\\dotnet\\sdks]"; - actions.RunProcess[@"cmd.exe /C powershell -NoProfile -ExecutionPolicy unrestricted -file C:\Project\install-dotnet.ps1 -Version 2.1.3 -InstallDir C:\Project\.dotnet"] = 0; - actions.RunProcess[@"cmd.exe /C del C:\Project\install-dotnet.ps1"] = 0; + action(); actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet --info"] = 0; actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet clean C:\Project\test.csproj"] = 0; actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet restore C:\Project\test.csproj"] = 0; - actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\.dotnet\dotnet build --no-incremental C:\Project\test.csproj"] = 0; + actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\.dotnet\dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project\test.csproj"] = 0; actions.FileExists["csharp.log"] = true; actions.FileExists[@"C:\Project\test.csproj"] = true; actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; @@ -1005,7 +981,28 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap actions.LoadXml[@"C:\Project\test.csproj"] = xml; var autobuilder = CreateAutoBuilder(true, dotnetVersion: "2.1.3"); - TestAutobuilderScript(autobuilder, 0, 7); + TestAutobuilderScript(autobuilder, 0, commandsRun); + } + + [Fact] + public void TestDotnetVersionWindowsWithPwsh() + { + TestDotnetVersionWindows(() => + { + actions.RunProcess[@"cmd.exe /C pwsh -NoProfile -ExecutionPolicy unrestricted -Command ""[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Version 2.1.3 -InstallDir C:\Project\.dotnet"""] = 0; + }, + 6); + } + + [Fact] + public void TestDotnetVersionWindowsWithoutPwsh() + { + TestDotnetVersionWindows(() => + { + actions.RunProcess[@"cmd.exe /C pwsh -NoProfile -ExecutionPolicy unrestricted -Command ""[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Version 2.1.3 -InstallDir C:\Project\.dotnet"""] = 1; + actions.RunProcess[@"cmd.exe /C powershell -NoProfile -ExecutionPolicy unrestricted -Command ""[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Version 2.1.3 -InstallDir C:\Project\.dotnet"""] = 0; + }, + 7); } [Fact] diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs index 8787406c104..3d7a1168e30 100644 --- a/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs @@ -36,7 +36,7 @@ namespace Semmle.Autobuild.CSharp builder.Log(Severity.Info, "Attempting to build using .NET Core"); } - return WithDotNet(builder, (dotNetPath, environment, compatibleClr) => + return WithDotNet(builder, (dotNetPath, environment) => { var ret = GetInfoCommand(builder.Actions, dotNetPath, environment); foreach (var projectOrSolution in builder.ProjectsOrSolutionsToBuild) @@ -49,7 +49,7 @@ namespace Semmle.Autobuild.CSharp restoreCommand.QuoteArgument(projectOrSolution.FullPath); var restore = restoreCommand.Script; - var build = GetBuildScript(builder, dotNetPath, environment, compatibleClr, projectOrSolution.FullPath); + var build = GetBuildScript(builder, dotNetPath, environment, projectOrSolution.FullPath); ret &= BuildScript.Try(clean) & BuildScript.Try(restore) & build; } @@ -57,7 +57,7 @@ namespace Semmle.Autobuild.CSharp }); } - private static BuildScript WithDotNet(Autobuilder builder, Func?, bool, BuildScript> f) + private static BuildScript WithDotNet(Autobuilder builder, Func?, BuildScript> f) { var installDir = builder.Actions.PathCombine(builder.Options.RootDirectory, ".dotnet"); var installScript = DownloadDotNet(builder, installDir); @@ -81,35 +81,10 @@ namespace Semmle.Autobuild.CSharp env = null; } - // The CLR tracer is always compatible on Windows - if (builder.Actions.IsWindows()) - return f(installDir, env, true); - - // The CLR tracer is only compatible on .NET Core >= 3 on Linux and macOS (see - // https://github.com/dotnet/coreclr/issues/19622) - return BuildScript.Bind(GetInstalledRuntimesScript(builder.Actions, installDir, env), (runtimes, runtimesRet) => - { - var compatibleClr = false; - if (runtimesRet == 0) - { - var minimumVersion = new Version(3, 0); - var regex = new Regex(@"Microsoft\.NETCore\.App (\d\.\d\.\d)"); - compatibleClr = runtimes - .Select(runtime => regex.Match(runtime)) - .Where(m => m.Success) - .Select(m => m.Groups[1].Value) - .Any(m => Version.TryParse(m, out var v) && v >= minimumVersion); - } - - if (!compatibleClr) - { - if (env is null) - env = new Dictionary(); - env.Add("UseSharedCompilation", "false"); - } - - return f(installDir, env, compatibleClr); - }); + if (env is null) + env = new Dictionary(); + env.Add("UseSharedCompilation", "false"); + return f(installDir, env); }); } @@ -122,7 +97,7 @@ namespace Semmle.Autobuild.CSharp /// are needed). /// public static BuildScript WithDotNet(Autobuilder builder, Func?, BuildScript> f) - => WithDotNet(builder, (_1, env, _2) => f(env)); + => WithDotNet(builder, (_1, env) => f(env)); /// /// Returns a script for downloading relevant versions of the @@ -179,53 +154,20 @@ namespace Semmle.Autobuild.CSharp if (builder.Actions.IsWindows()) { - var psScript = @"param([string]$Version, [string]$InstallDir) -add-type @"" -using System.Net; -using System.Security.Cryptography.X509Certificates; -public class TrustAllCertsPolicy : ICertificatePolicy -{ - public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) - { - return true; - } -} -""@ -$AllProtocols = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12' -[System.Net.ServicePointManager]::SecurityProtocol = $AllProtocols -[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy -$Script = Invoke-WebRequest -useb 'https://dot.net/v1/dotnet-install.ps1' + var psCommand = $"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Version {version} -InstallDir {path}"; -$arguments = @{ - Channel = 'release' - Version = $Version - InstallDir = $InstallDir -} - -$ScriptBlock = [scriptblock]::create("".{$($Script)} $(&{$args} @arguments)"") - -Invoke-Command -ScriptBlock $ScriptBlock"; - var psScriptFile = builder.Actions.PathCombine(builder.Options.RootDirectory, "install-dotnet.ps1"); - builder.Actions.WriteAllText(psScriptFile, psScript); - - var install = new CommandBuilder(builder.Actions). - RunCommand("powershell"). + BuildScript GetInstall(string pwsh) => + new CommandBuilder(builder.Actions). + RunCommand(pwsh). Argument("-NoProfile"). Argument("-ExecutionPolicy"). Argument("unrestricted"). - Argument("-file"). - Argument(psScriptFile). - Argument("-Version"). - Argument(version). - Argument("-InstallDir"). - Argument(path); + Argument("-Command"). + Argument("\"" + psCommand + "\""). + Script; - var removeScript = new CommandBuilder(builder.Actions). - RunCommand("del"). - Argument(psScriptFile); - - return install.Script & BuildScript.Try(removeScript.Script); + return GetInstall("pwsh") | GetInstall("powershell"); } else { @@ -292,34 +234,17 @@ Invoke-Command -ScriptBlock $ScriptBlock"; return restore; } - private static BuildScript GetInstalledRuntimesScript(IBuildActions actions, string? dotNetPath, IDictionary? environment) - { - var listSdks = new CommandBuilder(actions, environment: environment, silent: true). - RunCommand(DotNetCommand(actions, dotNetPath)). - Argument("--list-runtimes"); - return listSdks.Script; - } - /// /// Gets the `dotnet build` script. - /// - /// The CLR tracer only works on .NET Core >= 3 on Linux and macOS (see - /// https://github.com/dotnet/coreclr/issues/19622), so in case we are - /// running on an older .NET Core, we disable shared compilation (and - /// hence the need for CLR tracing), by adding a - /// `/p:UseSharedCompilation=false` argument. /// - private static BuildScript GetBuildScript(Autobuilder builder, string? dotNetPath, IDictionary? environment, bool compatibleClr, string projOrSln) + private static BuildScript GetBuildScript(Autobuilder builder, string? dotNetPath, IDictionary? environment, string projOrSln) { var build = new CommandBuilder(builder.Actions, null, environment); var script = builder.MaybeIndex(build, DotNetCommand(builder.Actions, dotNetPath)). Argument("build"). Argument("--no-incremental"); - return compatibleClr ? - script.Argument(builder.Options.DotNetArguments). - QuoteArgument(projOrSln). - Script : + return script.Argument("/p:UseSharedCompilation=false"). Argument(builder.Options.DotNetArguments). QuoteArgument(projOrSln). diff --git a/csharp/change-notes/2021-04-09-dapper-support.md b/csharp/change-notes/2021-04-09-dapper-support.md new file mode 100644 index 00000000000..a7db9d045d2 --- /dev/null +++ b/csharp/change-notes/2021-04-09-dapper-support.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Support for the Dapper ORM library has been added to the SQL injection checks. \ No newline at end of file diff --git a/csharp/change-notes/2021-04-09-default-argument-values.md b/csharp/change-notes/2021-04-09-default-argument-values.md new file mode 100644 index 00000000000..ef34dca4f6a --- /dev/null +++ b/csharp/change-notes/2021-04-09-default-argument-values.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The extractor has been improved to store default argument values for parameters that are extracted from referenced assemblies. \ No newline at end of file diff --git a/csharp/change-notes/2021-04-14-customizations.md b/csharp/change-notes/2021-04-14-customizations.md new file mode 100644 index 00000000000..a2f957ca923 --- /dev/null +++ b/csharp/change-notes/2021-04-14-customizations.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* A new library, `Customizations.qll`, has been added, which allows for global customizations that affect all queries. \ No newline at end of file diff --git a/csharp/change-notes/2021-04-22-console-read-local-source.md b/csharp/change-notes/2021-04-22-console-read-local-source.md new file mode 100644 index 00000000000..9fbb5f2ff32 --- /dev/null +++ b/csharp/change-notes/2021-04-22-console-read-local-source.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* `System.Console.Read` methods have been added as data flow sources of local user input. \ No newline at end of file diff --git a/csharp/change-notes/2021-04-23-model-error-extraction.md b/csharp/change-notes/2021-04-23-model-error-extraction.md new file mode 100644 index 00000000000..7ac87844298 --- /dev/null +++ b/csharp/change-notes/2021-04-23-model-error-extraction.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Program model errors are now extracted in standalone extraction. \ No newline at end of file diff --git a/csharp/change-notes/2021-04-26-string-builder-summaries.md b/csharp/change-notes/2021-04-26-string-builder-summaries.md new file mode 100644 index 00000000000..285f40faf39 --- /dev/null +++ b/csharp/change-notes/2021-04-26-string-builder-summaries.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Data-flow modelling of `StringBuilder` objects has been improved. \ No newline at end of file diff --git a/csharp/change-notes/2021-05-03-implicit-constructor-init.md b/csharp/change-notes/2021-05-03-implicit-constructor-init.md new file mode 100644 index 00000000000..06267557aa8 --- /dev/null +++ b/csharp/change-notes/2021-05-03-implicit-constructor-init.md @@ -0,0 +1,14 @@ +lgtm,codescanning +* Implicit base constructor calls are now extracted. For example, in + ```csharp + class Base + { + public Base() { } + } + + class Sub : Base + { + public Sub() { } + } + ``` + there is an implicit call to the `Base` constructor from the `Sub` constructor. diff --git a/csharp/change-notes/2021-06-04-tuple-members.md b/csharp/change-notes/2021-06-04-tuple-members.md new file mode 100644 index 00000000000..035b2141b28 --- /dev/null +++ b/csharp/change-notes/2021-06-04-tuple-members.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Members extracted from `TupleTypes` have been fixed to be assigned to the underlying ``struct ValueTuple`N``. \ No newline at end of file diff --git a/csharp/change-notes/2021-06-15-unsafe-non-source-code.md b/csharp/change-notes/2021-06-15-unsafe-non-source-code.md new file mode 100644 index 00000000000..8e45080b132 --- /dev/null +++ b/csharp/change-notes/2021-06-15-unsafe-non-source-code.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The `Modifiable::isUnsafe` predicate has been fixed to handle symbols that are not extracted from source code. \ No newline at end of file diff --git a/csharp/extractor/Semmle.Extraction.CIL/Context.Factories.cs b/csharp/extractor/Semmle.Extraction.CIL/Context.Factories.cs index 0cdc5bf8dac..5ce1a58491f 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Context.Factories.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Context.Factories.cs @@ -36,7 +36,7 @@ namespace Semmle.Extraction.CIL c.Extract(this); }); #if DEBUG_LABELS - using var writer = new StringWriter(); + using var writer = new EscapingTextWriter(); e.WriteId(writer); var id = writer.ToString(); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/ArrayType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/ArrayType.cs index b838a9070ff..0f4138e20c5 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/ArrayType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/ArrayType.cs @@ -29,7 +29,7 @@ namespace Semmle.Extraction.CIL.Entities public override int GetHashCode() => HashCode.Combine(elementType, rank); - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { elementType.WriteId(trapFile, inContext); trapFile.Write('['); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs index d28fa0035c3..c94038902f7 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Assembly.cs @@ -32,7 +32,7 @@ namespace Semmle.Extraction.CIL.Entities file = new File(cx, cx.AssemblyPath); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write(FullName); trapFile.Write("#file:///"); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/LabelledEntity.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/LabelledEntity.cs index 150074f22ee..780ced94916 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/LabelledEntity.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Base/LabelledEntity.cs @@ -25,7 +25,7 @@ namespace Semmle.Extraction.CIL public override string ToString() { - using var writer = new StringWriter(); + using var writer = new EscapingTextWriter(); WriteQuotedId(writer); return writer.ToString(); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/ByRefType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/ByRefType.cs index 2a527419249..5b9ae9fd1aa 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/ByRefType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/ByRefType.cs @@ -33,7 +33,7 @@ namespace Semmle.Extraction.CIL.Entities public override void WriteAssemblyPrefix(TextWriter trapFile) => throw new NotImplementedException(); - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { ElementType.WriteId(trapFile, inContext); trapFile.Write('&'); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/ConstructedType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/ConstructedType.cs index 6e2fb90beae..c97ef697700 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/ConstructedType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/ConstructedType.cs @@ -100,7 +100,7 @@ namespace Semmle.Extraction.CIL.Entities throw new NotImplementedException(); } - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { idWriter.WriteId(trapFile, inContext); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/ErrorType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/ErrorType.cs index e08cea2854c..41b5810ba27 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/ErrorType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/ErrorType.cs @@ -10,7 +10,7 @@ namespace Semmle.Extraction.CIL.Entities { } - public override void WriteId(TextWriter trapFile, bool inContext) => trapFile.Write(""); + public override void WriteId(EscapingTextWriter trapFile, bool inContext) => trapFile.Write(""); public override CilTypeKind Kind => CilTypeKind.ValueOrRefType; diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Event.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Event.cs index a54d0a8065d..0ed8e871d55 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Event.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Event.cs @@ -20,7 +20,7 @@ namespace Semmle.Extraction.CIL.Entities ed = cx.MdReader.GetEventDefinition(handle); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { parent.WriteId(trapFile); trapFile.Write('.'); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs index 8decef24128..96ad1715c27 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Field.cs @@ -14,7 +14,7 @@ namespace Semmle.Extraction.CIL.Entities { } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(DeclaringType); trapFile.Write('.'); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs index a07ffc61de8..c2fd6837e3e 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs @@ -14,7 +14,7 @@ namespace Semmle.Extraction.CIL.Entities TransformedPath = Context.Extractor.PathTransformer.Transform(OriginalPath); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write(TransformedPath.DatabaseId); trapFile.Write(";sourcefile"); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs index b49abce64c9..9c3fbadcf20 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs @@ -12,7 +12,7 @@ namespace Semmle.Extraction.CIL.Entities this.transformedPath = path; } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write(transformedPath.DatabaseId); trapFile.Write(";folder"); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/FunctionPointerType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/FunctionPointerType.cs index 3b6bbba00cc..511826e003c 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/FunctionPointerType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/FunctionPointerType.cs @@ -42,7 +42,7 @@ namespace Semmle.Extraction.CIL.Entities public override void WriteAssemblyPrefix(TextWriter trapFile) { } - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { WriteName( trapFile.Write, diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/ITypeSignature.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/ITypeSignature.cs index beaecdfab6f..004d2707738 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/ITypeSignature.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/ITypeSignature.cs @@ -4,6 +4,6 @@ namespace Semmle.Extraction.CIL.Entities { internal interface ITypeSignature { - void WriteId(TextWriter trapFile, IGenericContext gc); + void WriteId(EscapingTextWriter trapFile, IGenericContext gc); } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/LocalVariable.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/LocalVariable.cs index 5d7a00c64c9..b7690d2498e 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/LocalVariable.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/LocalVariable.cs @@ -16,7 +16,7 @@ namespace Semmle.Extraction.CIL.Entities type = t; } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(method); trapFile.Write('_'); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs index 9df82edaa68..199eb691689 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Method.cs @@ -37,17 +37,15 @@ namespace Semmle.Extraction.CIL.Entities public virtual IList? LocalVariables => throw new NotImplementedException(); public IList? Parameters { get; protected set; } - public override void WriteId(TextWriter trapFile) => WriteMethodId(trapFile, DeclaringType, NameLabel); - public abstract string NameLabel { get; } - protected internal void WriteMethodId(TextWriter trapFile, Type parent, string methodName) + public override void WriteId(EscapingTextWriter trapFile) { signature.ReturnType.WriteId(trapFile, this); trapFile.Write(' '); - parent.WriteId(trapFile); + DeclaringType.WriteId(trapFile); trapFile.Write('.'); - trapFile.Write(methodName); + trapFile.Write(NameLabel); if (signature.GenericParameterCount > 0) { @@ -61,11 +59,9 @@ namespace Semmle.Extraction.CIL.Entities trapFile.WriteSeparator(",", ref index); param.WriteId(trapFile, this); } - trapFile.Write(')'); + trapFile.Write(");cil-method"); } - public override string IdSuffix => ";cil-method"; - protected IEnumerable PopulateFlags { get diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodSpecificationMethod.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodSpecificationMethod.cs index 840d106b536..2cea67ec16c 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodSpecificationMethod.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodSpecificationMethod.cs @@ -26,7 +26,7 @@ namespace Semmle.Extraction.CIL.Entities unboundMethod = (Method)Context.CreateGeneric(gc, ms.Method); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { unboundMethod.WriteId(trapFile); trapFile.Write('<'); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodTypeParameter.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodTypeParameter.cs index 1795eb29269..a36cf372ea0 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodTypeParameter.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/MethodTypeParameter.cs @@ -9,7 +9,7 @@ namespace Semmle.Extraction.CIL.Entities private readonly Method method; private readonly int index; - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { if (!(inContext && method == gc)) { diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/ModifiedType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/ModifiedType.cs index f160c6869de..36e08a2e594 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/ModifiedType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/ModifiedType.cs @@ -37,7 +37,7 @@ namespace Semmle.Extraction.CIL.Entities public override void WriteAssemblyPrefix(TextWriter trapFile) => throw new NotImplementedException(); - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { Unmodified.WriteId(trapFile, inContext); trapFile.Write(IsRequired ? " modreq" : " modopt"); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/NamedTypeIdWriter.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/NamedTypeIdWriter.cs index 59c7d172d01..3a71b76b0c3 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/NamedTypeIdWriter.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/NamedTypeIdWriter.cs @@ -12,7 +12,7 @@ namespace Semmle.Extraction.CIL.Entities this.type = type; } - public void WriteId(TextWriter trapFile, bool inContext) + public void WriteId(EscapingTextWriter trapFile, bool inContext) { if (type.IsPrimitiveType) { diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Namespace.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Namespace.cs index 1d0b373952b..3b7354c791d 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Namespace.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Namespace.cs @@ -14,7 +14,7 @@ namespace Semmle.Extraction.CIL.Entities public bool IsGlobalNamespace => ParentNamespace is null; - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { if (ParentNamespace is not null && !ParentNamespace.IsGlobalNamespace) { @@ -22,10 +22,9 @@ namespace Semmle.Extraction.CIL.Entities trapFile.Write('.'); } trapFile.Write(Name); + trapFile.Write(";namespace"); } - public override string IdSuffix => ";namespacee"; - public override bool Equals(object? obj) { if (obj is Namespace ns && Name == ns.Name) diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/NoMetadataHandleType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/NoMetadataHandleType.cs index c2a340c4de1..41a28e32bf1 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/NoMetadataHandleType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/NoMetadataHandleType.cs @@ -137,7 +137,7 @@ namespace Semmle.Extraction.CIL.Entities } } - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { idWriter.WriteId(trapFile, inContext); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Parameter.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Parameter.cs index 9cf96412309..b8906f6b845 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Parameter.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Parameter.cs @@ -19,7 +19,7 @@ namespace Semmle.Extraction.CIL.Entities type = t; } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(parameterizable); trapFile.Write('_'); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/PointerType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/PointerType.cs index 8b1209925b6..1a6bd474bd0 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/PointerType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/PointerType.cs @@ -20,7 +20,7 @@ namespace Semmle.Extraction.CIL.Entities public override int GetHashCode() => HashCode.Combine(pointee, nameof(PointerType)); - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { pointee.WriteId(trapFile, inContext); trapFile.Write('*'); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/PrimitiveType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/PrimitiveType.cs index 1e331397237..0e776c1aaad 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/PrimitiveType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/PrimitiveType.cs @@ -20,7 +20,7 @@ namespace Semmle.Extraction.CIL.Entities public override int GetHashCode() => typeCode.GetHashCode(); - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { Type.WritePrimitiveTypeId(trapFile, Name); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs index 9e56c22a099..99a462069e1 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Property.cs @@ -23,7 +23,7 @@ namespace Semmle.Extraction.CIL.Entities this.type = type; } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(type); trapFile.Write('.'); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/SignatureDecoder.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/SignatureDecoder.cs index ea646449421..0f012c0c18d 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/SignatureDecoder.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/SignatureDecoder.cs @@ -17,7 +17,7 @@ namespace Semmle.Extraction.CIL.Entities this.shape = shape; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { elementType.WriteId(trapFile, gc); trapFile.Write('['); @@ -38,7 +38,7 @@ namespace Semmle.Extraction.CIL.Entities this.elementType = elementType; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { elementType.WriteId(trapFile, gc); trapFile.Write('&'); @@ -54,7 +54,7 @@ namespace Semmle.Extraction.CIL.Entities this.signature = signature; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { FunctionPointerType.WriteName( trapFile.Write, @@ -84,7 +84,7 @@ namespace Semmle.Extraction.CIL.Entities this.typeArguments = typeArguments; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { genericType.WriteId(trapFile, gc); trapFile.Write('<'); @@ -112,7 +112,7 @@ namespace Semmle.Extraction.CIL.Entities this.index = index; } - public void WriteId(TextWriter trapFile, IGenericContext outerGc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext outerGc) { if (!ReferenceEquals(innerGc, outerGc) && innerGc is Method method) { @@ -132,7 +132,7 @@ namespace Semmle.Extraction.CIL.Entities this.index = index; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { trapFile.Write("T!"); trapFile.Write(index); @@ -158,7 +158,7 @@ namespace Semmle.Extraction.CIL.Entities this.isRequired = isRequired; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { unmodifiedType.WriteId(trapFile, gc); trapFile.Write(isRequired ? " modreq(" : " modopt("); @@ -186,7 +186,7 @@ namespace Semmle.Extraction.CIL.Entities this.elementType = elementType; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { elementType.WriteId(trapFile, gc); trapFile.Write('*'); @@ -207,7 +207,7 @@ namespace Semmle.Extraction.CIL.Entities this.typeCode = typeCode; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { trapFile.Write(typeCode.Id()); } @@ -227,7 +227,7 @@ namespace Semmle.Extraction.CIL.Entities this.elementType = elementType; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { elementType.WriteId(trapFile, gc); trapFile.Write("[]"); @@ -248,7 +248,7 @@ namespace Semmle.Extraction.CIL.Entities this.handle = handle; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { var type = (Type)gc.Context.Create(handle); type.WriteId(trapFile); @@ -269,7 +269,7 @@ namespace Semmle.Extraction.CIL.Entities this.handle = handle; } - public void WriteId(TextWriter trapFile, IGenericContext gc) + public void WriteId(EscapingTextWriter trapFile, IGenericContext gc) { 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 ff67844121a..5420ac53645 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/SourceLocation.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/SourceLocation.cs @@ -15,7 +15,7 @@ namespace Semmle.Extraction.CIL.Entities file = cx.CreateSourceFile(location.File); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { file.WriteId(trapFile); trapFile.Write(','); diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs index db4876f8a9a..5d27c5400d5 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Type.cs @@ -43,11 +43,13 @@ namespace Semmle.Extraction.CIL.Entities /// (This is to avoid infinite recursion generating a method ID that returns a /// type parameter.) /// - public abstract void WriteId(TextWriter trapFile, bool inContext); + public abstract void WriteId(EscapingTextWriter trapFile, bool inContext); - public sealed override void WriteId(TextWriter trapFile) => WriteId(trapFile, false); - - public override string IdSuffix => ";cil-type"; + public sealed override void WriteId(EscapingTextWriter trapFile) + { + WriteId(trapFile, false); + trapFile.Write(";cil-type"); + } /// /// Returns the friendly qualified name of types, such as @@ -58,10 +60,12 @@ namespace Semmle.Extraction.CIL.Entities /// public string GetQualifiedName() { - using var writer = new StringWriter(); + using var writer = new EscapingTextWriter(); WriteId(writer, false); var name = writer.ToString(); - return name.Substring(name.IndexOf(AssemblyTypeNameSeparator) + 2); + return name.Substring(name.IndexOf(AssemblyTypeNameSeparator) + 2). + Replace(";namespace", ""). + Replace(";cil-type", ""); } public abstract CilTypeKind Kind { get; } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeContainer.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeContainer.cs index e201e065255..aae0b4b0b48 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeContainer.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeContainer.cs @@ -12,16 +12,6 @@ namespace Semmle.Extraction.CIL.Entities { } - public abstract string IdSuffix { get; } - - public override void WriteQuotedId(TextWriter trapFile) - { - trapFile.Write("@\""); - WriteId(trapFile); - trapFile.Write(IdSuffix); - trapFile.Write('\"'); - } - public abstract IEnumerable MethodParameters { get; } public abstract IEnumerable TypeParameters { get; } } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeDefinitionType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeDefinitionType.cs index 51b02f1482e..a5d377846d7 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeDefinitionType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeDefinitionType.cs @@ -40,7 +40,7 @@ namespace Semmle.Extraction.CIL.Entities public override int GetHashCode() => handle.GetHashCode(); - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { idWriter.WriteId(trapFile, inContext); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeReferenceType.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeReferenceType.cs index c94a6978f5e..1af91d32586 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeReferenceType.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeReferenceType.cs @@ -88,7 +88,7 @@ namespace Semmle.Extraction.CIL.Entities public override IEnumerable ThisGenericArguments => typeParams.Value; - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { idWriter.WriteId(trapFile, inContext); } diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeTypeParameter.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeTypeParameter.cs index 15db1536a3f..580c8573acf 100644 --- a/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeTypeParameter.cs +++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/TypeTypeParameter.cs @@ -25,7 +25,7 @@ namespace Semmle.Extraction.CIL.Entities return type.GetHashCode() * 13 + index; } - public override void WriteId(TextWriter trapFile, bool inContext) + public override void WriteId(EscapingTextWriter trapFile, bool inContext) { type.WriteId(trapFile, inContext); trapFile.Write('!'); diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Driver/Driver.cs b/csharp/extractor/Semmle.Extraction.CSharp.Driver/Driver.cs index 1c4142097fe..e8a42b3f5a0 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Driver/Driver.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Driver/Driver.cs @@ -7,6 +7,8 @@ namespace Semmle.Extraction.CSharp { public static int Main(string[] args) { + Extractor.SetInvariantCulture(); + return (int)Extractor.Run(args); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Program.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Program.cs index 197001a22a6..e2c973ee60f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Program.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Program.cs @@ -52,6 +52,8 @@ namespace Semmle.Extraction.CSharp.Standalone { public static int Main(string[] args) { + Extractor.SetInvariantCulture(); + var options = Options.Create(args); // options.CIL = true; // To do: Enable this using var output = new ConsoleLogger(options.Verbosity); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Assembly.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Assembly.cs index c0ffce70dbe..9555fabec4f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Assembly.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Assembly.cs @@ -69,7 +69,7 @@ namespace Semmle.Extraction.CSharp.Entities return AssemblyConstructorFactory.Instance.CreateEntity(cx, outputAssemblyCacheKey, null); } - public override void WriteId(System.IO.TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write(assembly.ToString()); if (!(assemblyPath is null)) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Attribute.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Attribute.cs index b1bfb508d75..7e7fc39aab8 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Attribute.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Attribute.cs @@ -20,7 +20,7 @@ namespace Semmle.Extraction.CSharp.Entities this.entity = entity; } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { if (ReportingLocation?.IsInSource == true) { @@ -33,7 +33,7 @@ namespace Semmle.Extraction.CSharp.Entities } } - public override void WriteQuotedId(TextWriter trapFile) + public sealed override void WriteQuotedId(EscapingTextWriter trapFile) { if (ReportingLocation?.IsInSource == true) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CachedSymbol.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CachedSymbol.cs index d6fb0d65e5d..378bda881f7 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CachedSymbol.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CachedSymbol.cs @@ -16,7 +16,11 @@ namespace Semmle.Extraction.CSharp.Entities { } - public virtual Type? ContainingType => Symbol.ContainingType is not null ? Type.Create(Context, Symbol.ContainingType) : null; + public virtual Type? ContainingType => Symbol.ContainingType is not null + ? Symbol.ContainingType.IsTupleType + ? NamedType.CreateNamedTypeFromTupleType(Context, Symbol.ContainingType) + : Type.Create(Context, Symbol.ContainingType) + : null; public void PopulateModifiers(TextWriter trapFile) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs index a3394885d1c..6d4a2cf07f9 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs @@ -21,7 +21,7 @@ namespace Semmle.Extraction.CSharp.Entities public override bool NeedsPopulation => true; - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(Context.CreateLocation(Symbol.Location)); trapFile.Write(";commentblock"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs index a1c9fb5c643..b9fb6cdfee2 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs @@ -32,7 +32,7 @@ namespace Semmle.Extraction.CSharp.Entities public override bool NeedsPopulation => true; - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(Context.CreateLocation(Location)); trapFile.Write(";commentline"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilations/Compilation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilations/Compilation.cs index 1c929c1ad5a..f3672e4c6e4 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilations/Compilation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Compilations/Compilation.cs @@ -81,7 +81,7 @@ namespace Semmle.Extraction.CSharp.Entities trapFile.compilation_finished(this, (float)p.Total.Cpu.TotalSeconds, (float)p.Total.Elapsed.TotalSeconds); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write(hashCode); trapFile.Write(";compilation"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs index 1424ba31f4d..e3f77d9407e 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs @@ -41,7 +41,38 @@ namespace Semmle.Extraction.CSharp.Entities var initializer = syntax?.Initializer; if (initializer is null) + { + if (Symbol.MethodKind is MethodKind.Constructor) + { + var baseType = Symbol.ContainingType.BaseType; + if (baseType is null) + { + Context.ModelError(Symbol, "Unable to resolve base type in implicit constructor initializer"); + return; + } + + var baseConstructor = baseType.InstanceConstructors.FirstOrDefault(c => c.Arity is 0); + + if (baseConstructor is null) + { + Context.ModelError(Symbol, "Unable to resolve implicit constructor initializer call"); + return; + } + + var baseConstructorTarget = Create(Context, baseConstructor); + var info = new ExpressionInfo(Context, + AnnotatedTypeSymbol.CreateNotAnnotated(baseType), + Location, + Kinds.ExprKind.CONSTRUCTOR_INIT, + this, + -1, + isCompilerGenerated: true, + null); + + trapFile.expr_call(new Expression(info), baseConstructorTarget); + } return; + } ITypeSymbol initializerType; var symbolInfo = Context.GetSymbolInfo(initializer); @@ -113,8 +144,17 @@ namespace Semmle.Extraction.CSharp.Entities } } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { + if (!SymbolEqualityComparer.Default.Equals(Symbol, Symbol.OriginalDefinition)) + { + trapFile.WriteSubId(ContainingType!); + trapFile.Write("."); + trapFile.WriteSubId(OriginalDefinition); + trapFile.Write(";constructor"); + return; + } + if (Symbol.IsStatic) trapFile.Write("static"); trapFile.WriteSubId(ContainingType!); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs index e92a42d8ab1..ca9753d229b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs @@ -10,7 +10,7 @@ namespace Semmle.Extraction.CSharp.Entities private Event(Context cx, IEventSymbol init) : base(cx, init) { } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(ContainingType!); trapFile.Write('.'); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs index cdf17311440..a478047ac7b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs @@ -164,6 +164,50 @@ namespace Semmle.Extraction.CSharp.Entities } } + /// + /// Creates a generated expression for a default argument value. + /// + public static Expression? CreateGenerated(Context cx, IParameterSymbol parameter, IExpressionParentEntity parent, + int childIndex, Extraction.Entities.Location location) + { + if (!parameter.HasExplicitDefaultValue || + parameter.Type is IErrorTypeSymbol) + { + return null; + } + + var defaultValue = parameter.ExplicitDefaultValue; + + if (parameter.Type is INamedTypeSymbol nt && nt.EnumUnderlyingType is not null) + { + // = (MyEnum)1, = MyEnum.Value1, = default(MyEnum), = new MyEnum() + // we're generating a (MyEnum)value cast expression: + defaultValue ??= 0; + Action createChild = (parent, index) => Literal.CreateGenerated(cx, parent, index, nt.EnumUnderlyingType, defaultValue, location); + return Cast.CreateGenerated(cx, parent, childIndex, parameter.Type, defaultValue, createChild, location); + } + + if (defaultValue is null) + { + // = null, = default, = default(T), = new MyStruct() + // we're generating a default expression: + return Default.CreateGenerated(cx, parent, childIndex, location, parameter.Type.IsReferenceType ? ValueAsString(null) : null); + } + + if (parameter.Type.SpecialType == SpecialType.System_Object) + { + // this can happen in VB.NET + cx.ExtractionError($"Extracting default argument value 'object {parameter.Name} = default' instead of 'object {parameter.Name} = {defaultValue}'. The latter is not supported in C#.", + null, null, severity: Util.Logging.Severity.Warning); + + // we're generating a default expression: + return Default.CreateGenerated(cx, parent, childIndex, location, ValueAsString(null)); + } + + // const literal: + return Literal.CreateGenerated(cx, parent, childIndex, parameter.Type, defaultValue, location); + } + /// /// Adapt the operator kind depending on whether it's a dynamic call or a user-operator call. /// diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Default.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Default.cs index 9e1b42834b1..9fdfd18d3bd 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Default.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Default.cs @@ -14,5 +14,20 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions { TypeAccess.Create(Context, Syntax.Type, this, 0); } + + public static Expression CreateGenerated(Context cx, IExpressionParentEntity parent, int childIndex, Extraction.Entities.Location location, string? value) + { + var info = new ExpressionInfo( + cx, + null, + location, + ExprKind.DEFAULT, + parent, + childIndex, + true, + value); + + return new Expression(info); + } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs index 2fe1cf224ab..851d1103e1d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs @@ -118,7 +118,7 @@ namespace Semmle.Extraction.CSharp.Entities private readonly Lazy type; public Type Type => type.Value; - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(Type); trapFile.Write(" "); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs index 3da002f8b2c..8c00bb94335 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs @@ -73,13 +73,13 @@ namespace Semmle.Extraction.CSharp.Entities public static new Indexer Create(Context cx, IPropertySymbol prop) => IndexerFactory.Instance.CreateEntityFromSymbol(cx, prop); - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(ContainingType!); trapFile.Write('.'); trapFile.Write(Symbol.MetadataName); trapFile.Write('('); - trapFile.BuildList(",", Symbol.Parameters, (p, tb0) => tb0.WriteSubId(Type.Create(Context, p.Type))); + trapFile.BuildList(",", Symbol.Parameters, p => trapFile.WriteSubId(Type.Create(Context, p.Type))); trapFile.Write(");indexer"); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs index a94401f2a93..a94c170f408 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalFunction.cs @@ -10,12 +10,12 @@ namespace Semmle.Extraction.CSharp.Entities { } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { throw new InvalidOperationException(); } - public override void WriteQuotedId(TextWriter trapFile) + public sealed override void WriteQuotedId(EscapingTextWriter trapFile) { trapFile.Write('*'); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs index ee685731c60..81a47710ba1 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs @@ -8,12 +8,12 @@ namespace Semmle.Extraction.CSharp.Entities { private LocalVariable(Context cx, ISymbol init) : base(cx, init) { } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { throw new InvalidOperationException(); } - public override void WriteQuotedId(TextWriter trapFile) + public sealed override void WriteQuotedId(EscapingTextWriter trapFile) { trapFile.Write('*'); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs index 61c8d768521..20c0033c4de 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs @@ -20,26 +20,6 @@ namespace Semmle.Extraction.CSharp.Entities IEnumerable parameters = Symbol.Parameters; IEnumerable originalParameters = originalMethod.Symbol.Parameters; - if (IsReducedExtension) - { - if (this == originalMethod) - { - // Non-generic reduced extensions must be extracted exactly like the - // non-reduced counterparts - parameters = Symbol.ReducedFrom!.Parameters; - } - else - { - // Constructed reduced extensions are special because their non-reduced - // counterparts are not constructed. Therefore, we need to manually add - // the `this` parameter based on the type of the receiver - var originalThisParamSymbol = originalMethod.Symbol.Parameters.First(); - var originalThisParam = Parameter.Create(Context, originalThisParamSymbol, originalMethod); - ConstructedExtensionParameter.Create(Context, this, originalThisParam); - originalParameters = originalParameters.Skip(1); - } - } - foreach (var p in parameters.Zip(originalParameters, (paramSymbol, originalParam) => new { paramSymbol, originalParam })) { var original = SymbolEqualityComparer.Default.Equals(p.paramSymbol, p.originalParam) @@ -127,8 +107,32 @@ namespace Semmle.Extraction.CSharp.Entities /// /// Factored out to share logic between `Method` and `UserOperator`. /// - private static void BuildMethodId(Method m, TextWriter trapFile) + private static void BuildMethodId(Method m, EscapingTextWriter trapFile) { + if (!SymbolEqualityComparer.Default.Equals(m.Symbol, m.Symbol.OriginalDefinition)) + { + if (!SymbolEqualityComparer.Default.Equals(m.Symbol, m.ConstructedFromSymbol)) + { + trapFile.WriteSubId(Create(m.Context, m.ConstructedFromSymbol)); + trapFile.Write('<'); + // Encode the nullability of the type arguments in the label. + // Type arguments with different nullability can result in + // a constructed method with different nullability of its parameters and return type, + // so we need to create a distinct database entity for it. + trapFile.BuildList(",", m.Symbol.GetAnnotatedTypeArguments(), ta => { ta.Symbol.BuildOrWriteId(m.Context, trapFile, m.Symbol); trapFile.Write((int)ta.Nullability); }); + trapFile.Write('>'); + } + else + { + trapFile.WriteSubId(m.ContainingType!); + trapFile.Write("."); + trapFile.WriteSubId(m.OriginalDefinition); + } + + WritePostfix(m, trapFile); + return; + } + m.Symbol.ReturnType.BuildOrWriteId(m.Context, trapFile, m.Symbol); trapFile.Write(" "); @@ -141,24 +145,16 @@ namespace Semmle.Extraction.CSharp.Entities if (m.Symbol.IsGenericMethod) { - if (SymbolEqualityComparer.Default.Equals(m.Symbol, m.Symbol.OriginalDefinition)) - { - trapFile.Write('`'); - trapFile.Write(m.Symbol.TypeParameters.Length); - } - else - { - trapFile.Write('<'); - // Encode the nullability of the type arguments in the label. - // Type arguments with different nullability can result in - // a constructed method with different nullability of its parameters and return type, - // so we need to create a distinct database entity for it. - trapFile.BuildList(",", m.Symbol.GetAnnotatedTypeArguments(), (ta, tb0) => { ta.Symbol.BuildOrWriteId(m.Context, tb0, m.Symbol); trapFile.Write((int)ta.Nullability); }); - trapFile.Write('>'); - } + trapFile.Write('`'); + trapFile.Write(m.Symbol.TypeParameters.Length); } AddParametersToId(m.Context, trapFile, m.Symbol); + WritePostfix(m, trapFile); + } + + private static void WritePostfix(Method m, EscapingTextWriter trapFile) + { switch (m.Symbol.MethodKind) { case MethodKind.PropertyGet: @@ -182,19 +178,17 @@ namespace Semmle.Extraction.CSharp.Entities } } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { BuildMethodId(this, trapFile); } - protected static void AddParametersToId(Context cx, TextWriter trapFile, IMethodSymbol method) + protected static void AddParametersToId(Context cx, EscapingTextWriter trapFile, IMethodSymbol method) { trapFile.Write('('); var index = 0; - var @params = method.MethodKind == MethodKind.ReducedExtension - ? method.ReducedFrom!.Parameters - : method.Parameters; + var @params = method.Parameters; foreach (var param in @params) { @@ -222,7 +216,7 @@ namespace Semmle.Extraction.CSharp.Entities trapFile.Write(')'); } - public static void AddExplicitInterfaceQualifierToId(Context cx, System.IO.TextWriter trapFile, IEnumerable explicitInterfaceImplementations) + public static void AddExplicitInterfaceQualifierToId(Context cx, EscapingTextWriter trapFile, IEnumerable explicitInterfaceImplementations) { if (explicitInterfaceImplementations.Any()) trapFile.AppendList(",", explicitInterfaceImplementations.Select(impl => cx.CreateEntity(impl.ContainingType))); @@ -256,6 +250,11 @@ namespace Semmle.Extraction.CSharp.Entities case MethodKind.Constructor: return Constructor.Create(cx, methodDecl); case MethodKind.ReducedExtension: + if (SymbolEqualityComparer.Default.Equals(methodDecl, methodDecl.ConstructedFrom)) + { + return OrdinaryMethod.Create(cx, methodDecl.ReducedFrom!); + } + return OrdinaryMethod.Create(cx, methodDecl.ReducedFrom!.Construct(methodDecl.TypeArguments, methodDecl.TypeArgumentNullableAnnotations)); case MethodKind.Ordinary: case MethodKind.DelegateInvoke: return OrdinaryMethod.Create(cx, methodDecl); @@ -281,10 +280,7 @@ namespace Semmle.Extraction.CSharp.Entities } } - public Method OriginalDefinition => - IsReducedExtension - ? Create(Context, Symbol.ReducedFrom!) - : Create(Context, Symbol.OriginalDefinition); + public Method OriginalDefinition => Create(Context, Symbol.OriginalDefinition); public override Location? FullLocation => ReportingLocation; @@ -302,9 +298,7 @@ namespace Semmle.Extraction.CSharp.Entities public bool IsBoundGeneric => IsGeneric && !IsUnboundGeneric; - private bool IsReducedExtension => Symbol.MethodKind == MethodKind.ReducedExtension; - - protected IMethodSymbol ConstructedFromSymbol => Symbol.ConstructedFrom.ReducedFrom ?? Symbol.ConstructedFrom; + protected IMethodSymbol ConstructedFromSymbol => Symbol.ConstructedFrom; bool IExpressionParentEntity.IsTopLevelParent => true; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs index 1d56da2623f..806e73e3590 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs @@ -10,7 +10,7 @@ namespace Semmle.Extraction.CSharp.Entities public override Location? ReportingLocation => null; - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write(Symbol); trapFile.Write(";modifier"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs index 7d77ef276cb..68b108743f3 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Namespace.cs @@ -23,7 +23,7 @@ namespace Semmle.Extraction.CSharp.Entities public override bool NeedsPopulation => true; - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { if (!Symbol.IsGlobalNamespace) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/NamespaceDeclaration.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/NamespaceDeclaration.cs index 749c7ae22c8..b9fd57b2cea 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/NamespaceDeclaration.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/NamespaceDeclaration.cs @@ -20,7 +20,7 @@ namespace Semmle.Extraction.CSharp.Entities this.parent = parent; } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(Context.CreateLocation(ReportingLocation)); trapFile.Write(";namespacedeclaration"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/NonGeneratedSourceLocation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/NonGeneratedSourceLocation.cs index 141c428dd55..7642c30d1a7 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/NonGeneratedSourceLocation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/NonGeneratedSourceLocation.cs @@ -41,7 +41,7 @@ namespace Semmle.Extraction.CSharp.Entities get; } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write("loc,"); trapFile.WriteSubId(FileEntity); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs index 0b16b4ec9fe..576eb913433 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs @@ -15,14 +15,7 @@ namespace Semmle.Extraction.CSharp.Entities protected override IMethodSymbol BodyDeclaringSymbol => Symbol.PartialImplementationPart ?? Symbol; - public IMethodSymbol SourceDeclaration - { - get - { - var reducedFrom = Symbol.ReducedFrom ?? Symbol; - return reducedFrom.OriginalDefinition; - } - } + public IMethodSymbol SourceDeclaration => Symbol.OriginalDefinition; public override Microsoft.CodeAnalysis.Location ReportingLocation => Symbol.GetSymbolLocation(); @@ -53,7 +46,15 @@ namespace Semmle.Extraction.CSharp.Entities ExtractCompilerGenerated(trapFile); } - public static new OrdinaryMethod Create(Context cx, IMethodSymbol method) => OrdinaryMethodFactory.Instance.CreateEntityFromSymbol(cx, method); + public static new OrdinaryMethod Create(Context cx, IMethodSymbol method) + { + if (method.MethodKind == MethodKind.ReducedExtension) + { + cx.Extractor.Logger.Log(Util.Logging.Severity.Warning, "Reduced extension method symbols should not be directly extracted."); + } + + return OrdinaryMethodFactory.Instance.CreateEntityFromSymbol(cx, method); + } private class OrdinaryMethodFactory : CachedEntityFactory { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs index 3bbf38a71eb..95a93d69c12 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs @@ -4,6 +4,7 @@ using System.Linq; using Microsoft.CodeAnalysis.CSharp.Syntax; using Semmle.Extraction.Entities; using System.IO; +using System; namespace Semmle.Extraction.CSharp.Entities { @@ -26,20 +27,7 @@ namespace Semmle.Extraction.CSharp.Entities None, Ref, Out, Params, This, In } - protected virtual int Ordinal - { - get - { - // For some reason, methods of kind ReducedExtension - // omit the "this" parameter, so the parameters are - // actually numbered from 1. - // This is to be consistent from the original (unreduced) extension method. - var isReducedExtension = - Symbol.ContainingSymbol is IMethodSymbol method && - method.MethodKind == MethodKind.ReducedExtension; - return Symbol.Ordinal + (isReducedExtension ? 1 : 0); - } - } + protected virtual int Ordinal => Symbol.Ordinal; private Kind ParamKind { @@ -73,7 +61,7 @@ namespace Semmle.Extraction.CSharp.Entities public static Parameter Create(Context cx, IParameterSymbol param) => ParameterFactory.Instance.CreateEntity(cx, param, (param, null, null)); - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { if (Parent is null) Parent = Method.Create(Context, Symbol.ContainingSymbol as IMethodSymbol); @@ -124,6 +112,17 @@ namespace Semmle.Extraction.CSharp.Entities trapFile.param_location(this, Context.CreateLocation()); } + if (Symbol.HasExplicitDefaultValue && Context.Defines(Symbol)) + { + var defaultValueSyntax = GetDefaultValueFromSyntax(Symbol); + + Action defaultValueExpressionCreation = defaultValueSyntax is not null + ? () => Expression.Create(Context, defaultValueSyntax.Value, this, 0) + : () => Expression.CreateGenerated(Context, Symbol, this, 0, Location); + + Context.PopulateLater(defaultValueExpressionCreation); + } + if (!IsSourceDeclaration || !Symbol.FromSource()) return; @@ -139,36 +138,28 @@ namespace Semmle.Extraction.CSharp.Entities TypeMention.Create(Context, syntax.Type!, this, type); } } + } - if (Symbol.HasExplicitDefaultValue && Context.Defines(Symbol)) + private static EqualsValueClauseSyntax? GetDefaultValueFromSyntax(IParameterSymbol symbol) + { + // This is a slight bug in the dbscheme + // We should really define param_default(param, string) + // And use parameter child #0 to encode the default expression. + var defaultValue = GetParameterDefaultValue(symbol); + if (defaultValue is null) { - // This is a slight bug in the dbscheme - // We should really define param_default(param, string) - // And use parameter child #0 to encode the default expression. - var defaultValue = GetParameterDefaultValue(Symbol); - 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 + if (symbol.ContainingSymbol is IMethodSymbol method) { - // 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 is not null) - { - var i = method.Parameters.IndexOf(Symbol); - var indexer = (IPropertySymbol?)method.AssociatedSymbol; - if (indexer is not null) - defaultValue = GetParameterDefaultValue(indexer.Parameters[i]); - } - } - - if (defaultValue is not null) - { - Context.PopulateLater(() => - { - Expression.Create(Context, defaultValue.Value, this, 0); - }); + var i = method.Parameters.IndexOf(symbol); + if (method.AssociatedSymbol is IPropertySymbol indexer) + defaultValue = GetParameterDefaultValue(indexer.Parameters[i]); } } + + return defaultValue; } public override bool IsSourceDeclaration => Symbol.IsSourceDeclaration(); @@ -205,7 +196,7 @@ namespace Semmle.Extraction.CSharp.Entities public override bool NeedsPopulation => true; - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write("__arglist;type"); } @@ -266,33 +257,4 @@ namespace Semmle.Extraction.CSharp.Entities public override VarargsParam Create(Context cx, Method init) => new VarargsParam(cx, init); } } - - internal class ConstructedExtensionParameter : Parameter - { - private readonly ITypeSymbol constructedType; - - private ConstructedExtensionParameter(Context cx, Method method, Parameter original) - : base(cx, original.Symbol, method, original) - { - 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.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)); - - private class ExtensionParamFactory : CachedEntityFactory<(Method, Parameter), ConstructedExtensionParameter> - { - public static ExtensionParamFactory Instance { get; } = new ExtensionParamFactory(); - - 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/Property.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs index 5fb77cf4dc2..9e4a1c79ad2 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs @@ -21,7 +21,7 @@ namespace Semmle.Extraction.CSharp.Entities private Type Type => type.Value; - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(Type); trapFile.Write(" "); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs index 48381430428..18a98883c8b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/ArrayType.cs @@ -30,7 +30,7 @@ namespace Semmle.Extraction.CSharp.Entities PopulateType(trapFile); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(ElementType); Symbol.BuildArraySuffix(trapFile); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs index b43c4c741f4..8a28beaff7b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs @@ -22,7 +22,7 @@ namespace Semmle.Extraction.CSharp.Entities trapFile.parent_namespace(this, Namespace.Create(Context, Context.Compilation.GlobalNamespace)); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write("dynamic;type"); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/FunctionPointerType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/FunctionPointerType.cs index 2bb9cd3a1b6..4c3ab516172 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/FunctionPointerType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/FunctionPointerType.cs @@ -11,9 +11,9 @@ namespace Semmle.Extraction.CSharp.Entities { } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { - Symbol.BuildTypeId(Context, trapFile, Symbol); + Symbol.BuildTypeId(Context, trapFile, Symbol, constructUnderlyingTupleType: false); trapFile.Write(";functionpointertype"); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs index b607da5d998..01ca82b71ba 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs @@ -128,7 +128,7 @@ namespace Semmle.Extraction.CSharp.Entities private bool IsAnonymousType() => Symbol.IsAnonymousType || Symbol.Name.Contains("__AnonymousType"); - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { if (IsAnonymousType()) { @@ -141,7 +141,7 @@ namespace Semmle.Extraction.CSharp.Entities } } - public override void WriteQuotedId(TextWriter trapFile) + public sealed override void WriteQuotedId(EscapingTextWriter trapFile) { if (IsAnonymousType()) trapFile.Write('*'); @@ -195,7 +195,7 @@ namespace Semmle.Extraction.CSharp.Entities public override bool NeedsPopulation => true; - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(referencedType); trapFile.Write(";typeRef"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs index f65d7b17db6..6a049bc3939 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs @@ -13,7 +13,7 @@ namespace Semmle.Extraction.CSharp.Entities trapFile.types(this, Kinds.TypeKind.NULL, "null"); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write(";type"); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Nullability.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Nullability.cs index 3cd85bdbdde..c0ef8299eed 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Nullability.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Nullability.cs @@ -79,7 +79,7 @@ namespace Semmle.Extraction.CSharp.Entities return h; } - public void WriteId(TextWriter trapFile) + public void WriteId(EscapingTextWriter trapFile) { trapFile.Write(Annotation); trapFile.Write('('); @@ -90,7 +90,7 @@ namespace Semmle.Extraction.CSharp.Entities public override string ToString() { - using var w = new StringWriter(); + using var w = new EscapingTextWriter(); WriteId(w); return w.ToString(); } @@ -120,7 +120,7 @@ namespace Semmle.Extraction.CSharp.Entities } } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { Symbol.WriteId(trapFile); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs index 635172a0e62..c4f8aa74245 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/PointerType.cs @@ -11,7 +11,7 @@ namespace Semmle.Extraction.CSharp.Entities PointedAtType = Create(cx, Symbol.PointedAtType); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.WriteSubId(PointedAtType); trapFile.Write("*;type"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs index ca3e182a99d..56db07671d7 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs @@ -30,9 +30,9 @@ namespace Semmle.Extraction.CSharp.Entities // All tuple types are "local types" public override bool NeedsPopulation => true; - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { - Symbol.BuildTypeId(Context, trapFile, Symbol); + Symbol.BuildTypeId(Context, trapFile, Symbol, constructUnderlyingTupleType: false); trapFile.Write(";tuple"); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs index e2522648678..67936f9a913 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs @@ -81,22 +81,45 @@ namespace Semmle.Extraction.CSharp.Entities Symbol.BuildDisplayName(Context, trapFile, constructUnderlyingTupleType); trapFile.WriteLine("\")"); + var baseTypes = GetBaseTypeDeclarations(); + // Visit base types - var baseTypes = new List(); if (Symbol.GetNonObjectBaseType(Context) is INamedTypeSymbol @base) { - var baseKey = Create(Context, @base); - trapFile.extend(this, baseKey.TypeRef); - if (Symbol.TypeKind != TypeKind.Struct) - baseTypes.Add(baseKey); + var bts = GetBaseTypeDeclarations(baseTypes, @base); + + Context.PopulateLater(() => + { + var baseKey = Create(Context, @base); + trapFile.extend(this, baseKey.TypeRef); + + if (Symbol.TypeKind != TypeKind.Struct) + { + foreach (var bt in bts) + { + TypeMention.Create(Context, bt.Type, this, baseKey); + } + } + }); } + // Visit implemented interfaces if (!(base.Symbol is IArrayTypeSymbol)) { - foreach (var t in base.Symbol.Interfaces.Select(i => Create(Context, i))) + foreach (var i in base.Symbol.Interfaces) { - trapFile.implement(this, t.TypeRef); - baseTypes.Add(t); + var bts = GetBaseTypeDeclarations(baseTypes, i); + + Context.PopulateLater(() => + { + var interfaceKey = Create(Context, i); + trapFile.implement(this, interfaceKey.TypeRef); + + foreach (var bt in bts) + { + TypeMention.Create(Context, bt.Type, this, interfaceKey); + } + }); } } @@ -145,23 +168,30 @@ namespace Semmle.Extraction.CSharp.Entities } Modifier.ExtractModifiers(Context, trapFile, this, Symbol); + } - if (IsSourceDeclaration && Symbol.FromSource()) + private IEnumerable GetBaseTypeDeclarations() + { + if (!IsSourceDeclaration || !Symbol.FromSource()) { - var declSyntaxReferences = Symbol.DeclaringSyntaxReferences.Select(d => d.GetSyntax()).ToArray(); - - var baseLists = declSyntaxReferences.OfType().Select(c => c.BaseList); - baseLists = baseLists.Concat(declSyntaxReferences.OfType().Select(c => c.BaseList)); - baseLists = baseLists.Concat(declSyntaxReferences.OfType().Select(c => c.BaseList)); - - baseLists - .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)) - .Enumerate(); + return Enumerable.Empty(); } + + var declSyntaxReferences = Symbol.DeclaringSyntaxReferences.Select(d => d.GetSyntax()).ToArray(); + + var baseLists = declSyntaxReferences.OfType().Select(c => c.BaseList); + baseLists = baseLists.Concat(declSyntaxReferences.OfType().Select(c => c.BaseList)); + baseLists = baseLists.Concat(declSyntaxReferences.OfType().Select(c => c.BaseList)); + + return baseLists + .Where(bl => bl is not null) + .SelectMany(bl => bl!.Types) + .ToList(); + } + + private IEnumerable GetBaseTypeDeclarations(IEnumerable baseTypes, INamedTypeSymbol type) + { + return baseTypes.Where(bt => SymbolEqualityComparer.Default.Equals(Context.GetModel(bt).GetTypeInfo(bt.Type).Type, type)); } private void ExtractParametersForDelegateLikeType(TextWriter trapFile, IMethodSymbol invokeMethod, Action storeReturnType) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs index dcddf5d5cf2..146b1905018 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs @@ -103,7 +103,7 @@ namespace Semmle.Extraction.CSharp.Entities } } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { string kind; IEntity containingEntity; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs index 1d5e5412a88..1dd6d817b01 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs @@ -11,6 +11,8 @@ using System.Diagnostics; using System.Threading.Tasks; using Semmle.Util.Logging; using System.Collections.Concurrent; +using System.Globalization; +using System.Threading; namespace Semmle.Extraction.CSharp { @@ -52,6 +54,21 @@ namespace Semmle.Extraction.CSharp public void MissingType(string type) { } } + /// + /// Set the application culture to the invariant culture. + /// + /// This is required among others to ensure that the invariant culture is used for value formatting during TRAP + /// file writing. + /// + public static void SetInvariantCulture() + { + var culture = CultureInfo.InvariantCulture; + CultureInfo.DefaultThreadCurrentCulture = culture; + CultureInfo.DefaultThreadCurrentUICulture = culture; + Thread.CurrentThread.CurrentCulture = culture; + Thread.CurrentThread.CurrentUICulture = culture; + } + /// /// Command-line driver for the extractor. /// @@ -399,9 +416,10 @@ namespace Semmle.Extraction.CSharp compilerArguments.CompilationName, syntaxTrees, references, - compilerArguments.CompilationOptions. - WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default). - WithStrongNameProvider(new DesktopStrongNameProvider(compilerArguments.KeyFileSearchPaths)) + compilerArguments.CompilationOptions + .WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default) + .WithStrongNameProvider(new DesktopStrongNameProvider(compilerArguments.KeyFileSearchPaths)) + .WithMetadataImportOptions(MetadataImportOptions.All) ); }, (compilation, options) => analyser.EndInitialize(compilerArguments, options, compilation), diff --git a/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs index f03f1674969..c3bc02bf480 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs @@ -121,8 +121,6 @@ namespace Semmle.Extraction.CSharp named = named.TupleUnderlyingType; if (IdDependsOnImpl(named.ContainingType)) return true; - if (IdDependsOnImpl(named.GetNonObjectBaseType(cx))) - return true; if (IdDependsOnImpl(named.ConstructedFrom)) return true; return named.TypeArguments.Any(IdDependsOnImpl); @@ -160,10 +158,7 @@ namespace Semmle.Extraction.CSharp /// The trap builder used to store the result. /// The outer symbol being defined (to avoid recursive ids). /// Whether to build a type ID for the underlying `System.ValueTuple` struct in the case of tuple types. - public static void BuildTypeId(this ITypeSymbol type, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType = false) => - type.BuildTypeId(cx, trapFile, symbolBeingDefined, true, constructUnderlyingTupleType); - - private static void BuildTypeId(this ITypeSymbol type, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType) + public static void BuildTypeId(this ITypeSymbol type, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType) { using (cx.StackGuard) { @@ -171,7 +166,7 @@ namespace Semmle.Extraction.CSharp { case TypeKind.Array: var array = (IArrayTypeSymbol)type; - array.ElementType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass); + array.ElementType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false); array.BuildArraySuffix(trapFile); return; case TypeKind.Class: @@ -181,16 +176,16 @@ namespace Semmle.Extraction.CSharp case TypeKind.Delegate: case TypeKind.Error: var named = (INamedTypeSymbol)type; - named.BuildNamedTypeId(cx, trapFile, symbolBeingDefined, addBaseClass, constructUnderlyingTupleType); + named.BuildNamedTypeId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType); return; case TypeKind.Pointer: var ptr = (IPointerTypeSymbol)type; - ptr.PointedAtType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass); + ptr.PointedAtType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false); trapFile.Write('*'); return; case TypeKind.TypeParameter: var tp = (ITypeParameterSymbol)type; - tp.ContainingSymbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass); + tp.ContainingSymbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false); trapFile.Write('_'); trapFile.Write(tp.Name); return; @@ -207,7 +202,7 @@ 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, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType) { if (symbol is null) { @@ -232,7 +227,7 @@ namespace Semmle.Extraction.CSharp if (SymbolEqualityComparer.Default.Equals(symbol, symbolBeingDefined)) trapFile.Write("__self__"); else if (symbol is ITypeSymbol type && type.IdDependsOn(cx, symbolBeingDefined)) - type.BuildTypeId(cx, trapFile, symbolBeingDefined, addBaseClass, constructUnderlyingTupleType); + type.BuildTypeId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType); else if (symbol is INamedTypeSymbol namedType && namedType.IsTupleType && constructUnderlyingTupleType) trapFile.WriteSubId(NamedType.CreateNamedTypeFromTupleType(cx, namedType)); else @@ -249,8 +244,8 @@ 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) => - symbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, true); + public static void BuildOrWriteId(this ISymbol? symbol, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined) => + symbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false); /// /// Constructs an array suffix string for this array type symbol. @@ -264,7 +259,7 @@ namespace Semmle.Extraction.CSharp trapFile.Write(']'); } - private static void BuildAssembly(IAssemblySymbol asm, TextWriter trapFile, bool extraPrecise = false) + private static void BuildAssembly(IAssemblySymbol asm, EscapingTextWriter trapFile, bool extraPrecise = false) { var assembly = asm.Identity; trapFile.Write(assembly.Name); @@ -282,22 +277,22 @@ namespace Semmle.Extraction.CSharp trapFile.Write("::"); } - private static void BuildFunctionPointerTypeId(this IFunctionPointerTypeSymbol funptr, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined) + private static void BuildFunctionPointerTypeId(this IFunctionPointerTypeSymbol funptr, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined) { - BuildFunctionPointerSignature(funptr, trapFile, (s, tw) => s.BuildOrWriteId(cx, tw, symbolBeingDefined)); + BuildFunctionPointerSignature(funptr, trapFile, s => s.BuildOrWriteId(cx, trapFile, symbolBeingDefined)); } - private static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, TextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType) + private static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType) { if (!constructUnderlyingTupleType && named.IsTupleType) { trapFile.Write('('); trapFile.BuildList(",", named.TupleElements, - (f, tb0) => + f => { trapFile.Write((f.CorrespondingTupleField ?? f).Name); trapFile.Write(":"); - f.Type.BuildOrWriteId(cx, tb0, symbolBeingDefined, addBaseClass); + f.Type.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false); } ); trapFile.Write(")"); @@ -308,7 +303,7 @@ namespace Semmle.Extraction.CSharp { if (named.ContainingType is not null) { - named.ContainingType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass); + named.ContainingType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false); trapFile.Write('.'); } else if (named.ContainingNamespace is not null) @@ -333,38 +328,20 @@ namespace Semmle.Extraction.CSharp } else { - named.ConstructedFrom.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass, constructUnderlyingTupleType); + named.ConstructedFrom.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType); trapFile.Write('<'); // Encode the nullability of the type arguments in the label. // Type arguments with different nullability can result in // a constructed type with different nullability of its members and methods, // so we need to create a distinct database entity for it. trapFile.BuildList(",", named.GetAnnotatedTypeArguments(), - (ta, tb0) => ta.Symbol.BuildOrWriteId(cx, tb0, symbolBeingDefined, addBaseClass) + ta => ta.Symbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false) ); trapFile.Write('>'); } - - if (addBaseClass && named.GetNonObjectBaseType(cx) is INamedTypeSymbol @base) - { - // We need to limit unfolding of base classes. For example, in - // - // ```csharp - // class C1 { } - // class C2 : C1> { } - // class C3 : C1> { } - // class C4 : C3 { } - // ``` - // - // when we generate the label for `C4`, the base class `C3` has itself `C1>` as - // a base class, which in turn has `C1>` as a base class. The latter has the original - // base class `C3` as a type argument, which would lead to infinite unfolding. - trapFile.Write(" : "); - @base.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass: false); - } } - private static void BuildNamespace(this INamespaceSymbol ns, Context cx, TextWriter trapFile) + private static void BuildNamespace(this INamespaceSymbol ns, Context cx, EscapingTextWriter trapFile) { trapFile.WriteSubId(Namespace.Create(cx, ns)); trapFile.Write('.'); @@ -377,7 +354,7 @@ namespace Semmle.Extraction.CSharp trapFile.Write("<>__AnonType"); trapFile.Write(hackTypeNumber); trapFile.Write('<'); - trapFile.BuildList(",", type.GetMembers().OfType(), (prop, tb0) => BuildDisplayName(prop.Type, cx, tb0)); + trapFile.BuildList(",", type.GetMembers().OfType(), prop => BuildDisplayName(prop.Type, cx, trapFile)); trapFile.Write('>'); } @@ -396,7 +373,7 @@ namespace Semmle.Extraction.CSharp var elementType = array.ElementType; if (elementType.MetadataName.Contains("`")) { - trapFile.Write(elementType.Name); + trapFile.Write(TrapExtensions.EncodeString(elementType.Name)); return; } elementType.BuildDisplayName(cx, trapFile); @@ -433,7 +410,7 @@ namespace Semmle.Extraction.CSharp } public static void BuildFunctionPointerSignature(IFunctionPointerTypeSymbol funptr, TextWriter trapFile, - Action buildNested) + Action buildNested) { trapFile.Write("delegate* "); trapFile.Write(funptr.Signature.CallingConvention.ToString().ToLowerInvariant()); @@ -447,19 +424,19 @@ namespace Semmle.Extraction.CSharp trapFile.Write('<'); trapFile.BuildList(",", funptr.Signature.Parameters, - (p, trap) => + p => { - buildNested(p.Type, trap); + buildNested(p.Type); switch (p.RefKind) { case RefKind.Out: - trap.Write(" out"); + trapFile.Write(" out"); break; case RefKind.In: - trap.Write(" in"); + trapFile.Write(" in"); break; case RefKind.Ref: - trap.Write(" ref"); + trapFile.Write(" ref"); break; } }); @@ -469,7 +446,7 @@ namespace Semmle.Extraction.CSharp trapFile.Write(","); } - buildNested(funptr.Signature.ReturnType, trapFile); + buildNested(funptr.Signature.ReturnType); if (funptr.Signature.ReturnsByRef) trapFile.Write(" ref"); @@ -481,7 +458,7 @@ namespace Semmle.Extraction.CSharp private static void BuildFunctionPointerTypeDisplayName(this IFunctionPointerTypeSymbol funptr, Context cx, TextWriter trapFile) { - BuildFunctionPointerSignature(funptr, trapFile, (s, tw) => s.BuildDisplayName(cx, tw)); + BuildFunctionPointerSignature(funptr, trapFile, s => s.BuildDisplayName(cx, trapFile)); } private static void BuildNamedTypeDisplayName(this INamedTypeSymbol namedType, Context cx, TextWriter trapFile, bool constructUnderlyingTupleType) @@ -492,7 +469,7 @@ namespace Semmle.Extraction.CSharp trapFile.BuildList( ",", namedType.TupleElements.Select(f => f.Type), - (t, tb0) => t.BuildDisplayName(cx, tb0)); + t => t.BuildDisplayName(cx, trapFile)); trapFile.Write(")"); return; } @@ -503,7 +480,7 @@ namespace Semmle.Extraction.CSharp } else { - trapFile.Write(namedType.Name); + trapFile.Write(TrapExtensions.EncodeString(namedType.Name)); } if (namedType.IsGenericType && namedType.TypeKind != TypeKind.Error && namedType.TypeArguments.Any()) @@ -512,11 +489,11 @@ namespace Semmle.Extraction.CSharp trapFile.BuildList( ",", namedType.TypeArguments, - (p, tb0) => + p => { if (IsReallyBound(namedType)) { - p.BuildDisplayName(cx, tb0); + p.BuildDisplayName(cx, trapFile); } }); trapFile.Write('>'); diff --git a/csharp/extractor/Semmle.Extraction/Context.cs b/csharp/extractor/Semmle.Extraction/Context.cs index c0580db96a7..410b3c6980a 100644 --- a/csharp/extractor/Semmle.Extraction/Context.cs +++ b/csharp/extractor/Semmle.Extraction/Context.cs @@ -35,12 +35,14 @@ namespace Semmle.Extraction // A recursion guard against writing to the trap file whilst writing an id to the trap file. private bool writingLabel = false; + private readonly Queue labelQueue = new(); + protected void DefineLabel(IEntity entity) { if (writingLabel) { // Don't define a label whilst writing a label. - PopulateLater(() => DefineLabel(entity)); + labelQueue.Enqueue(entity); } else { @@ -52,6 +54,10 @@ namespace Semmle.Extraction finally { writingLabel = false; + if (labelQueue.Any()) + { + DefineLabel(labelQueue.Dequeue()); + } } } } @@ -108,7 +114,7 @@ namespace Semmle.Extraction Populate(init as ISymbol, entity); #if DEBUG_LABELS - using var id = new StringWriter(); + using var id = new EscapingTextWriter(); entity.WriteQuotedId(id); CheckEntityHasUniqueLabel(id.ToString(), entity); #endif @@ -376,6 +382,19 @@ namespace Semmle.Extraction Extractor.Message(msg); } + private void ExtractionError(InternalError error) + { + ExtractionError(new Message(error.Message, error.EntityText, CreateLocation(error.Location), error.StackTrace, Severity.Error)); + } + + private void ReportError(InternalError error) + { + if (!Extractor.Standalone) + throw error; + + ExtractionError(error); + } + /// /// Signal an error in the program model. /// @@ -383,8 +402,7 @@ namespace Semmle.Extraction /// The error message. public void ModelError(SyntaxNode node, string msg) { - if (!Extractor.Standalone) - throw new InternalError(node, msg); + ReportError(new InternalError(node, msg)); } /// @@ -394,8 +412,7 @@ namespace Semmle.Extraction /// The error message. public void ModelError(ISymbol symbol, string msg) { - if (!Extractor.Standalone) - throw new InternalError(symbol, msg); + ReportError(new InternalError(symbol, msg)); } /// @@ -404,8 +421,7 @@ namespace Semmle.Extraction /// The error message. public void ModelError(string msg) { - if (!Extractor.Standalone) - throw new InternalError(msg); + ReportError(new InternalError(msg)); } /// diff --git a/csharp/extractor/Semmle.Extraction/Entities/Base/Entity.cs b/csharp/extractor/Semmle.Extraction/Entities/Base/Entity.cs index 1d5081df2e5..c5d630bc7b1 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/Base/Entity.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/Base/Entity.cs @@ -15,9 +15,14 @@ namespace Semmle.Extraction public Label Label { get; set; } - public abstract void WriteId(TextWriter trapFile); + public abstract void WriteId(EscapingTextWriter trapFile); - public abstract void WriteQuotedId(TextWriter trapFile); + public virtual void WriteQuotedId(EscapingTextWriter trapFile) + { + trapFile.WriteUnescaped("@\""); + WriteId(trapFile); + trapFile.WriteUnescaped('\"'); + } public abstract Location? ReportingLocation { get; } @@ -27,9 +32,10 @@ namespace Semmle.Extraction { trapFile.WriteLabel(this); trapFile.Write("="); + using var escaping = new EscapingTextWriter(trapFile); try { - WriteQuotedId(trapFile); + WriteQuotedId(escaping); } catch (Exception ex) // lgtm[cs/catch-of-all-exceptions] { @@ -51,7 +57,7 @@ namespace Semmle.Extraction /// public string GetDebugLabel() { - using var writer = new StringWriter(); + using var writer = new EscapingTextWriter(); writer.WriteLabel(Label.Value); writer.Write('='); WriteQuotedId(writer); diff --git a/csharp/extractor/Semmle.Extraction/Entities/Base/IEntity.cs b/csharp/extractor/Semmle.Extraction/Entities/Base/IEntity.cs index 5aadbeb9e4d..dcf8dcbc373 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/Base/IEntity.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/Base/IEntity.cs @@ -29,14 +29,14 @@ namespace Semmle.Extraction /// Writes the unique identifier of this entitiy to a trap file. /// /// The trapfile to write to. - void WriteId(TextWriter trapFile); + void WriteId(EscapingTextWriter trapFile); /// /// Writes the quoted identifier of this entity, /// which could be @"..." or * /// /// The trapfile to write to. - void WriteQuotedId(TextWriter trapFile); + void WriteQuotedId(EscapingTextWriter trapFile); /// /// The location for reporting purposes. diff --git a/csharp/extractor/Semmle.Extraction/Entities/Base/LabelledEntity.cs b/csharp/extractor/Semmle.Extraction/Entities/Base/LabelledEntity.cs index 12a98ce2303..62d9cbd64be 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/Base/LabelledEntity.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/Base/LabelledEntity.cs @@ -7,12 +7,5 @@ namespace Semmle.Extraction protected LabelledEntity(Context cx) : base(cx) { } - - public override void WriteQuotedId(TextWriter trapFile) - { - trapFile.Write("@\""); - WriteId(trapFile); - trapFile.Write('\"'); - } } } diff --git a/csharp/extractor/Semmle.Extraction/Entities/Base/UnlabelledEntity.cs b/csharp/extractor/Semmle.Extraction/Entities/Base/UnlabelledEntity.cs index 6b6a22eb4b5..506a84bf7ad 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/Base/UnlabelledEntity.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/Base/UnlabelledEntity.cs @@ -9,14 +9,14 @@ namespace Semmle.Extraction cx.AddFreshLabel(this); } - public sealed override void WriteId(TextWriter writer) + public sealed override void WriteId(EscapingTextWriter writer) { writer.Write('*'); } - public sealed override void WriteQuotedId(TextWriter writer) + public sealed override void WriteQuotedId(EscapingTextWriter writer) { - WriteId(writer); + writer.Write('*'); } } } diff --git a/csharp/extractor/Semmle.Extraction/Entities/File.cs b/csharp/extractor/Semmle.Extraction/Entities/File.cs index ed8881f0d2f..11d37c99636 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/File.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/File.cs @@ -17,7 +17,7 @@ namespace Semmle.Extraction.Entities public override bool NeedsPopulation => true; - public override void WriteId(System.IO.TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write(TransformedPath.DatabaseId); trapFile.Write(";sourcefile"); diff --git a/csharp/extractor/Semmle.Extraction/Entities/Folder.cs b/csharp/extractor/Semmle.Extraction/Entities/Folder.cs index 82364c987e7..07e8c805e7f 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/Folder.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/Folder.cs @@ -15,7 +15,7 @@ namespace Semmle.Extraction.Entities public override bool NeedsPopulation => true; - public override void WriteId(System.IO.TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write(Symbol.DatabaseId); trapFile.Write(";folder"); diff --git a/csharp/extractor/Semmle.Extraction/Entities/GeneratedFile.cs b/csharp/extractor/Semmle.Extraction/Entities/GeneratedFile.cs index db458e574a5..7c0e5df7be9 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/GeneratedFile.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/GeneratedFile.cs @@ -13,7 +13,7 @@ namespace Semmle.Extraction.Entities trapFile.files(this, "", "", ""); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write("GENERATED;sourcefile"); } diff --git a/csharp/extractor/Semmle.Extraction/Entities/GeneratedLocation.cs b/csharp/extractor/Semmle.Extraction/Entities/GeneratedLocation.cs index 331e70262dc..db552f7e452 100644 --- a/csharp/extractor/Semmle.Extraction/Entities/GeneratedLocation.cs +++ b/csharp/extractor/Semmle.Extraction/Entities/GeneratedLocation.cs @@ -17,7 +17,7 @@ namespace Semmle.Extraction.Entities trapFile.locations_default(this, generatedFile, 0, 0, 0, 0); } - public override void WriteId(TextWriter trapFile) + public override void WriteId(EscapingTextWriter trapFile) { trapFile.Write("loc,"); trapFile.WriteSubId(generatedFile); diff --git a/csharp/extractor/Semmle.Extraction/EscapingTextWriter.cs b/csharp/extractor/Semmle.Extraction/EscapingTextWriter.cs new file mode 100644 index 00000000000..6294ec3ffd3 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction/EscapingTextWriter.cs @@ -0,0 +1,340 @@ +using System; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Semmle.Extraction +{ + /// + /// A `TextWriter` object that wraps another `TextWriter` object, and which + /// HTML escapes the characters `&`, `{`, `}`, `"`, `@`, and `#`, before + /// writing to the underlying object. + /// + public sealed class EscapingTextWriter : TextWriter + { + private readonly TextWriter wrapped; + private readonly bool disposeUnderlying; + + public EscapingTextWriter(TextWriter wrapped, bool disposeUnderlying = false) + { + this.wrapped = wrapped; + this.disposeUnderlying = disposeUnderlying; + } + + /// + /// Creates a new instance with a new underlying `StringWriter` object. The + /// underlying object is disposed of when this object is. + /// + public EscapingTextWriter() : this(new StringWriter(), true) { } + + public EscapingTextWriter(IFormatProvider? formatProvider) : base(formatProvider) + => throw new NotImplementedException(); + + private void WriteEscaped(char c) + { + switch (c) + { + case '&': + wrapped.Write("&"); + break; + case '{': + wrapped.Write("{"); + break; + case '}': + wrapped.Write("}"); + break; + case '"': + wrapped.Write("""); + break; + case '@': + wrapped.Write("@"); + break; + case '#': + wrapped.Write("#"); + break; + default: + wrapped.Write(c); + break; + }; + } + + public void WriteSubId(IEntity entity) + { + if (entity is null) + { + wrapped.Write(""); + return; + } + + WriteUnescaped('{'); + wrapped.WriteLabel(entity); + WriteUnescaped('}'); + } + + public void WriteUnescaped(char c) + => wrapped.Write(c); + + public void WriteUnescaped(string s) + => wrapped.Write(s); + + #region overrides + + public override Encoding Encoding => wrapped.Encoding; + + public override IFormatProvider FormatProvider => wrapped.FormatProvider; + + public override string NewLine { get => wrapped.NewLine; } + + public override void Close() + => throw new NotImplementedException(); + + public override ValueTask DisposeAsync() + => throw new NotImplementedException(); + + public override bool Equals(object? obj) + => wrapped.Equals(obj) && obj is EscapingTextWriter other && disposeUnderlying == other.disposeUnderlying; + + public override void Flush() + => wrapped.Flush(); + + public override Task FlushAsync() + => wrapped.FlushAsync(); + + public override int GetHashCode() + => HashCode.Combine(wrapped, disposeUnderlying); + + public override string ToString() + => wrapped.ToString() ?? ""; + + public override void Write(bool value) + => wrapped.Write(value); + + public override void Write(char value) + => WriteEscaped(value); + + public override void Write(char[]? buffer) + { + if (buffer is null) + return; + Write(buffer, 0, buffer.Length); + } + + public override void Write(char[] buffer, int index, int count) + { + for (var i = index; i < buffer.Length && i < index + count; i++) + { + WriteEscaped(buffer[i]); + } + } + + + public override void Write(decimal value) + => wrapped.Write(value); + + public override void Write(double value) + => wrapped.Write(value); + + public override void Write(int value) + => wrapped.Write(value); + + public override void Write(long value) + => wrapped.Write(value); + + public override void Write(object? value) + => Write(value?.ToString()); + + public override void Write(ReadOnlySpan buffer) + { + foreach (var c in buffer) + { + WriteEscaped(c); + } + } + + public override void Write(float value) + => wrapped.Write(value); + + public override void Write(string? value) + { + if (value is null) + { + wrapped.Write(value); + } + else + { + foreach (var c in value) + { + WriteEscaped(c); + } + } + } + + public override void Write(string format, object? arg0) + => Write(string.Format(format, arg0)); + + public override void Write(string format, object? arg0, object? arg1) + => Write(string.Format(format, arg0, arg1)); + + public override void Write(string format, object? arg0, object? arg1, object? arg2) + => Write(string.Format(format, arg0, arg1, arg2)); + + public override void Write(string format, params object?[] arg) + => Write(string.Format(format, arg)); + + public override void Write(StringBuilder? value) + { + if (value is null) + { + wrapped.Write(value); + } + else + { + for (var i = 0; i < value.Length; i++) + { + WriteEscaped(value[i]); + } + } + } + + public override void Write(uint value) + => wrapped.Write(value); + + public override void Write(ulong value) + => wrapped.Write(value); + + public override Task WriteAsync(char value) + => throw new NotImplementedException(); + + public override Task WriteAsync(char[] buffer, int index, int count) + => throw new NotImplementedException(); + + public override Task WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + => throw new NotImplementedException(); + + public override Task WriteAsync(string? value) + => throw new NotImplementedException(); + + public override Task WriteAsync(StringBuilder? value, CancellationToken cancellationToken = default) + => throw new NotImplementedException(); + + public override void WriteLine() + => wrapped.WriteLine(); + + public override void WriteLine(bool value) + => wrapped.WriteLine(value); + + public override void WriteLine(char value) + { + Write(value); + WriteLine(); + } + + public override void WriteLine(char[]? buffer) + { + Write(buffer); + WriteLine(); + } + + public override void WriteLine(char[] buffer, int index, int count) + { + Write(buffer, index, count); + WriteLine(); + } + + public override void WriteLine(decimal value) + => wrapped.WriteLine(value); + + public override void WriteLine(double value) + => wrapped.WriteLine(value); + + public override void WriteLine(int value) + => wrapped.WriteLine(value); + + public override void WriteLine(long value) + => wrapped.WriteLine(value); + + public override void WriteLine(object? value) + { + Write(value); + WriteLine(); + } + + public override void WriteLine(ReadOnlySpan buffer) + { + Write(buffer); + WriteLine(); + } + + public override void WriteLine(float value) + => wrapped.WriteLine(value); + + public override void WriteLine(string? value) + { + Write(value); + WriteLine(); + } + + public override void WriteLine(string format, object? arg0) + { + Write(format, arg0); + WriteLine(); + } + + public override void WriteLine(string format, object? arg0, object? arg1) + { + Write(format, arg0, arg1); + WriteLine(); + } + + public override void WriteLine(string format, object? arg0, object? arg1, object? arg2) + { + Write(format, arg0, arg1, arg2); + WriteLine(); + } + + public override void WriteLine(string format, params object?[] arg) + { + Write(format, arg); + WriteLine(); + } + + public override void WriteLine(StringBuilder? value) + { + Write(value); + WriteLine(); + } + + public override void WriteLine(uint value) + => wrapped.WriteLine(value); + + public override void WriteLine(ulong value) + => wrapped.WriteLine(value); + + public override Task WriteLineAsync() + => throw new NotImplementedException(); + + public override Task WriteLineAsync(char value) + => throw new NotImplementedException(); + + public override Task WriteLineAsync(char[] buffer, int index, int count) + => throw new NotImplementedException(); + + public override Task WriteLineAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + => throw new NotImplementedException(); + + public override Task WriteLineAsync(string? value) + => throw new NotImplementedException(); + + public override Task WriteLineAsync(StringBuilder? value, CancellationToken cancellationToken = default) + => throw new NotImplementedException(); + + protected override void Dispose(bool disposing) + { + if (disposing && disposeUnderlying) + wrapped.Dispose(); + } + + #endregion overrides + } +} diff --git a/csharp/extractor/Semmle.Extraction/TrapExtensions.cs b/csharp/extractor/Semmle.Extraction/TrapExtensions.cs index 11111467147..e1343019335 100644 --- a/csharp/extractor/Semmle.Extraction/TrapExtensions.cs +++ b/csharp/extractor/Semmle.Extraction/TrapExtensions.cs @@ -17,19 +17,6 @@ namespace Semmle.Extraction trapFile.WriteLabel(entity.Label.Value); } - public static void WriteSubId(this TextWriter trapFile, IEntity entity) - { - if (entity is null) - { - trapFile.Write(""); - return; - } - - trapFile.Write('{'); - trapFile.WriteLabel(entity); - trapFile.Write('}'); - } - public static void WriteSeparator(this TextWriter trapFile, string separator, ref int index) { if (index++ > 0) @@ -130,7 +117,7 @@ namespace Semmle.Extraction return s; } - private static string EncodeString(string s) => s.Replace("\"", "\"\""); + public static string EncodeString(string s) => s.Replace("\"", "\"\""); /// /// Output a string to the trap file, such that the encoded output does not exceed @@ -231,9 +218,9 @@ namespace Semmle.Extraction /// The separator string (e.g. ",") /// The list of items. /// The original trap builder (fluent interface). - public static TextWriter AppendList(this TextWriter trapFile, string separator, IEnumerable items) where T : IEntity + public static TextWriter AppendList(this EscapingTextWriter trapFile, string separator, IEnumerable items) where T : IEntity { - return trapFile.BuildList(separator, items, (x, tb0) => { tb0.WriteSubId(x); }); + return trapFile.BuildList(separator, items, x => trapFile.WriteSubId(x)); } /// @@ -245,7 +232,8 @@ namespace Semmle.Extraction /// The list of items. /// The action on each item. /// The original trap builder (fluent interface). - public static TextWriter BuildList(this TextWriter trapFile, string separator, IEnumerable items, Action action) + public static T1 BuildList(this T1 trapFile, string separator, IEnumerable items, Action action) + where T1 : TextWriter { var first = true; foreach (var item in items) @@ -254,7 +242,7 @@ namespace Semmle.Extraction first = false; else trapFile.Write(separator); - action(item, trapFile); + action(item); } return trapFile; } diff --git a/csharp/ql/src/API Abuse/DisposeNotCalledOnException.ql b/csharp/ql/src/API Abuse/DisposeNotCalledOnException.ql index 7469cbc59e7..abb962449b9 100644 --- a/csharp/ql/src/API Abuse/DisposeNotCalledOnException.ql +++ b/csharp/ql/src/API Abuse/DisposeNotCalledOnException.ql @@ -18,36 +18,6 @@ import csharp import Dispose import semmle.code.csharp.frameworks.System -/** - * Gets an exception type that may be thrown during the execution of method `m`. - * Assumes any exception may be thrown by library types. - */ -Class getAThrownException(Method m) { - m.fromLibrary() and - result = any(SystemExceptionClass sc) - or - exists(ControlFlowElement cfe | - cfe = any(ThrowElement te | result = te.getExpr().getType()) or - cfe = any(MethodCall mc | result = getAThrownException(mc.getARuntimeTarget())) - | - cfe.getEnclosingCallable() = m and - not isTriedAgainstException(cfe, result) - ) -} - -/** - * Holds if control flow element is tried against throwing an exception of type - * `ec`. - */ -pragma[noinline] -predicate isTriedAgainstException(ControlFlowElement cfe, ExceptionClass ec) { - (cfe instanceof ThrowElement or cfe instanceof MethodCall) and - exists(TryStmt ts | - ts.getATriedElement() = cfe and - exists(ts.getAnExceptionHandler(ec)) - ) -} - private class DisposeCall extends MethodCall { DisposeCall() { this.getTarget() instanceof DisposeMethod } } @@ -78,8 +48,17 @@ predicate disposeReachableFromDisposableCreation(DisposeCall disposeCall, Expr d reachesDisposeCall(disposeCall, DataFlow::exprNode(disposableCreation)) } -class MethodCallThatMayThrow extends MethodCall { - MethodCallThatMayThrow() { exists(getAThrownException(this.getARuntimeTarget())) } +/** + * Holds if control flow element is tried against throwing an exception of type + * `ec`. + */ +pragma[noinline] +predicate isTriedAgainstException(ControlFlowElement cfe, ExceptionClass ec) { + (cfe instanceof ThrowElement or cfe instanceof MethodCall) and + exists(TryStmt ts | + ts.getATriedElement() = cfe and + exists(ts.getAnExceptionHandler(ec)) + ) } ControlFlowElement getACatchOrFinallyClauseChild() { @@ -88,15 +67,71 @@ ControlFlowElement getACatchOrFinallyClauseChild() { result = getACatchOrFinallyClauseChild().getAChild() } -from DisposeCall disposeCall, Expr disposableCreation, MethodCallThatMayThrow callThatThrows -where +private predicate candidate(DisposeCall disposeCall, Call call, Expr disposableCreation) { disposeReachableFromDisposableCreation(disposeCall, disposableCreation) and // The dispose call is not, itself, within a dispose method. not disposeCall.getEnclosingCallable() instanceof DisposeMethod and // Dispose call not within a finally or catch block not getACatchOrFinallyClauseChild() = disposeCall and // At least one method call exists between the allocation and disposal that could throw - disposableCreation.getAReachableElement() = callThatThrows and - callThatThrows.getAReachableElement() = disposeCall + disposableCreation.getAReachableElement() = call and + call.getAReachableElement() = disposeCall +} + +private class RelevantMethod extends Method { + RelevantMethod() { + exists(Call call | + candidate(_, call, _) and + this = call.getARuntimeTarget() + ) + or + exists(RelevantMethod other | other.calls(this)) + } + + pragma[noinline] + private RelevantMethod callsNoTry() { + exists(MethodCall mc | + result = mc.getARuntimeTarget() and + not isTriedAgainstException(mc, _) and + mc.getEnclosingCallable() = this + ) + } + + pragma[noinline] + private RelevantMethod callsInTry(MethodCall mc) { + result = mc.getARuntimeTarget() and + isTriedAgainstException(mc, _) and + mc.getEnclosingCallable() = this + } + + /** + * Gets an exception type that may be thrown during the execution of this method. + * Assumes any exception may be thrown by library types. + */ + Class getAThrownException() { + this.fromLibrary() and + result instanceof SystemExceptionClass + or + exists(ControlFlowElement cfe | + result = cfe.(ThrowElement).getExpr().getType() and + cfe.getEnclosingCallable() = this + or + result = this.callsInTry(cfe).getAThrownException() + | + not isTriedAgainstException(cfe, result) + ) + or + result = this.callsNoTry().getAThrownException() + } +} + +class MethodCallThatMayThrow extends MethodCall { + MethodCallThatMayThrow() { + exists(this.getARuntimeTarget().(RelevantMethod).getAThrownException()) + } +} + +from DisposeCall disposeCall, Expr disposableCreation, MethodCallThatMayThrow callThatThrows +where candidate(disposeCall, callThatThrows, disposableCreation) select disposeCall, "Dispose missed if exception is thrown by $@.", callThatThrows, callThatThrows.toString() diff --git a/csharp/ql/src/Bad Practices/UseOfHtmlInputHidden.ql b/csharp/ql/src/Bad Practices/UseOfHtmlInputHidden.ql index 93c3850e103..09ee699163f 100644 --- a/csharp/ql/src/Bad Practices/UseOfHtmlInputHidden.ql +++ b/csharp/ql/src/Bad Practices/UseOfHtmlInputHidden.ql @@ -3,6 +3,7 @@ * @description Finds uses of hidden fields on forms * @kind problem * @problem.severity recommendation + * @security-severity 6.4 * @precision medium * @id cs/web/html-hidden-input * @tags security diff --git a/csharp/ql/src/Configuration/EmptyPasswordInConfigurationFile.ql b/csharp/ql/src/Configuration/EmptyPasswordInConfigurationFile.ql index 315e5e084bd..e7dee2143c1 100644 --- a/csharp/ql/src/Configuration/EmptyPasswordInConfigurationFile.ql +++ b/csharp/ql/src/Configuration/EmptyPasswordInConfigurationFile.ql @@ -3,6 +3,7 @@ * @description Finds empty passwords in configuration files. * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision medium * @id cs/empty-password-in-configuration * @tags security diff --git a/csharp/ql/src/Configuration/PasswordInConfigurationFile.ql b/csharp/ql/src/Configuration/PasswordInConfigurationFile.ql index 85c332345d6..eb4756ea962 100644 --- a/csharp/ql/src/Configuration/PasswordInConfigurationFile.ql +++ b/csharp/ql/src/Configuration/PasswordInConfigurationFile.ql @@ -3,6 +3,7 @@ * @description Finds passwords in configuration files. * @kind problem * @problem.severity warning + * @security-severity 3.6 * @precision medium * @id cs/password-in-configuration * @tags security diff --git a/csharp/ql/src/Customizations.qll b/csharp/ql/src/Customizations.qll new file mode 100644 index 00000000000..d5378a44f0d --- /dev/null +++ b/csharp/ql/src/Customizations.qll @@ -0,0 +1,12 @@ +/** + * Contains customizations to the standard library. + * + * This module is imported by `csharp.qll`, so any customizations defined here automatically + * apply to all queries. + * + * Typical examples of customizations include adding new subclasses of abstract classes such as + * the `RemoteFlowSource` and `SummarizedCallable` classes associated with the security queries + * to model frameworks that are not covered by the standard library. + */ + +import csharp diff --git a/csharp/ql/src/Diagnostics/DiagnosticExtractionErrors.ql b/csharp/ql/src/Diagnostics/DiagnosticExtractionErrors.ql new file mode 100644 index 00000000000..23943d8491f --- /dev/null +++ b/csharp/ql/src/Diagnostics/DiagnosticExtractionErrors.ql @@ -0,0 +1,63 @@ +/** + * @name Extraction errors + * @description List all errors reported by the extractor or the compiler. Extractor errors are + * limited to those files where there are no compilation errors. + * @kind diagnostic + * @id cs/diagnostics/extraction-errors + */ + +import csharp +import semmle.code.csharp.commons.Diagnostics + +private newtype TDiagnosticError = + TCompilerError(CompilerError c) or + TExtractorError(ExtractorError e) + +abstract private class DiagnosticError extends TDiagnosticError { + abstract string getMessage(); + + abstract string toString(); + + abstract Location getLocation(); + + string getLocationMessage() { + if getLocation().getFile().fromSource() + then result = " in " + getLocation().getFile() + else result = "" + } +} + +private class DiagnosticCompilerError extends DiagnosticError { + CompilerError c; + + DiagnosticCompilerError() { this = TCompilerError(c) } + + override string getMessage() { + result = "Compiler error" + this.getLocationMessage() + ": " + c.getMessage() + } + + override string toString() { result = c.toString() } + + override Location getLocation() { result = c.getLocation() } +} + +private class DiagnosticExtractorError extends DiagnosticError { + ExtractorError e; + + DiagnosticExtractorError() { + this = TExtractorError(e) and + not exists(CompilerError ce | ce.getLocation().getFile() = e.getLocation().getFile()) + } + + override string getMessage() { + result = + "Unexpected " + e.getOrigin() + " error" + this.getLocationMessage() + ": " + e.getText() + } + + override string toString() { result = e.toString() } + + override Location getLocation() { result = e.getLocation() } +} + +from DiagnosticError error +select error.getMessage(), 2 diff --git a/csharp/ql/src/Diagnostics/DiagnosticNoExtractionErrors.ql b/csharp/ql/src/Diagnostics/DiagnosticNoExtractionErrors.ql new file mode 100644 index 00000000000..cb86e293efc --- /dev/null +++ b/csharp/ql/src/Diagnostics/DiagnosticNoExtractionErrors.ql @@ -0,0 +1,17 @@ +/** + * @name Successfully extracted files + * @description A list of all files in the source code directory that were extracted + * without encountering an extraction or compiler error in the file. + * @kind diagnostic + * @id cs/diagnostics/successfully-extracted-files + */ + +import csharp +import semmle.code.csharp.commons.Diagnostics + +from File file +where + file.fromSource() and + not exists(ExtractorError e | e.getLocation().getFile() = file) and + not exists(CompilerError e | e.getLocation().getFile() = file) +select file, "" diff --git a/csharp/ql/src/Input Validation/UseOfFileUpload.ql b/csharp/ql/src/Input Validation/UseOfFileUpload.ql index f3a3b0aeffa..4eb96e2c072 100644 --- a/csharp/ql/src/Input Validation/UseOfFileUpload.ql +++ b/csharp/ql/src/Input Validation/UseOfFileUpload.ql @@ -3,6 +3,7 @@ * @description Finds uses of file upload * @kind problem * @problem.severity recommendation + * @security-severity 5.9 * @precision high * @id cs/web/file-upload * @tags security diff --git a/csharp/ql/src/Language Abuse/ForeachCapture.qhelp b/csharp/ql/src/Language Abuse/ForeachCapture.qhelp index da966c4d967..516ed43cbf4 100644 --- a/csharp/ql/src/Language Abuse/ForeachCapture.qhelp +++ b/csharp/ql/src/Language Abuse/ForeachCapture.qhelp @@ -29,7 +29,7 @@ However on older compilers, the actual output is 10 10 10 10 10 10 10 10 1 -
  • Eric Lippert's Blog: Closing over the loop variable considered harmful.
  • +
  • Eric Lippert's Blog: Closing over the loop variable considered harmful.
  • diff --git a/csharp/ql/src/Likely Bugs/MishandlingJapaneseEra.qhelp b/csharp/ql/src/Likely Bugs/MishandlingJapaneseEra.qhelp index ee484d49d46..a2000deb2d5 100644 --- a/csharp/ql/src/Likely Bugs/MishandlingJapaneseEra.qhelp +++ b/csharp/ql/src/Likely Bugs/MishandlingJapaneseEra.qhelp @@ -27,7 +27,7 @@ The Japanese Calendar's Y2K Moment.
  • - Era Handling for the Japanese Calendar. + Era Handling for the Japanese Calendar.
  • List of Japanese Eras (Wikipedia) diff --git a/csharp/ql/src/Likely Bugs/ReferenceEqualsOnValueTypes.qhelp b/csharp/ql/src/Likely Bugs/ReferenceEqualsOnValueTypes.qhelp index ede1d57f1cf..96feb557d3b 100644 --- a/csharp/ql/src/Likely Bugs/ReferenceEqualsOnValueTypes.qhelp +++ b/csharp/ql/src/Likely Bugs/ReferenceEqualsOnValueTypes.qhelp @@ -25,7 +25,7 @@ really be compared using i == j.

  • MSDN: Object.ReferenceEquals Method.
  • -
  • The Way I See It: Object.ReferenceEquals(ValueVar, ValueVar) will always return false.
  • +
  • The Way I See It: Object.ReferenceEquals(ValueVar, ValueVar) will always return false.
  • diff --git a/csharp/ql/src/Likely Bugs/ThreadUnsafeICryptoTransform.ql b/csharp/ql/src/Likely Bugs/ThreadUnsafeICryptoTransform.ql index 4f7e83b8be0..1ee9c4a2bfa 100644 --- a/csharp/ql/src/Likely Bugs/ThreadUnsafeICryptoTransform.ql +++ b/csharp/ql/src/Likely Bugs/ThreadUnsafeICryptoTransform.ql @@ -5,6 +5,7 @@ * but under some circumstances may also result in incorrect results. * @kind problem * @problem.severity warning + * @security-severity 6.9 * @precision medium * @id cs/thread-unsafe-icryptotransform-field-in-class * @tags concurrency diff --git a/csharp/ql/src/Likely Bugs/ThreadUnsafeICryptoTransformLambda.ql b/csharp/ql/src/Likely Bugs/ThreadUnsafeICryptoTransformLambda.ql index 1c8018e3b27..33f8a8ab47c 100644 --- a/csharp/ql/src/Likely Bugs/ThreadUnsafeICryptoTransformLambda.ql +++ b/csharp/ql/src/Likely Bugs/ThreadUnsafeICryptoTransformLambda.ql @@ -6,6 +6,7 @@ * but under some circumstances may also result in incorrect results. * @kind problem * @problem.severity warning + * @security-severity 6.9 * @precision medium * @id cs/thread-unsafe-icryptotransform-captured-in-lambda * @tags concurrency diff --git a/csharp/ql/src/Metrics/Files/FNumberOfTests.qhelp b/csharp/ql/src/Metrics/Files/FNumberOfTests.qhelp index 1ebd170eaad..cddb5383bac 100644 --- a/csharp/ql/src/Metrics/Files/FNumberOfTests.qhelp +++ b/csharp/ql/src/Metrics/Files/FNumberOfTests.qhelp @@ -53,7 +53,7 @@ Microsoft Visual Studio Unit Testing Framework: documentation at MSDN.
  • - xUnit.net: official website at https://xunit.github.io/. + xUnit.net: official website at https://xunit.net/.
  • diff --git a/csharp/ql/src/Metrics/Summaries/LinesOfCode.ql b/csharp/ql/src/Metrics/Summaries/LinesOfCode.ql new file mode 100644 index 00000000000..e93e3c7416f --- /dev/null +++ b/csharp/ql/src/Metrics/Summaries/LinesOfCode.ql @@ -0,0 +1,12 @@ +/** + * @id cs/summary/lines-of-code + * @name Total lines of code in the database + * @description The total number of lines of code across all files. This is a useful metric of the size of a database. For all files that were seen during the build, this query counts the lines of code, excluding whitespace or comments. + * @kind metric + * @tags summary + * lines-of-code + */ + +import csharp + +select sum(File f | f.fromSource() | f.getNumberOfLinesOfCode()) diff --git a/csharp/ql/src/Security Features/CWE-011/ASPNetDebug.qhelp b/csharp/ql/src/Security Features/CWE-011/ASPNetDebug.qhelp index 5a65094f39c..5caf33d0cc9 100644 --- a/csharp/ql/src/Security Features/CWE-011/ASPNetDebug.qhelp +++ b/csharp/ql/src/Security Features/CWE-011/ASPNetDebug.qhelp @@ -42,7 +42,7 @@ To fix this problem, the 'debug' flag should be set to false, or re
  • MSDN: -Why debug=false in ASP.NET applications in production environment. +Why debug=false in ASP.NET applications in production environment.
  • MSDN: diff --git a/csharp/ql/src/Security Features/CWE-011/ASPNetDebug.ql b/csharp/ql/src/Security Features/CWE-011/ASPNetDebug.ql index 447306e4522..c9b2112b488 100644 --- a/csharp/ql/src/Security Features/CWE-011/ASPNetDebug.ql +++ b/csharp/ql/src/Security Features/CWE-011/ASPNetDebug.ql @@ -4,12 +4,14 @@ * debug builds provide additional information useful to a malicious attacker. * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision very-high * @id cs/web/debug-binary * @tags security * maintainability * frameworks/asp.net * external/cwe/cwe-11 + * external/cwe/cwe-532 */ import csharp diff --git a/csharp/ql/src/Security Features/CWE-016/ASPNetMaxRequestLength.ql b/csharp/ql/src/Security Features/CWE-016/ASPNetMaxRequestLength.ql index d2c232b3bd3..6c58c910089 100644 --- a/csharp/ql/src/Security Features/CWE-016/ASPNetMaxRequestLength.ql +++ b/csharp/ql/src/Security Features/CWE-016/ASPNetMaxRequestLength.ql @@ -4,6 +4,7 @@ * denial-of-service attacks. * @kind problem * @problem.severity warning + * @security-severity 6.9 * @id cs/web/large-max-request-length * @tags security * frameworks/asp.net diff --git a/csharp/ql/src/Security Features/CWE-016/ASPNetPagesValidateRequest.ql b/csharp/ql/src/Security Features/CWE-016/ASPNetPagesValidateRequest.ql index 7985cec592a..362b8a70ebe 100644 --- a/csharp/ql/src/Security Features/CWE-016/ASPNetPagesValidateRequest.ql +++ b/csharp/ql/src/Security Features/CWE-016/ASPNetPagesValidateRequest.ql @@ -3,6 +3,7 @@ * @description ASP.NET pages should not disable the built-in request validation. * @kind problem * @problem.severity warning + * @security-severity 6.9 * @id cs/web/request-validation-disabled * @tags security * frameworks/asp.net diff --git a/csharp/ql/src/Security Features/CWE-016/ASPNetRequestValidationMode.ql b/csharp/ql/src/Security Features/CWE-016/ASPNetRequestValidationMode.ql index 0e3e92fa821..a270a5928bb 100644 --- a/csharp/ql/src/Security Features/CWE-016/ASPNetRequestValidationMode.ql +++ b/csharp/ql/src/Security Features/CWE-016/ASPNetRequestValidationMode.ql @@ -6,6 +6,7 @@ * @kind problem * @id cs/insecure-request-validation-mode * @problem.severity warning + * @security-severity 6.9 * @tags security * external/cwe/cwe-016 */ diff --git a/csharp/ql/src/Security Features/CWE-020/RuntimeChecksBypass.ql b/csharp/ql/src/Security Features/CWE-020/RuntimeChecksBypass.ql index 24de3f11075..cda257234ab 100644 --- a/csharp/ql/src/Security Features/CWE-020/RuntimeChecksBypass.ql +++ b/csharp/ql/src/Security Features/CWE-020/RuntimeChecksBypass.ql @@ -4,6 +4,7 @@ * @kind problem * @id cs/serialization-check-bypass * @problem.severity warning + * @security-severity 5.9 * @precision medium * @tags security * external/cwe/cwe-20 diff --git a/csharp/ql/src/Security Features/CWE-020/UntrustedDataToExternalAPI.ql b/csharp/ql/src/Security Features/CWE-020/UntrustedDataToExternalAPI.ql index a1183b7392d..c378e31d8aa 100644 --- a/csharp/ql/src/Security Features/CWE-020/UntrustedDataToExternalAPI.ql +++ b/csharp/ql/src/Security Features/CWE-020/UntrustedDataToExternalAPI.ql @@ -5,6 +5,7 @@ * @kind path-problem * @precision low * @problem.severity error + * @security-severity 5.9 * @tags security external/cwe/cwe-20 */ diff --git a/csharp/ql/src/Security Features/CWE-022/TaintedPath.ql b/csharp/ql/src/Security Features/CWE-022/TaintedPath.ql index f25dd129f0c..bf75ab47904 100644 --- a/csharp/ql/src/Security Features/CWE-022/TaintedPath.ql +++ b/csharp/ql/src/Security Features/CWE-022/TaintedPath.ql @@ -3,6 +3,7 @@ * @description Accessing paths influenced by users can allow an attacker to access unexpected resources. * @kind path-problem * @problem.severity error + * @security-severity 6.4 * @precision high * @id cs/path-injection * @tags security diff --git a/csharp/ql/src/Security Features/CWE-022/ZipSlip.ql b/csharp/ql/src/Security Features/CWE-022/ZipSlip.ql index 57a39e39d6c..5f6855701ed 100644 --- a/csharp/ql/src/Security Features/CWE-022/ZipSlip.ql +++ b/csharp/ql/src/Security Features/CWE-022/ZipSlip.ql @@ -6,6 +6,7 @@ * @kind path-problem * @id cs/zipslip * @problem.severity error + * @security-severity 6.4 * @precision high * @tags security * external/cwe/cwe-022 diff --git a/csharp/ql/src/Security Features/CWE-078/CommandInjection.ql b/csharp/ql/src/Security Features/CWE-078/CommandInjection.ql index ece6dc5a0db..7056d3222f2 100644 --- a/csharp/ql/src/Security Features/CWE-078/CommandInjection.ql +++ b/csharp/ql/src/Security Features/CWE-078/CommandInjection.ql @@ -4,6 +4,7 @@ * user to change the meaning of the command. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id cs/command-line-injection * @tags correctness diff --git a/csharp/ql/src/Security Features/CWE-078/StoredCommandInjection.ql b/csharp/ql/src/Security Features/CWE-078/StoredCommandInjection.ql index b7a3c724d02..b8264b4d8a1 100644 --- a/csharp/ql/src/Security Features/CWE-078/StoredCommandInjection.ql +++ b/csharp/ql/src/Security Features/CWE-078/StoredCommandInjection.ql @@ -4,6 +4,7 @@ * user to change the meaning of the command. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision medium * @id cs/stored-command-line-injection * @tags correctness diff --git a/csharp/ql/src/Security Features/CWE-079/StoredXSS.ql b/csharp/ql/src/Security Features/CWE-079/StoredXSS.ql index 3c9b22583a8..fcf10553a6a 100644 --- a/csharp/ql/src/Security Features/CWE-079/StoredXSS.ql +++ b/csharp/ql/src/Security Features/CWE-079/StoredXSS.ql @@ -4,6 +4,7 @@ * scripting vulnerability if the data was originally user-provided. * @kind path-problem * @problem.severity error + * @security-severity 2.9 * @precision medium * @id cs/web/stored-xss * @tags security diff --git a/csharp/ql/src/Security Features/CWE-079/XSS.ql b/csharp/ql/src/Security Features/CWE-079/XSS.ql index 77543c3c244..d58a7828a6f 100644 --- a/csharp/ql/src/Security Features/CWE-079/XSS.ql +++ b/csharp/ql/src/Security Features/CWE-079/XSS.ql @@ -4,6 +4,7 @@ * allows for a cross-site scripting vulnerability. * @kind path-problem * @problem.severity error + * @security-severity 2.9 * @precision high * @id cs/web/xss * @tags security diff --git a/csharp/ql/src/Security Features/CWE-089/SecondOrderSqlInjection.ql b/csharp/ql/src/Security Features/CWE-089/SecondOrderSqlInjection.ql index d13702cc9ad..fde86253edc 100644 --- a/csharp/ql/src/Security Features/CWE-089/SecondOrderSqlInjection.ql +++ b/csharp/ql/src/Security Features/CWE-089/SecondOrderSqlInjection.ql @@ -4,6 +4,7 @@ * of malicious SQL code by the user. * @kind path-problem * @problem.severity error + * @security-severity 6.4 * @precision medium * @id cs/second-order-sql-injection * @tags security diff --git a/csharp/ql/src/Security Features/CWE-089/SqlInjection.ql b/csharp/ql/src/Security Features/CWE-089/SqlInjection.ql index 3214b34792e..456e36db36e 100644 --- a/csharp/ql/src/Security Features/CWE-089/SqlInjection.ql +++ b/csharp/ql/src/Security Features/CWE-089/SqlInjection.ql @@ -4,6 +4,7 @@ * malicious SQL code by the user. * @kind path-problem * @problem.severity error + * @security-severity 6.4 * @precision high * @id cs/sql-injection * @tags security diff --git a/csharp/ql/src/Security Features/CWE-090/LDAPInjection.ql b/csharp/ql/src/Security Features/CWE-090/LDAPInjection.ql index cbe927fd7dd..e0e667ed8da 100644 --- a/csharp/ql/src/Security Features/CWE-090/LDAPInjection.ql +++ b/csharp/ql/src/Security Features/CWE-090/LDAPInjection.ql @@ -4,6 +4,7 @@ * malicious LDAP code by the user. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id cs/ldap-injection * @tags security diff --git a/csharp/ql/src/Security Features/CWE-090/StoredLDAPInjection.ql b/csharp/ql/src/Security Features/CWE-090/StoredLDAPInjection.ql index 2618ab3f146..0c705fbce33 100644 --- a/csharp/ql/src/Security Features/CWE-090/StoredLDAPInjection.ql +++ b/csharp/ql/src/Security Features/CWE-090/StoredLDAPInjection.ql @@ -4,6 +4,7 @@ * insertion of malicious LDAP code by the user. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision medium * @id cs/stored-ldap-injection * @tags security diff --git a/csharp/ql/src/Security Features/CWE-091/XMLInjection.ql b/csharp/ql/src/Security Features/CWE-091/XMLInjection.ql index f06485e43a1..4e2548895ad 100644 --- a/csharp/ql/src/Security Features/CWE-091/XMLInjection.ql +++ b/csharp/ql/src/Security Features/CWE-091/XMLInjection.ql @@ -5,6 +5,7 @@ * @kind problem * @id cs/xml-injection * @problem.severity error + * @security-severity 5.9 * @precision high * @tags security * external/cwe/cwe-091 diff --git a/csharp/ql/src/Security Features/CWE-094/CodeInjection.ql b/csharp/ql/src/Security Features/CWE-094/CodeInjection.ql index 486328bdf3c..8c711400d61 100644 --- a/csharp/ql/src/Security Features/CWE-094/CodeInjection.ql +++ b/csharp/ql/src/Security Features/CWE-094/CodeInjection.ql @@ -4,6 +4,7 @@ * malicious code. * @kind path-problem * @problem.severity error + * @security-severity 10.0 * @precision high * @id cs/code-injection * @tags security diff --git a/csharp/ql/src/Security Features/CWE-099/ResourceInjection.ql b/csharp/ql/src/Security Features/CWE-099/ResourceInjection.ql index d6960ef735e..ca32d21b3cb 100644 --- a/csharp/ql/src/Security Features/CWE-099/ResourceInjection.ql +++ b/csharp/ql/src/Security Features/CWE-099/ResourceInjection.ql @@ -4,6 +4,7 @@ * malicious user providing an unintended resource. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id cs/resource-injection * @tags security diff --git a/csharp/ql/src/Security Features/CWE-112/MissingXMLValidation.ql b/csharp/ql/src/Security Features/CWE-112/MissingXMLValidation.ql index ab629d1f4d5..b13d357980b 100644 --- a/csharp/ql/src/Security Features/CWE-112/MissingXMLValidation.ql +++ b/csharp/ql/src/Security Features/CWE-112/MissingXMLValidation.ql @@ -4,6 +4,7 @@ * schema. * @kind path-problem * @problem.severity recommendation + * @security-severity 3.6 * @precision high * @id cs/xml/missing-validation * @tags security diff --git a/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjection.ql b/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjection.ql index 9b1f1fd20a9..54b578d3072 100644 --- a/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjection.ql +++ b/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjection.ql @@ -6,6 +6,7 @@ * @kind problem * @id cs/assembly-path-injection * @problem.severity error + * @security-severity 6.0 * @precision high * @tags security * external/cwe/cwe-114 diff --git a/csharp/ql/src/Security Features/CWE-117/LogForging.ql b/csharp/ql/src/Security Features/CWE-117/LogForging.ql index 400beef0daf..b7642d4e15a 100644 --- a/csharp/ql/src/Security Features/CWE-117/LogForging.ql +++ b/csharp/ql/src/Security Features/CWE-117/LogForging.ql @@ -4,6 +4,7 @@ * insertion of forged log entries by a malicious user. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id cs/log-forging * @tags security diff --git a/csharp/ql/src/Security Features/CWE-119/LocalUnvalidatedArithmetic.ql b/csharp/ql/src/Security Features/CWE-119/LocalUnvalidatedArithmetic.ql index 9f88c2a4eac..263429e6995 100644 --- a/csharp/ql/src/Security Features/CWE-119/LocalUnvalidatedArithmetic.ql +++ b/csharp/ql/src/Security Features/CWE-119/LocalUnvalidatedArithmetic.ql @@ -5,6 +5,7 @@ * to return any value. * @kind problem * @problem.severity warning + * @security-severity 10.0 * @precision high * @id cs/unvalidated-local-pointer-arithmetic * @tags security diff --git a/csharp/ql/src/Security Features/CWE-134/UncontrolledFormatString.ql b/csharp/ql/src/Security Features/CWE-134/UncontrolledFormatString.ql index a22c13bbb97..7494412e3b3 100644 --- a/csharp/ql/src/Security Features/CWE-134/UncontrolledFormatString.ql +++ b/csharp/ql/src/Security Features/CWE-134/UncontrolledFormatString.ql @@ -4,6 +4,7 @@ * and cause a denial of service. * @kind path-problem * @problem.severity error + * @security-severity 6.9 * @precision high * @id cs/uncontrolled-format-string * @tags security diff --git a/csharp/ql/src/Security Features/CWE-201/ExposureInTransmittedData.ql b/csharp/ql/src/Security Features/CWE-201/ExposureInTransmittedData.ql index b8b18c6b56d..fa40db533d5 100644 --- a/csharp/ql/src/Security Features/CWE-201/ExposureInTransmittedData.ql +++ b/csharp/ql/src/Security Features/CWE-201/ExposureInTransmittedData.ql @@ -3,6 +3,7 @@ * @description Transmitting sensitive information to the user is a potential security risk. * @kind path-problem * @problem.severity error + * @security-severity 1.4 * @precision high * @id cs/sensitive-data-transmission * @tags security diff --git a/csharp/ql/src/Security Features/CWE-209/ExceptionInformationExposure.qhelp b/csharp/ql/src/Security Features/CWE-209/ExceptionInformationExposure.qhelp index fc508827c9f..f36d8a3204e 100644 --- a/csharp/ql/src/Security Features/CWE-209/ExceptionInformationExposure.qhelp +++ b/csharp/ql/src/Security Features/CWE-209/ExceptionInformationExposure.qhelp @@ -36,7 +36,4 @@ use the error log, but remote users will not see the information.

    - -
  • OWASP: Information Leak.
  • - diff --git a/csharp/ql/src/Security Features/CWE-209/ExceptionInformationExposure.ql b/csharp/ql/src/Security Features/CWE-209/ExceptionInformationExposure.ql index d9db652c8d8..23e72e4e5e9 100644 --- a/csharp/ql/src/Security Features/CWE-209/ExceptionInformationExposure.ql +++ b/csharp/ql/src/Security Features/CWE-209/ExceptionInformationExposure.ql @@ -5,6 +5,7 @@ * developing a subsequent exploit. * @kind path-problem * @problem.severity error + * @security-severity 3.6 * @precision high * @id cs/information-exposure-through-exception * @tags security diff --git a/csharp/ql/src/Security Features/CWE-248/MissingASPNETGlobalErrorHandler.ql b/csharp/ql/src/Security Features/CWE-248/MissingASPNETGlobalErrorHandler.ql index 33e791feb42..323630d0c4e 100644 --- a/csharp/ql/src/Security Features/CWE-248/MissingASPNETGlobalErrorHandler.ql +++ b/csharp/ql/src/Security Features/CWE-248/MissingASPNETGlobalErrorHandler.ql @@ -4,6 +4,7 @@ * a global error handler, otherwise they may leak exception information. * @kind problem * @problem.severity warning + * @security-severity 3.6 * @precision high * @id cs/web/missing-global-error-handler * @tags security diff --git a/csharp/ql/src/Security Features/CWE-312/CleartextStorage.ql b/csharp/ql/src/Security Features/CWE-312/CleartextStorage.ql index 44209db60cb..b19ca4ff1bd 100644 --- a/csharp/ql/src/Security Features/CWE-312/CleartextStorage.ql +++ b/csharp/ql/src/Security Features/CWE-312/CleartextStorage.ql @@ -4,6 +4,7 @@ * attacker. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id cs/cleartext-storage-of-sensitive-information * @tags security diff --git a/csharp/ql/src/Security Features/CWE-321/HardcodedEncryptionKey.ql b/csharp/ql/src/Security Features/CWE-321/HardcodedEncryptionKey.ql index 15dd46bc1f2..ff244adee95 100644 --- a/csharp/ql/src/Security Features/CWE-321/HardcodedEncryptionKey.ql +++ b/csharp/ql/src/Security Features/CWE-321/HardcodedEncryptionKey.ql @@ -4,7 +4,9 @@ * @kind problem * @id cs/hardcoded-key * @problem.severity error + * @security-severity 5.9 * @tags security + * external/cwe/cwe-320 */ /* diff --git a/csharp/ql/src/Security Features/CWE-321/HardcodedSymmetricEncryptionKey.ql b/csharp/ql/src/Security Features/CWE-321/HardcodedSymmetricEncryptionKey.ql index 4113651677b..2cabc38aa8b 100644 --- a/csharp/ql/src/Security Features/CWE-321/HardcodedSymmetricEncryptionKey.ql +++ b/csharp/ql/src/Security Features/CWE-321/HardcodedSymmetricEncryptionKey.ql @@ -4,7 +4,9 @@ * @kind path-problem * @id cs/hard-coded-symmetric-encryption-key * @problem.severity error + * @security-severity 3.6 * @tags security + * external/cwe/cwe-321 */ /* diff --git a/csharp/ql/src/Security Features/CWE-327/DontInstallRootCert.ql b/csharp/ql/src/Security Features/CWE-327/DontInstallRootCert.ql index dbd400b3a95..a843008e582 100644 --- a/csharp/ql/src/Security Features/CWE-327/DontInstallRootCert.ql +++ b/csharp/ql/src/Security Features/CWE-327/DontInstallRootCert.ql @@ -5,6 +5,7 @@ * @kind path-problem * @id cs/adding-cert-to-root-store * @problem.severity error + * @security-severity 5.2 * @tags security * external/cwe/cwe-327 */ diff --git a/csharp/ql/src/Security Features/CWE-327/InsecureSQLConnection.ql b/csharp/ql/src/Security Features/CWE-327/InsecureSQLConnection.ql index ffdbc531ade..a16358b1f90 100644 --- a/csharp/ql/src/Security Features/CWE-327/InsecureSQLConnection.ql +++ b/csharp/ql/src/Security Features/CWE-327/InsecureSQLConnection.ql @@ -4,6 +4,7 @@ * @kind path-problem * @id cs/insecure-sql-connection * @problem.severity error + * @security-severity 5.2 * @precision medium * @tags security * external/cwe/cwe-327 diff --git a/csharp/ql/src/Security Features/CWE-352/MissingAntiForgeryTokenValidation.ql b/csharp/ql/src/Security Features/CWE-352/MissingAntiForgeryTokenValidation.ql index 134031adf43..fb40413716f 100644 --- a/csharp/ql/src/Security Features/CWE-352/MissingAntiForgeryTokenValidation.ql +++ b/csharp/ql/src/Security Features/CWE-352/MissingAntiForgeryTokenValidation.ql @@ -4,6 +4,7 @@ * allows a malicious attacker to submit a request on behalf of the user. * @kind problem * @problem.severity error + * @security-severity 6.4 * @precision high * @id cs/web/missing-token-validation * @tags security diff --git a/csharp/ql/src/Security Features/CWE-359/ExposureOfPrivateInformation.ql b/csharp/ql/src/Security Features/CWE-359/ExposureOfPrivateInformation.ql index 7f765d7c0aa..20323b66bb9 100644 --- a/csharp/ql/src/Security Features/CWE-359/ExposureOfPrivateInformation.ql +++ b/csharp/ql/src/Security Features/CWE-359/ExposureOfPrivateInformation.ql @@ -4,6 +4,7 @@ * unauthorized persons. * @kind path-problem * @problem.severity error + * @security-severity 3.6 * @precision high * @id cs/exposure-of-sensitive-information * @tags security diff --git a/csharp/ql/src/Security Features/CWE-384/AbandonSession.ql b/csharp/ql/src/Security Features/CWE-384/AbandonSession.ql index c9a397c02c6..75daa5fc10c 100644 --- a/csharp/ql/src/Security Features/CWE-384/AbandonSession.ql +++ b/csharp/ql/src/Security Features/CWE-384/AbandonSession.ql @@ -5,6 +5,7 @@ * their session. * @kind problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id cs/session-reuse * @tags security diff --git a/csharp/ql/src/Security Features/CWE-451/MissingXFrameOptions.ql b/csharp/ql/src/Security Features/CWE-451/MissingXFrameOptions.ql index abf3f0a55ad..87757be5400 100644 --- a/csharp/ql/src/Security Features/CWE-451/MissingXFrameOptions.ql +++ b/csharp/ql/src/Security Features/CWE-451/MissingXFrameOptions.ql @@ -4,6 +4,7 @@ * overlay their own UI on top of the site by using an iframe. * @kind problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id cs/web/missing-x-frame-options * @tags security diff --git a/csharp/ql/src/Security Features/CWE-502/DeserializedDelegate.ql b/csharp/ql/src/Security Features/CWE-502/DeserializedDelegate.ql index 31d28311908..76035d9fcba 100644 --- a/csharp/ql/src/Security Features/CWE-502/DeserializedDelegate.ql +++ b/csharp/ql/src/Security Features/CWE-502/DeserializedDelegate.ql @@ -5,6 +5,7 @@ * @kind problem * @id cs/deserialized-delegate * @problem.severity warning + * @security-severity 5.9 * @precision high * @tags security * external/cwe/cwe-502 diff --git a/csharp/ql/src/Security Features/CWE-502/UnsafeDeserialization.ql b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserialization.ql index 40022d40573..338347c0887 100644 --- a/csharp/ql/src/Security Features/CWE-502/UnsafeDeserialization.ql +++ b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserialization.ql @@ -5,6 +5,7 @@ * @kind problem * @id cs/unsafe-deserialization * @problem.severity warning + * @security-severity 5.9 * @precision low * @tags security * external/cwe/cwe-502 diff --git a/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationUntrustedInput.ql b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationUntrustedInput.ql index 80a3762a8bc..32981563ab6 100644 --- a/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationUntrustedInput.ql +++ b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationUntrustedInput.ql @@ -5,6 +5,7 @@ * @kind path-problem * @id cs/unsafe-deserialization-untrusted-input * @problem.severity error + * @security-severity 5.9 * @precision high * @tags security * external/cwe/cwe-502 diff --git a/csharp/ql/src/Security Features/CWE-548/ASPNetDirectoryListing.ql b/csharp/ql/src/Security Features/CWE-548/ASPNetDirectoryListing.ql index ba40219f3db..82532ed40e0 100644 --- a/csharp/ql/src/Security Features/CWE-548/ASPNetDirectoryListing.ql +++ b/csharp/ql/src/Security Features/CWE-548/ASPNetDirectoryListing.ql @@ -3,6 +3,7 @@ * @description Directory browsing should not be enabled in production as it can leak sensitive information. * @kind problem * @problem.severity warning + * @security-severity 3.6 * @precision very-high * @id cs/web/directory-browse-enabled * @tags security diff --git a/csharp/ql/src/Security Features/CWE-601/UrlRedirect.ql b/csharp/ql/src/Security Features/CWE-601/UrlRedirect.ql index a3ece934561..37594e7cf72 100644 --- a/csharp/ql/src/Security Features/CWE-601/UrlRedirect.ql +++ b/csharp/ql/src/Security Features/CWE-601/UrlRedirect.ql @@ -4,6 +4,7 @@ * may cause redirection to malicious web sites. * @kind path-problem * @problem.severity error + * @security-severity 2.7 * @precision high * @id cs/web/unvalidated-url-redirection * @tags security diff --git a/csharp/ql/src/Security Features/CWE-611/UntrustedDataInsecureXml.ql b/csharp/ql/src/Security Features/CWE-611/UntrustedDataInsecureXml.ql index 9acb765252f..2b37eb33390 100644 --- a/csharp/ql/src/Security Features/CWE-611/UntrustedDataInsecureXml.ql +++ b/csharp/ql/src/Security Features/CWE-611/UntrustedDataInsecureXml.ql @@ -3,6 +3,7 @@ * @description Untrusted XML is read with an insecure resolver and DTD processing enabled. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id cs/xml/insecure-dtd-handling * @tags security diff --git a/csharp/ql/src/Security Features/CWE-611/UseXmlSecureResolver.ql b/csharp/ql/src/Security Features/CWE-611/UseXmlSecureResolver.ql index d1159a39f99..1073c873d8c 100644 --- a/csharp/ql/src/Security Features/CWE-611/UseXmlSecureResolver.ql +++ b/csharp/ql/src/Security Features/CWE-611/UseXmlSecureResolver.ql @@ -4,6 +4,7 @@ * be restricted using a secure resolver or disabling DTD processing. * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision low * @id cs/insecure-xml-read * @tags security diff --git a/csharp/ql/src/Security Features/CWE-614/RequireSSL.ql b/csharp/ql/src/Security Features/CWE-614/RequireSSL.ql index 0cfd4868c29..49dd6e52e13 100644 --- a/csharp/ql/src/Security Features/CWE-614/RequireSSL.ql +++ b/csharp/ql/src/Security Features/CWE-614/RequireSSL.ql @@ -5,6 +5,7 @@ * is used at all times. * @kind problem * @problem.severity error + * @security-severity 5.2 * @precision high * @id cs/web/requiressl-not-set * @tags security diff --git a/csharp/ql/src/Security Features/CWE-643/StoredXPathInjection.ql b/csharp/ql/src/Security Features/CWE-643/StoredXPathInjection.ql index 5078afd9b33..5d3ee1db4e7 100644 --- a/csharp/ql/src/Security Features/CWE-643/StoredXPathInjection.ql +++ b/csharp/ql/src/Security Features/CWE-643/StoredXPathInjection.ql @@ -4,6 +4,7 @@ * user is vulnerable to insertion of malicious code by the user. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision medium * @id cs/xml/stored-xpath-injection * @tags security diff --git a/csharp/ql/src/Security Features/CWE-643/XPathInjection.ql b/csharp/ql/src/Security Features/CWE-643/XPathInjection.ql index 5506ef6b637..a158ccfab69 100644 --- a/csharp/ql/src/Security Features/CWE-643/XPathInjection.ql +++ b/csharp/ql/src/Security Features/CWE-643/XPathInjection.ql @@ -4,6 +4,7 @@ * malicious code by the user. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id cs/xml/xpath-injection * @tags security diff --git a/csharp/ql/src/Security Features/CWE-730/ReDoS.ql b/csharp/ql/src/Security Features/CWE-730/ReDoS.ql index 94db79a0693..79ade61af90 100644 --- a/csharp/ql/src/Security Features/CWE-730/ReDoS.ql +++ b/csharp/ql/src/Security Features/CWE-730/ReDoS.ql @@ -4,6 +4,7 @@ * exponential time on certain input. * @kind path-problem * @problem.severity error + * @security-severity 3.6 * @precision high * @id cs/redos * @tags security diff --git a/csharp/ql/src/Security Features/CWE-730/RegexInjection.ql b/csharp/ql/src/Security Features/CWE-730/RegexInjection.ql index ad0974235e9..5aca2ad9c49 100644 --- a/csharp/ql/src/Security Features/CWE-730/RegexInjection.ql +++ b/csharp/ql/src/Security Features/CWE-730/RegexInjection.ql @@ -5,6 +5,7 @@ * exponential time on certain inputs. * @kind path-problem * @problem.severity error + * @security-severity 3.6 * @precision high * @id cs/regex-injection * @tags security diff --git a/csharp/ql/src/Security Features/CWE-798/HardcodedConnectionString.ql b/csharp/ql/src/Security Features/CWE-798/HardcodedConnectionString.ql index b9e2ee248cc..0aa5f9026d1 100644 --- a/csharp/ql/src/Security Features/CWE-798/HardcodedConnectionString.ql +++ b/csharp/ql/src/Security Features/CWE-798/HardcodedConnectionString.ql @@ -3,6 +3,7 @@ * @description Credentials are hard-coded in a connection string in the source code of the application. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id cs/hardcoded-connection-string-credentials * @tags security diff --git a/csharp/ql/src/Security Features/CWE-798/HardcodedCredentials.ql b/csharp/ql/src/Security Features/CWE-798/HardcodedCredentials.ql index 06b69d95b5f..7b183189921 100644 --- a/csharp/ql/src/Security Features/CWE-798/HardcodedCredentials.ql +++ b/csharp/ql/src/Security Features/CWE-798/HardcodedCredentials.ql @@ -3,6 +3,7 @@ * @description Credentials are hard coded in the source code of the application. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id cs/hardcoded-credentials * @tags security diff --git a/csharp/ql/src/Security Features/CWE-807/ConditionalBypass.ql b/csharp/ql/src/Security Features/CWE-807/ConditionalBypass.ql index e3ba8463b76..3922c262031 100644 --- a/csharp/ql/src/Security Features/CWE-807/ConditionalBypass.ql +++ b/csharp/ql/src/Security Features/CWE-807/ConditionalBypass.ql @@ -4,6 +4,7 @@ * passing through authentication systems. * @kind path-problem * @problem.severity error + * @security-severity 6.4 * @precision high * @id cs/user-controlled-bypass * @tags security diff --git a/csharp/ql/src/Security Features/CWE-838/InappropriateEncoding.ql b/csharp/ql/src/Security Features/CWE-838/InappropriateEncoding.ql index 88a0b970a7a..8b8bd478031 100644 --- a/csharp/ql/src/Security Features/CWE-838/InappropriateEncoding.ql +++ b/csharp/ql/src/Security Features/CWE-838/InappropriateEncoding.ql @@ -4,6 +4,7 @@ * pose a security risk. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision low * @id cs/inappropriate-encoding * @tags security diff --git a/csharp/ql/src/Security Features/CookieWithOverlyBroadDomain.ql b/csharp/ql/src/Security Features/CookieWithOverlyBroadDomain.ql index f3cce1c36f5..472a87441ed 100644 --- a/csharp/ql/src/Security Features/CookieWithOverlyBroadDomain.ql +++ b/csharp/ql/src/Security Features/CookieWithOverlyBroadDomain.ql @@ -3,6 +3,7 @@ * @description Finds cookies with an overly broad domain. * @kind problem * @problem.severity warning + * @security-severity 6.4 * @precision high * @id cs/web/broad-cookie-domain * @tags security diff --git a/csharp/ql/src/Security Features/CookieWithOverlyBroadPath.ql b/csharp/ql/src/Security Features/CookieWithOverlyBroadPath.ql index a75b41794dd..ec6953f72e1 100644 --- a/csharp/ql/src/Security Features/CookieWithOverlyBroadPath.ql +++ b/csharp/ql/src/Security Features/CookieWithOverlyBroadPath.ql @@ -3,6 +3,7 @@ * @description Finds cookies with an overly broad path. * @kind problem * @problem.severity warning + * @security-severity 6.4 * @precision high * @id cs/web/broad-cookie-path * @tags security diff --git a/csharp/ql/src/Security Features/Encryption using ECB.ql b/csharp/ql/src/Security Features/Encryption using ECB.ql index 2d36bd99306..72c63b9c565 100644 --- a/csharp/ql/src/Security Features/Encryption using ECB.ql +++ b/csharp/ql/src/Security Features/Encryption using ECB.ql @@ -3,6 +3,7 @@ * @description Highlights uses of the encryption mode 'CipherMode.ECB'. This mode should normally not be used because it is vulnerable to replay attacks. * @kind problem * @problem.severity warning + * @security-severity 5.2 * @precision high * @id cs/ecb-encryption * @tags security diff --git a/csharp/ql/src/Security Features/HeaderCheckingDisabled.ql b/csharp/ql/src/Security Features/HeaderCheckingDisabled.ql index 85793c0a730..94d01609100 100644 --- a/csharp/ql/src/Security Features/HeaderCheckingDisabled.ql +++ b/csharp/ql/src/Security Features/HeaderCheckingDisabled.ql @@ -3,6 +3,7 @@ * @description Finds places where header checking is disabled. * @kind problem * @problem.severity warning + * @security-severity 3.6 * @precision high * @id cs/web/disabled-header-checking * @tags security diff --git a/csharp/ql/src/Security Features/InadequateRSAPadding.ql b/csharp/ql/src/Security Features/InadequateRSAPadding.ql index 6176e4ac74e..ddeb9b370f6 100644 --- a/csharp/ql/src/Security Features/InadequateRSAPadding.ql +++ b/csharp/ql/src/Security Features/InadequateRSAPadding.ql @@ -3,6 +3,7 @@ * @description Finds uses of RSA encryption with inadequate padding. * @kind problem * @problem.severity warning + * @security-severity 5.2 * @precision high * @id cs/inadequate-rsa-padding * @tags security diff --git a/csharp/ql/src/Security Features/InsecureRandomness.ql b/csharp/ql/src/Security Features/InsecureRandomness.ql index ef1665819f7..434f8c287f2 100644 --- a/csharp/ql/src/Security Features/InsecureRandomness.ql +++ b/csharp/ql/src/Security Features/InsecureRandomness.ql @@ -5,6 +5,7 @@ * be generated. * @kind path-problem * @problem.severity warning + * @security-severity 5.9 * @precision high * @id cs/insecure-randomness * @tags security diff --git a/csharp/ql/src/Security Features/InsufficientKeySize.ql b/csharp/ql/src/Security Features/InsufficientKeySize.ql index d5e50a60d7f..70caea4b179 100644 --- a/csharp/ql/src/Security Features/InsufficientKeySize.ql +++ b/csharp/ql/src/Security Features/InsufficientKeySize.ql @@ -3,6 +3,7 @@ * @description Finds uses of encryption algorithms with too small a key size * @kind problem * @problem.severity warning + * @security-severity 5.2 * @precision high * @id cs/insufficient-key-size * @tags security diff --git a/csharp/ql/src/Security Features/PersistentCookie.ql b/csharp/ql/src/Security Features/PersistentCookie.ql index 7305bdaf239..c7041cb7a36 100644 --- a/csharp/ql/src/Security Features/PersistentCookie.ql +++ b/csharp/ql/src/Security Features/PersistentCookie.ql @@ -3,6 +3,7 @@ * @description Persistent cookies are vulnerable to attacks. * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision high * @id cs/web/persistent-cookie * @tags security diff --git a/csharp/ql/src/Security Features/WeakEncryption.ql b/csharp/ql/src/Security Features/WeakEncryption.ql index e0666d48d19..b6d543d6de7 100644 --- a/csharp/ql/src/Security Features/WeakEncryption.ql +++ b/csharp/ql/src/Security Features/WeakEncryption.ql @@ -3,6 +3,7 @@ * @description Finds uses of encryption algorithms that are weak and obsolete * @kind problem * @problem.severity warning + * @security-severity 5.2 * @precision high * @id cs/weak-encryption * @tags security diff --git a/csharp/ql/src/Stubs/make_stubs.py b/csharp/ql/src/Stubs/make_stubs.py index e94cf0c7261..7d2ce9f7ed7 100644 --- a/csharp/ql/src/Stubs/make_stubs.py +++ b/csharp/ql/src/Stubs/make_stubs.py @@ -43,12 +43,6 @@ if not foundCS: print("Test directory does not contain .cs files. Please specify a working qltest directory.") exit(1) -cmd = ['odasa', 'selfTest'] -print('Running ' + ' '.join(cmd)) -if subprocess.check_call(cmd): - print("odasa selfTest failed. Ensure odasa is on your current path.") - exit(1) - csharpQueries = os.path.abspath(os.path.dirname(sys.argv[0])) outputFile = os.path.join(testDir, 'stubs.cs') @@ -58,56 +52,75 @@ if os.path.isfile(outputFile): os.remove(outputFile) # It would interfere with the test. print("Removed previous", outputFile) -cmd = ['odasa', 'qltest', '--optimize', '--leave-temp-files', testDir] +cmd = ['codeql', 'test', 'run', '--keep-databases', testDir] print('Running ' + ' '.join(cmd)) if subprocess.check_call(cmd): - print("qltest failed. Please fix up the test before proceeding.") + print("codeql test failed. Please fix up the test before proceeding.") exit(1) -dbDir = os.path.join(testDir, os.path.basename(testDir) + ".testproj", "db-csharp") +dbDir = os.path.join(testDir, os.path.basename(testDir) + ".testproj") if not os.path.isdir(dbDir): - print("Expected database directory " + dbDir + " not found. Please contact Semmle.") + print("Expected database directory " + dbDir + " not found.") exit(1) -cmd = ['odasa', 'runQuery', '--query', os.path.join(csharpQueries, 'MinimalStubsFromSource.ql'), '--db', dbDir, '--output-file', outputFile] +cmd = ['codeql', 'query', 'run', os.path.join( + csharpQueries, 'MinimalStubsFromSource.ql'), '--database', dbDir, '--output', outputFile] print('Running ' + ' '.join(cmd)) if subprocess.check_call(cmd): - print('Failed to run the query to generate output file. Please contact Semmle.') + print('Failed to run the query to generate output file.') exit(1) -# Remove the leading " and trailing " bytes from the file -len = os.stat(outputFile).st_size -f = open(outputFile, "rb") -try: - quote = f.read(1) - if quote != b'"': - print("Unexpected character in file. Please contact Semmle.") - contents = f.read(len-3) - quote = f.read(1) - if quote != b'"': - print("Unexpected end character. Please contact Semmle.", quote) -finally: - f.close() +# Remove the leading and trailing bytes from the file +length = os.stat(outputFile).st_size +if length < 20: + contents = b'' +else: + f = open(outputFile, "rb") + try: + countTillSlash = 0 + foundSlash = False + slash = f.read(1) + while slash != b'': + if slash == b'/': + foundSlash = True + break + countTillSlash += 1 + slash = f.read(1) + + if not foundSlash: + countTillSlash = 0 + + f.seek(0) + quote = f.read(countTillSlash) + print("Start characters in file skipped.", quote) + post = b'\x0e\x01\x08#select\x01\x01\x00s\x00' + contents = f.read(length - len(post) - countTillSlash) + quote = f.read(len(post)) + if quote != post: + print("Unexpected end character in file.", quote) + finally: + f.close() f = open(outputFile, "wb") f.write(contents) f.close() -cmd = ['odasa', 'qltest', '--optimize', testDir] +cmd = ['codeql', 'test', 'run', testDir] print('Running ' + ' '.join(cmd)) if subprocess.check_call(cmd): - print('\nTest failed. You may need to fix up', outputFile) - print('It may help to view', outputFile, ' in Visual Studio') - print("Next steps:") - print('1. Look at the compilation errors, and fix up', outputFile, 'so that the test compiles') - print('2. Re-run odasa qltest --optimize "' + testDir + '"') - print('3. git add "' + outputFile + '"') - exit(1) + print('\nTest failed. You may need to fix up', outputFile) + print('It may help to view', outputFile, ' in Visual Studio') + print("Next steps:") + print('1. Look at the compilation errors, and fix up', + outputFile, 'so that the test compiles') + print('2. Re-run codeql test run "' + testDir + '"') + print('3. git add "' + outputFile + '"') + exit(1) print("\nStub generation successful! Next steps:") print('1. Edit "semmle-extractor-options" in the .cs files to remove unused references') -print('2. Re-run odasa qltest --optimize "' + testDir + '"') +print('2. Re-run codeql test run "' + testDir + '"') print('3. git add "' + outputFile + '"') print('4. Commit your changes.') diff --git a/csharp/ql/src/csharp.qll b/csharp/ql/src/csharp.qll index 3c821c9a443..dc187fc8d92 100644 --- a/csharp/ql/src/csharp.qll +++ b/csharp/ql/src/csharp.qll @@ -2,6 +2,7 @@ * The default C# QL library. */ +import Customizations import semmle.code.csharp.Attribute import semmle.code.csharp.Callable import semmle.code.csharp.Comments diff --git a/csharp/ql/src/definitions.qll b/csharp/ql/src/definitions.qll index 2004ad0d218..c1b456f4dbf 100644 --- a/csharp/ql/src/definitions.qll +++ b/csharp/ql/src/definitions.qll @@ -187,5 +187,11 @@ cached Declaration definitionOf(Use use, string kind) { result = use.getDefinition() and result.fromSource() and - kind = use.getUseType() + kind = use.getUseType() and + // Some entities have many locations. This can arise for files that + // are duplicated multiple times in the database at different + // locations. Rather than letting the result set explode, we just + // exclude results that are "too ambiguous" -- we could also arbitrarily + // pick one location later on. + strictcount(result.getLocation()) < 10 } diff --git a/csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll b/csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll index e16b71733b5..4b3f19cbdde 100644 --- a/csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll @@ -55,6 +55,8 @@ module UnaliasedSSAInstructions { result = TUnaliasedSSAPhiInstruction(blockStartInstr, memoryLocation) } + TRawInstruction reusedPhiInstruction(TRawInstruction blockStartInstr) { none() } + class TChiInstruction = TUnaliasedSSAChiInstruction; TChiInstruction chiInstruction(TRawInstruction primaryInstruction) { @@ -75,7 +77,7 @@ module UnaliasedSSAInstructions { * a class alias. */ module AliasedSSAInstructions { - class TPhiInstruction = TAliasedSSAPhiInstruction; + class TPhiInstruction = TAliasedSSAPhiInstruction or TUnaliasedSSAPhiInstruction; TPhiInstruction phiInstruction( TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation @@ -83,6 +85,10 @@ module AliasedSSAInstructions { result = TAliasedSSAPhiInstruction(blockStartInstr, memoryLocation) } + TPhiInstruction reusedPhiInstruction(TRawInstruction blockStartInstr) { + result = TUnaliasedSSAPhiInstruction(blockStartInstr, _) + } + class TChiInstruction = TAliasedSSAChiInstruction; TChiInstruction chiInstruction(TRawInstruction primaryInstruction) { diff --git a/csharp/ql/src/experimental/ir/implementation/internal/TOperand.qll b/csharp/ql/src/experimental/ir/implementation/internal/TOperand.qll index 9c3e7620186..20fe77f8223 100644 --- a/csharp/ql/src/experimental/ir/implementation/internal/TOperand.qll +++ b/csharp/ql/src/experimental/ir/implementation/internal/TOperand.qll @@ -92,6 +92,16 @@ module RawOperands { none() } + /** + * Returns the Phi operand with the specified parameters. + */ + TPhiOperand reusedPhiOperand( + Raw::PhiInstruction useInstr, Raw::Instruction defInstr, Raw::IRBlock predecessorBlock, + Overlap overlap + ) { + none() + } + /** * Returns the Chi operand with the specified parameters. */ @@ -123,6 +133,16 @@ module UnaliasedSSAOperands { result = Internal::TUnaliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap) } + /** + * Returns the Phi operand with the specified parameters. + */ + TPhiOperand reusedPhiOperand( + Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr, + Unaliased::IRBlock predecessorBlock, Overlap overlap + ) { + none() + } + /** * Returns the Chi operand with the specified parameters. */ diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll index af6f3d81982..453838215ff 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll @@ -297,7 +297,8 @@ class Instruction extends Construction::TStageInstruction { /** * Gets the opcode that specifies the operation performed by this instruction. */ - final Opcode getOpcode() { result = Construction::getInstructionOpcode(this) } + pragma[inline] + final Opcode getOpcode() { Construction::getInstructionOpcode(result, this) } /** * Gets all direct uses of the result of this instruction. The result can be @@ -1645,6 +1646,19 @@ class CallInstruction extends Instruction { * Gets the number of arguments of the call, including the `this` pointer, if any. */ final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) } + + /** + * Holds if the result is a side effect for the argument at the specified index, or `this` if + * `index` is `-1`. + * + * This helper predicate makes it easy to join on both of these columns at once, avoiding + * pathological join orders in case the argument index should get joined first. + */ + pragma[noinline] + final SideEffectInstruction getAParameterSideEffect(int index) { + this = result.getPrimaryInstruction() and + index = result.(IndexedInstruction).getIndex() + } } /** @@ -1980,6 +1994,14 @@ class PhiInstruction extends Instruction { */ pragma[noinline] final Instruction getAnInput() { result = this.getAnInputOperand().getDef() } + + /** + * Gets the input operand representing the value that flows from the specified predecessor block. + */ + final PhiInputOperand getInputOperand(IRBlock predecessorBlock) { + result = this.getAnOperand() and + result.getPredecessorBlock() = predecessorBlock + } } /** diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll b/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll index a2ce0662dc2..d7cf89ca9aa 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll @@ -28,11 +28,15 @@ class Operand extends TStageOperand { cached Operand() { // Ensure that the operand does not refer to instructions from earlier stages that are unreachable here - exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or - exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or + exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) + or + exists(Instruction use | this = nonSSAMemoryOperand(use, _)) + or exists(Instruction use, Instruction def, IRBlock predecessorBlock | - this = phiOperand(use, def, predecessorBlock, _) - ) or + this = phiOperand(use, def, predecessorBlock, _) or + this = reusedPhiOperand(use, def, predecessorBlock, _) + ) + or exists(Instruction use | this = chiOperand(use, _)) } @@ -431,7 +435,11 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand { Overlap overlap; cached - PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) } + PhiInputOperand() { + this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) + or + this = reusedPhiOperand(useInstr, defInstr, predecessorBlock, overlap) + } override string toString() { result = "Phi" } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/IRConstruction.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/IRConstruction.qll index f5fe747472f..956a95e0667 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/internal/IRConstruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/IRConstruction.qll @@ -165,10 +165,10 @@ import Cached cached private module Cached { cached - Opcode getInstructionOpcode(TRawInstruction instr) { + predicate getInstructionOpcode(Opcode opcode, TRawInstruction instr) { exists(TranslatedElement element, InstructionTag tag | instructionOrigin(instr, element, tag) and - element.hasInstruction(result, tag, _) + element.hasInstruction(opcode, tag, _) ) } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedElement.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedElement.qll index 81d7b71b952..04e05dc9814 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedElement.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/TranslatedElement.qll @@ -57,7 +57,8 @@ private Element getRealParent(Expr expr) { result = expr.getParent() } */ predicate isIRConstant(Expr expr) { exists(expr.getValue()) } -// Pulled out to work around QL-796 +// Pulled out for performance. See +// https://github.com/github/codeql-coreql-team/issues/1044. private predicate isOrphan(Expr expr) { not exists(getRealParent(expr)) } /** diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Common.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Common.qll index 267cf903b00..ef40d716fea 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Common.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Common.qll @@ -182,15 +182,21 @@ abstract class TranslatedCompilerGeneratedVariableAccess extends TranslatedCompi override Instruction getChildSuccessor(TranslatedElement child) { none() } + /** + * Returns the type of the accessed variable. Can be overriden when the return + * type is different than the type of the underlying variable. + */ + Type getVariableType() { result = getResultType() } + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = AddressTag() and opcode instanceof Opcode::VariableAddress and - resultType = getTypeForGLValue(getResultType()) + resultType = getTypeForGLValue(getVariableType()) or needsLoad() and tag = LoadTag() and opcode instanceof Opcode::Load and - resultType = getTypeForPRValue(getResultType()) + resultType = getTypeForPRValue(getVariableType()) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { diff --git a/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Foreach.qll b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Foreach.qll index a526ec5bdcd..195072ed124 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Foreach.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/internal/desugar/Foreach.qll @@ -359,9 +359,16 @@ private class TranslatedMoveNextEnumAcc extends TTranslatedCompilerGeneratedElem override Type getResultType() { result instanceof BoolType } + override Type getVariableType() { + exists(TranslatedForeachGetEnumerator ge | + ge.getAST() = generatedBy and + result = ge.getCallResultType() + ) + } + override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { tag = ForeachEnumTempVar() and - type = getTypeForPRValue(getResultType()) + type = getTypeForPRValue(getVariableType()) } override IRVariable getInstructionVariable(InstructionTag tag) { @@ -384,9 +391,16 @@ private class TranslatedForeachCurrentEnumAcc extends TTranslatedCompilerGenerat override Type getResultType() { result instanceof BoolType } + override Type getVariableType() { + exists(TranslatedForeachGetEnumerator ge | + ge.getAST() = generatedBy and + result = ge.getCallResultType() + ) + } + override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { tag = ForeachEnumTempVar() and - type = getTypeForPRValue(getResultType()) + type = getTypeForPRValue(getVariableType()) } override IRVariable getInstructionVariable(InstructionTag tag) { @@ -409,9 +423,16 @@ private class TranslatedForeachDisposeEnumAcc extends TTranslatedCompilerGenerat override Type getResultType() { result instanceof BoolType } + override Type getVariableType() { + exists(TranslatedForeachGetEnumerator ge | + ge.getAST() = generatedBy and + result = ge.getCallResultType() + ) + } + override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { tag = ForeachEnumTempVar() and - type = getTypeForPRValue(getResultType()) + type = getTypeForPRValue(getVariableType()) } override IRVariable getInstructionVariable(InstructionTag tag) { diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll index af6f3d81982..453838215ff 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll @@ -297,7 +297,8 @@ class Instruction extends Construction::TStageInstruction { /** * Gets the opcode that specifies the operation performed by this instruction. */ - final Opcode getOpcode() { result = Construction::getInstructionOpcode(this) } + pragma[inline] + final Opcode getOpcode() { Construction::getInstructionOpcode(result, this) } /** * Gets all direct uses of the result of this instruction. The result can be @@ -1645,6 +1646,19 @@ class CallInstruction extends Instruction { * Gets the number of arguments of the call, including the `this` pointer, if any. */ final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) } + + /** + * Holds if the result is a side effect for the argument at the specified index, or `this` if + * `index` is `-1`. + * + * This helper predicate makes it easy to join on both of these columns at once, avoiding + * pathological join orders in case the argument index should get joined first. + */ + pragma[noinline] + final SideEffectInstruction getAParameterSideEffect(int index) { + this = result.getPrimaryInstruction() and + index = result.(IndexedInstruction).getIndex() + } } /** @@ -1980,6 +1994,14 @@ class PhiInstruction extends Instruction { */ pragma[noinline] final Instruction getAnInput() { result = this.getAnInputOperand().getDef() } + + /** + * Gets the input operand representing the value that flows from the specified predecessor block. + */ + final PhiInputOperand getInputOperand(IRBlock predecessorBlock) { + result = this.getAnOperand() and + result.getPredecessorBlock() = predecessorBlock + } } /** diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll index a2ce0662dc2..d7cf89ca9aa 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll @@ -28,11 +28,15 @@ class Operand extends TStageOperand { cached Operand() { // Ensure that the operand does not refer to instructions from earlier stages that are unreachable here - exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or - exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or + exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) + or + exists(Instruction use | this = nonSSAMemoryOperand(use, _)) + or exists(Instruction use, Instruction def, IRBlock predecessorBlock | - this = phiOperand(use, def, predecessorBlock, _) - ) or + this = phiOperand(use, def, predecessorBlock, _) or + this = reusedPhiOperand(use, def, predecessorBlock, _) + ) + or exists(Instruction use | this = chiOperand(use, _)) } @@ -431,7 +435,11 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand { Overlap overlap; cached - PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) } + PhiInputOperand() { + this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) + or + this = reusedPhiOperand(useInstr, defInstr, predecessorBlock, overlap) + } override string toString() { result = "Phi" } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll index 19fb0490f80..9997b5b49a7 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll @@ -4,6 +4,71 @@ private import AliasAnalysisImports private class IntValue = Ints::IntValue; +/** + * If `instr` is a `SideEffectInstruction`, gets the primary `CallInstruction` that caused the side + * effect. If `instr` is a `CallInstruction`, gets that same `CallInstruction`. + */ +private CallInstruction getPrimaryCall(Instruction instr) { + result = instr + or + result = instr.(SideEffectInstruction).getPrimaryInstruction() +} + +/** + * Holds if `operand` serves as an input argument (or indirection) to `call`, in the position + * specified by `input`. + */ +private predicate isCallInput( + CallInstruction call, Operand operand, AliasModels::FunctionInput input +) { + call = getPrimaryCall(operand.getUse()) and + ( + exists(int index | + input.isParameterOrQualifierAddress(index) and + operand = call.getArgumentOperand(index) + ) + or + exists(int index, ReadSideEffectInstruction read | + input.isParameterDerefOrQualifierObject(index) and + read = call.getAParameterSideEffect(index) and + operand = read.getSideEffectOperand() + ) + ) +} + +/** + * Holds if `instr` serves as a return value or output argument indirection for `call`, in the + * position specified by `output`. + */ +private predicate isCallOutput( + CallInstruction call, Instruction instr, AliasModels::FunctionOutput output +) { + call = getPrimaryCall(instr) and + ( + output.isReturnValue() and instr = call + or + exists(int index, WriteSideEffectInstruction write | + output.isParameterDerefOrQualifierObject(index) and + write = call.getAParameterSideEffect(index) and + instr = write + ) + ) +} + +/** + * Holds if the address in `operand` flows directly to the result of `resultInstr` due to modeled + * address flow through a function call. + */ +private predicate hasAddressFlowThroughCall(Operand operand, Instruction resultInstr) { + exists( + CallInstruction call, AliasModels::FunctionInput input, AliasModels::FunctionOutput output + | + call.getStaticCallTarget().(AliasModels::AliasFunction).hasAddressFlow(input, output) and + isCallInput(call, operand, input) and + isCallOutput(call, resultInstr, output) + ) +} + /** * Holds if the operand `tag` of instruction `instr` is used in a way that does * not result in any address held in that operand from escaping beyond the @@ -25,6 +90,9 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) { or // Converting an address to a `bool` does not escape the address. instr.(ConvertInstruction).getResultIRType() instanceof IRBooleanType + or + instr instanceof CallInstruction and + not exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis()) ) ) or @@ -34,7 +102,7 @@ private predicate operandIsConsumedWithoutEscaping(Operand operand) { private predicate operandEscapesDomain(Operand operand) { not operandIsConsumedWithoutEscaping(operand) and - not operandIsPropagated(operand, _) and + not operandIsPropagated(operand, _, _) and not isArgumentForParameter(_, operand, _) and not isOnlyEscapesViaReturnArgument(operand) and not operand.getUse() instanceof ReturnValueInstruction and @@ -69,67 +137,67 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) { } /** - * Holds if any address held in operand `tag` of instruction `instr` is - * propagated to the result of `instr`, offset by the number of bits in - * `bitOffset`. If the address is propagated, but the offset is not known to be - * a constant, then `bitOffset` is unknown. + * Holds if any address held in operand `operand` is propagated to the result of `instr`, offset by + * the number of bits in `bitOffset`. If the address is propagated, but the offset is not known to + * be a constant, then `bitOffset` is `unknown()`. */ -private predicate operandIsPropagated(Operand operand, IntValue bitOffset) { - exists(Instruction instr | - instr = operand.getUse() and - ( - // Converting to a non-virtual base class adds the offset of the base class. - exists(ConvertToNonVirtualBaseInstruction convert | - convert = instr and - bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8) - ) - or - // Conversion using dynamic_cast results in an unknown offset - instr instanceof CheckedConvertOrNullInstruction and - bitOffset = Ints::unknown() - or - // Converting to a derived class subtracts the offset of the base class. - exists(ConvertToDerivedInstruction convert | - convert = instr and - bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8)) - ) - or - // Converting to a virtual base class adds an unknown offset. - instr instanceof ConvertToVirtualBaseInstruction and - bitOffset = Ints::unknown() - or - // Conversion to another pointer type propagates the source address. - exists(ConvertInstruction convert, IRType resultType | - convert = instr and - resultType = convert.getResultIRType() and - resultType instanceof IRAddressType and - bitOffset = 0 - ) - or - // Adding an integer to or subtracting an integer from a pointer propagates - // the address with an offset. - exists(PointerOffsetInstruction ptrOffset | - ptrOffset = instr and - operand = ptrOffset.getLeftOperand() and - bitOffset = getPointerBitOffset(ptrOffset) - ) - or - // Computing a field address from a pointer propagates the address plus the - // offset of the field. - bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField()) - or - // A copy propagates the source value. - operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 - or - // Some functions are known to propagate an argument - isAlwaysReturnedArgument(operand) and bitOffset = 0 +private predicate operandIsPropagated(Operand operand, IntValue bitOffset, Instruction instr) { + // Some functions are known to propagate an argument + hasAddressFlowThroughCall(operand, instr) and + bitOffset = 0 + or + instr = operand.getUse() and + ( + // Converting to a non-virtual base class adds the offset of the base class. + exists(ConvertToNonVirtualBaseInstruction convert | + convert = instr and + bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8) ) + or + // Conversion using dynamic_cast results in an unknown offset + instr instanceof CheckedConvertOrNullInstruction and + bitOffset = Ints::unknown() + or + // Converting to a derived class subtracts the offset of the base class. + exists(ConvertToDerivedInstruction convert | + convert = instr and + bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8)) + ) + or + // Converting to a virtual base class adds an unknown offset. + instr instanceof ConvertToVirtualBaseInstruction and + bitOffset = Ints::unknown() + or + // Conversion to another pointer type propagates the source address. + exists(ConvertInstruction convert, IRType resultType | + convert = instr and + resultType = convert.getResultIRType() and + resultType instanceof IRAddressType and + bitOffset = 0 + ) + or + // Adding an integer to or subtracting an integer from a pointer propagates + // the address with an offset. + exists(PointerOffsetInstruction ptrOffset | + ptrOffset = instr and + operand = ptrOffset.getLeftOperand() and + bitOffset = getPointerBitOffset(ptrOffset) + ) + or + // Computing a field address from a pointer propagates the address plus the + // offset of the field. + bitOffset = Language::getFieldBitOffset(instr.(FieldAddressInstruction).getField()) + or + // A copy propagates the source value. + operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 ) } private predicate operandEscapesNonReturn(Operand operand) { - // The address is propagated to the result of the instruction, and that result itself is returned - operandIsPropagated(operand, _) and resultEscapesNonReturn(operand.getUse()) + exists(Instruction instr | + // The address is propagated to the result of the instruction, and that result itself is returned + operandIsPropagated(operand, _, instr) and resultEscapesNonReturn(instr) + ) or // The operand is used in a function call which returns it, and the return value is then returned exists(CallInstruction ci, Instruction init | @@ -151,9 +219,11 @@ private predicate operandEscapesNonReturn(Operand operand) { } private predicate operandMayReachReturn(Operand operand) { - // The address is propagated to the result of the instruction, and that result itself is returned - operandIsPropagated(operand, _) and - resultMayReachReturn(operand.getUse()) + exists(Instruction instr | + // The address is propagated to the result of the instruction, and that result itself is returned + operandIsPropagated(operand, _, instr) and + resultMayReachReturn(instr) + ) or // The operand is used in a function call which returns it, and the return value is then returned exists(CallInstruction ci, Instruction init | @@ -173,9 +243,9 @@ private predicate operandMayReachReturn(Operand operand) { private predicate operandReturned(Operand operand, IntValue bitOffset) { // The address is propagated to the result of the instruction, and that result itself is returned - exists(IntValue bitOffset1, IntValue bitOffset2 | - operandIsPropagated(operand, bitOffset1) and - resultReturned(operand.getUse(), bitOffset2) and + exists(Instruction instr, IntValue bitOffset1, IntValue bitOffset2 | + operandIsPropagated(operand, bitOffset1, instr) and + resultReturned(instr, bitOffset2) and bitOffset = Ints::add(bitOffset1, bitOffset2) ) or @@ -214,24 +284,27 @@ private predicate isArgumentForParameter( ) } -private predicate isAlwaysReturnedArgument(Operand operand) { - exists(AliasModels::AliasFunction f | - f = operand.getUse().(CallInstruction).getStaticCallTarget() and - f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex()) - ) -} - private predicate isOnlyEscapesViaReturnArgument(Operand operand) { exists(AliasModels::AliasFunction f | f = operand.getUse().(CallInstruction).getStaticCallTarget() and - f.parameterEscapesOnlyViaReturn(operand.(PositionalArgumentOperand).getIndex()) + ( + f.parameterEscapesOnlyViaReturn(operand.(PositionalArgumentOperand).getIndex()) + or + f.parameterEscapesOnlyViaReturn(-1) and + operand instanceof ThisArgumentOperand + ) ) } private predicate isNeverEscapesArgument(Operand operand) { exists(AliasModels::AliasFunction f | f = operand.getUse().(CallInstruction).getStaticCallTarget() and - f.parameterNeverEscapes(operand.(PositionalArgumentOperand).getIndex()) + ( + f.parameterNeverEscapes(operand.(PositionalArgumentOperand).getIndex()) + or + f.parameterNeverEscapes(-1) and + operand instanceof ThisArgumentOperand + ) ) } @@ -265,17 +338,23 @@ predicate allocationEscapes(Configuration::Allocation allocation) { exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) ) + or + Configuration::phaseNeedsSoundEscapeAnalysis() and + resultEscapesNonReturn(allocation.getABaseInstruction()) } /** * Equivalent to `operandIsPropagated()`, but includes interprocedural propagation. */ -private predicate operandIsPropagatedIncludingByCall(Operand operand, IntValue bitOffset) { - operandIsPropagated(operand, bitOffset) +private predicate operandIsPropagatedIncludingByCall( + Operand operand, IntValue bitOffset, Instruction instr +) { + operandIsPropagated(operand, bitOffset, instr) or exists(CallInstruction call, Instruction init | isArgumentForParameter(call, operand, init) and - resultReturned(init, bitOffset) + resultReturned(init, bitOffset) and + instr = call ) } @@ -292,8 +371,7 @@ private predicate hasBaseAndOffset(AddressOperand addrOperand, Instruction base, // We already have an offset from `middle`. hasBaseAndOffset(addrOperand, middle, previousBitOffset) and // `middle` is propagated from `base`. - middleOperand = middle.getAnOperand() and - operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset) and + operandIsPropagatedIncludingByCall(middleOperand, additionalBitOffset, middle) and base = middleOperand.getDef() and bitOffset = Ints::add(previousBitOffset, additionalBitOffset) ) @@ -333,8 +411,51 @@ predicate addressOperandAllocationAndOffset( allocation.getABaseInstruction() = base and hasBaseAndOffset(addrOperand, base, bitOffset) and not exists(Instruction previousBase | - hasBaseAndOffset(addrOperand, previousBase, _) and + hasBaseAndOffset(addrOperand, pragma[only_bind_out](previousBase), _) and previousBase = base.getAnOperand().getDef() ) ) } + +/** + * Predicates used only for printing annotated IR dumps. These should not be used in production + * queries. + */ +module Print { + string getOperandProperty(Operand operand, string key) { + key = "alloc" and + result = + strictconcat(Configuration::Allocation allocation, IntValue bitOffset | + addressOperandAllocationAndOffset(operand, allocation, bitOffset) + | + allocation.toString() + Ints::getBitOffsetString(bitOffset), ", " + ) + or + key = "prop" and + result = + strictconcat(Instruction destInstr, IntValue bitOffset, string value | + operandIsPropagatedIncludingByCall(operand, bitOffset, destInstr) and + if destInstr = operand.getUse() + then value = "@" + Ints::getBitOffsetString(bitOffset) + "->result" + else value = "@" + Ints::getBitOffsetString(bitOffset) + "->" + destInstr.getResultId() + | + value, ", " + ) + } + + string getInstructionProperty(Instruction instr, string key) { + key = "prop" and + result = + strictconcat(IntValue bitOffset, Operand sourceOperand, string value | + operandIsPropagatedIncludingByCall(sourceOperand, bitOffset, instr) and + if instr = sourceOperand.getUse() + then value = sourceOperand.getDumpId() + Ints::getBitOffsetString(bitOffset) + "->@" + else + value = + sourceOperand.getUse().getResultId() + "." + sourceOperand.getDumpId() + + Ints::getBitOffsetString(bitOffset) + "->@" + | + value, ", " + ) + } +} diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll index 7992aa9ed14..e0bf271dcc7 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll @@ -3,6 +3,276 @@ import experimental.ir.implementation.IRConfiguration import experimental.ir.internal.IntegerConstant as Ints module AliasModels { + class ParameterIndex = int; + + /** + * An input to a function. This can be: + * - The value of one of the function's parameters + * - The value pointed to by one of function's pointer or reference parameters + * - The value of the function's `this` pointer + * - The value pointed to by the function's `this` pointer + */ + abstract class FunctionInput extends string { + FunctionInput() { none() } + + /** + * Holds if this is the input value of the parameter with index `index`. + * + * Example: + * ``` + * void func(int n, char* p, float& r); + * ``` + * - `isParameter(0)` holds for the `FunctionInput` that represents the value of `n` (with type + * `int`) on entry to the function. + * - `isParameter(1)` holds for the `FunctionInput` that represents the value of `p` (with type + * `char*`) on entry to the function. + * - `isParameter(2)` holds for the `FunctionInput` that represents the "value" of the reference + * `r` (with type `float&`) on entry to the function, _not_ the value of the referred-to + * `float`. + */ + predicate isParameter(ParameterIndex index) { none() } + + /** + * Holds if this is the input value of the parameter with index `index`. + * DEPRECATED: Use `isParameter(index)` instead. + */ + deprecated final predicate isInParameter(ParameterIndex index) { isParameter(index) } + + /** + * Holds if this is the input value pointed to by a pointer parameter to a function, or the input + * value referred to by a reference parameter to a function, where the parameter has index + * `index`. + * + * Example: + * ``` + * void func(int n, char* p, float& r); + * ``` + * - `isParameterDeref(1)` holds for the `FunctionInput` that represents the value of `*p` (with + * type `char`) on entry to the function. + * - `isParameterDeref(2)` holds for the `FunctionInput` that represents the value of `r` (with type + * `float`) on entry to the function. + * - There is no `FunctionInput` for which `isParameterDeref(0)` holds, because `n` is neither a + * pointer nor a reference. + */ + predicate isParameterDeref(ParameterIndex index) { none() } + + /** + * Holds if this is the input value pointed to by a pointer parameter to a function, or the input + * value referred to by a reference parameter to a function, where the parameter has index + * `index`. + * DEPRECATED: Use `isParameterDeref(index)` instead. + */ + deprecated final predicate isInParameterPointer(ParameterIndex index) { + isParameterDeref(index) + } + + /** + * Holds if this is the input value pointed to by the `this` pointer of an instance member + * function. + * + * Example: + * ``` + * struct C { + * void mfunc(int n, char* p, float& r) const; + * }; + * ``` + * - `isQualifierObject()` holds for the `FunctionInput` that represents the value of `*this` + * (with type `C const`) on entry to the function. + */ + predicate isQualifierObject() { none() } + + /** + * Holds if this is the input value pointed to by the `this` pointer of an instance member + * function. + * DEPRECATED: Use `isQualifierObject()` instead. + */ + deprecated final predicate isInQualifier() { isQualifierObject() } + + /** + * Holds if this is the input value of the `this` pointer of an instance member function. + * + * Example: + * ``` + * struct C { + * void mfunc(int n, char* p, float& r) const; + * }; + * ``` + * - `isQualifierAddress()` holds for the `FunctionInput` that represents the value of `this` + * (with type `C const *`) on entry to the function. + */ + predicate isQualifierAddress() { none() } + + /** + * Holds if `i >= 0` and `isParameter(i)` holds for this value, or + * if `i = -1` and `isQualifierAddress()` holds for this value. + */ + final predicate isParameterOrQualifierAddress(ParameterIndex i) { + i >= 0 and this.isParameter(i) + or + i = -1 and this.isQualifierAddress() + } + + /** + * Holds if this is the input value pointed to by the return value of a + * function, if the function returns a pointer, or the input value referred + * to by the return value of a function, if the function returns a reference. + * + * Example: + * ``` + * char* getPointer(); + * float& getReference(); + * int getInt(); + * ``` + * - `isReturnValueDeref()` holds for the `FunctionInput` that represents the + * value of `*getPointer()` (with type `char`). + * - `isReturnValueDeref()` holds for the `FunctionInput` that represents the + * value of `getReference()` (with type `float`). + * - There is no `FunctionInput` of `getInt()` for which + * `isReturnValueDeref()` holds because the return type of `getInt()` is + * neither a pointer nor a reference. + * + * Note that data flows in through function return values are relatively + * rare, but they do occur when a function returns a reference to itself, + * part of itself, or one of its other inputs. + */ + predicate isReturnValueDeref() { none() } + + /** + * Holds if `i >= 0` and `isParameterDeref(i)` holds for this value, or + * if `i = -1` and `isQualifierObject()` holds for this value. + */ + final predicate isParameterDerefOrQualifierObject(ParameterIndex i) { + i >= 0 and this.isParameterDeref(i) + or + i = -1 and this.isQualifierObject() + } + } + + /** + * An output from a function. This can be: + * - The value pointed to by one of function's pointer or reference parameters + * - The value pointed to by the function's `this` pointer + * - The function's return value + * - The value pointed to by the function's return value, if the return value is a pointer or + * reference + */ + abstract class FunctionOutput extends string { + FunctionOutput() { none() } + + /** + * Holds if this is the output value pointed to by a pointer parameter to a function, or the + * output value referred to by a reference parameter to a function, where the parameter has + * index `index`. + * + * Example: + * ``` + * void func(int n, char* p, float& r); + * ``` + * - `isParameterDeref(1)` holds for the `FunctionOutput` that represents the value of `*p` (with + * type `char`) on return from the function. + * - `isParameterDeref(2)` holds for the `FunctionOutput` that represents the value of `r` (with + * type `float`) on return from the function. + * - There is no `FunctionOutput` for which `isParameterDeref(0)` holds, because `n` is neither a + * pointer nor a reference. + */ + predicate isParameterDeref(ParameterIndex i) { none() } + + /** + * Holds if this is the output value pointed to by a pointer parameter to a function, or the + * output value referred to by a reference parameter to a function, where the parameter has + * index `index`. + * DEPRECATED: Use `isParameterDeref(index)` instead. + */ + deprecated final predicate isOutParameterPointer(ParameterIndex index) { + isParameterDeref(index) + } + + /** + * Holds if this is the output value pointed to by the `this` pointer of an instance member + * function. + * + * Example: + * ``` + * struct C { + * void mfunc(int n, char* p, float& r); + * }; + * ``` + * - `isQualifierObject()` holds for the `FunctionOutput` that represents the value of `*this` + * (with type `C`) on return from the function. + */ + predicate isQualifierObject() { none() } + + /** + * Holds if this is the output value pointed to by the `this` pointer of an instance member + * function. + * DEPRECATED: Use `isQualifierObject()` instead. + */ + deprecated final predicate isOutQualifier() { isQualifierObject() } + + /** + * Holds if this is the value returned by a function. + * + * Example: + * ``` + * int getInt(); + * char* getPointer(); + * float& getReference(); + * ``` + * - `isReturnValue()` holds for the `FunctionOutput` that represents the value returned by + * `getInt()` (with type `int`). + * - `isReturnValue()` holds for the `FunctionOutput` that represents the value returned by + * `getPointer()` (with type `char*`). + * - `isReturnValue()` holds for the `FunctionOutput` that represents the "value" of the reference + * returned by `getReference()` (with type `float&`), _not_ the value of the referred-to + * `float`. + */ + predicate isReturnValue() { none() } + + /** + * Holds if this is the value returned by a function. + * DEPRECATED: Use `isReturnValue()` instead. + */ + deprecated final predicate isOutReturnValue() { isReturnValue() } + + /** + * Holds if this is the output value pointed to by the return value of a function, if the function + * returns a pointer, or the output value referred to by the return value of a function, if the + * function returns a reference. + * + * Example: + * ``` + * char* getPointer(); + * float& getReference(); + * int getInt(); + * ``` + * - `isReturnValueDeref()` holds for the `FunctionOutput` that represents the value of + * `*getPointer()` (with type `char`). + * - `isReturnValueDeref()` holds for the `FunctionOutput` that represents the value of + * `getReference()` (with type `float`). + * - There is no `FunctionOutput` of `getInt()` for which `isReturnValueDeref()` holds because the + * return type of `getInt()` is neither a pointer nor a reference. + */ + predicate isReturnValueDeref() { none() } + + /** + * Holds if this is the output value pointed to by the return value of a function, if the function + * returns a pointer, or the output value referred to by the return value of a function, if the + * function returns a reference. + * DEPRECATED: Use `isReturnValueDeref()` instead. + */ + deprecated final predicate isOutReturnPointer() { isReturnValueDeref() } + + /** + * Holds if `i >= 0` and `isParameterDeref(i)` holds for this is the value, or + * if `i = -1` and `isQualifierObject()` holds for this value. + */ + final predicate isParameterDerefOrQualifierObject(ParameterIndex i) { + i >= 0 and this.isParameterDeref(i) + or + i = -1 and this.isQualifierObject() + } + } + /** * Models the aliasing behavior of a library function. */ @@ -44,5 +314,10 @@ module AliasModels { * Holds if the function always returns the value of the parameter at the specified index. */ abstract predicate parameterIsAlwaysReturned(int index); + + /** + * Holds if the address passed in via `input` is always propagated to `output`. + */ + abstract predicate hasAddressFlow(FunctionInput input, FunctionOutput output); } } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll index 5be476e12ee..dbdd3c14c85 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll @@ -14,3 +14,5 @@ class Allocation extends IRAutomaticVariable { none() } } + +predicate phaseNeedsSoundEscapeAnalysis() { any() } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 340f524fce8..5092e921cb3 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -43,24 +43,81 @@ private module Cached { class TStageInstruction = TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction; + /** + * If `oldInstruction` is a `Phi` instruction that has exactly one reachable predecessor block, + * this predicate returns the `PhiInputOperand` corresponding to that predecessor block. + * Otherwise, this predicate does not hold. + */ + private OldIR::PhiInputOperand getDegeneratePhiOperand(OldInstruction oldInstruction) { + result = + unique(OldIR::PhiInputOperand operand | + operand = oldInstruction.(OldIR::PhiInstruction).getAnInputOperand() and + operand.getPredecessorBlock() instanceof OldBlock + ) + } + cached predicate hasInstruction(TStageInstruction instr) { instr instanceof TRawInstruction and instr instanceof OldInstruction or - instr instanceof TPhiInstruction + instr = phiInstruction(_, _) + or + instr = reusedPhiInstruction(_) and + // Check that the phi instruction is *not* degenerate, but we can't use + // getDegeneratePhiOperand in the first stage with phi instyructions + not exists( + unique(OldIR::PhiInputOperand operand | + operand = instr.(OldIR::PhiInstruction).getAnInputOperand() and + operand.getPredecessorBlock() instanceof OldBlock + ) + ) or instr instanceof TChiInstruction or instr instanceof TUnreachedInstruction } - private IRBlock getNewBlock(OldBlock oldBlock) { - result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction()) + cached + IRBlock getNewBlock(OldBlock oldBlock) { + exists(Instruction newEnd, OldIR::Instruction oldEnd | + ( + result.getLastInstruction() = newEnd and + not newEnd instanceof ChiInstruction + or + newEnd = result.getLastInstruction().(ChiInstruction).getAPredecessor() // does this work? + ) and + ( + oldBlock.getLastInstruction() = oldEnd and + not oldEnd instanceof OldIR::ChiInstruction + or + oldEnd = oldBlock.getLastInstruction().(OldIR::ChiInstruction).getAPredecessor() // does this work? + ) and + oldEnd = getNewInstruction(newEnd) + ) + } + + /** + * Gets the block from the old IR that corresponds to `newBlock`. + */ + private OldBlock getOldBlock(IRBlock newBlock) { getNewBlock(result) = newBlock } + + /** + * Holds if this iteration of SSA can model the def/use information for the result of + * `oldInstruction`, either because alias analysis has determined a memory location for that + * result, or because a previous iteration of the IR already computed that def/use information + * completely. + */ + private predicate canModelResultForOldInstruction(OldInstruction oldInstruction) { + // We're modeling the result's memory location ourselves. + exists(Alias::getResultMemoryLocation(oldInstruction)) + or + // This result was already modeled by a previous iteration of SSA. + Alias::canReuseSSAForOldResult(oldInstruction) } cached predicate hasModeledMemoryResult(Instruction instruction) { - exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or + canModelResultForOldInstruction(getOldInstruction(instruction)) or instruction instanceof PhiInstruction or // Phis always have modeled results instruction instanceof ChiInstruction // Chis always have modeled results } @@ -117,6 +174,32 @@ private module Cached { ) } + /** + * Gets the new definition instruction for `oldOperand` based on `oldOperand`'s definition in the + * old IR. Usually, this will just get the old definition of `oldOperand` and map it to the + * corresponding new instruction. However, if the old definition of `oldOperand` is a `Phi` + * instruction that is now degenerate due all but one of its predecessor branches being + * unreachable, this predicate will recurse through any degenerate `Phi` instructions to find the + * true definition. + */ + private Instruction getNewDefinitionFromOldSSA(OldIR::MemoryOperand oldOperand, Overlap overlap) { + exists(Overlap originalOverlap | + originalOverlap = oldOperand.getDefinitionOverlap() and + ( + result = getNewInstruction(oldOperand.getAnyDef()) and + overlap = originalOverlap + or + exists(OldIR::PhiInputOperand phiOperand, Overlap phiOperandOverlap | + phiOperand = getDegeneratePhiOperand(oldOperand.getAnyDef()) and + result = getNewDefinitionFromOldSSA(phiOperand, phiOperandOverlap) and + overlap = + combineOverlap(pragma[only_bind_out](phiOperandOverlap), + pragma[only_bind_out](originalOverlap)) + ) + ) + ) + } + cached private Instruction getMemoryOperandDefinition0( Instruction instruction, MemoryOperandTag tag, Overlap overlap @@ -148,6 +231,12 @@ private module Cached { overlap instanceof MustExactlyOverlap and exists(MustTotallyOverlap o | exists(getMemoryOperandDefinition0(instruction, tag, o))) ) + or + exists(OldIR::NonPhiMemoryOperand oldOperand | + result = getNewDefinitionFromOldSSA(oldOperand, overlap) and + oldOperand.getUse() = instruction and + tag = oldOperand.getOperandTag() + ) } /** @@ -214,10 +303,24 @@ private module Cached { ) } + /** + * Gets the new definition instruction for the operand of `instr` that flows from the block + * `newPredecessorBlock`, based on that operand's definition in the old IR. + */ + private Instruction getNewPhiOperandDefinitionFromOldSSA( + Instruction instr, IRBlock newPredecessorBlock, Overlap overlap + ) { + exists(OldIR::PhiInstruction oldPhi, OldIR::PhiInputOperand oldOperand | + oldPhi = getOldInstruction(instr) and + oldOperand = oldPhi.getInputOperand(getOldBlock(newPredecessorBlock)) and + result = getNewDefinitionFromOldSSA(oldOperand, overlap) + ) + } + pragma[noopt] cached Instruction getPhiOperandDefinition( - PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap + Instruction instr, IRBlock newPredecessorBlock, Overlap overlap ) { exists( Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock, @@ -229,6 +332,8 @@ private module Cached { result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and overlap = Alias::getOverlap(actualDefLocation, useLocation) ) + or + result = getNewPhiOperandDefinitionFromOldSSA(instr, newPredecessorBlock, overlap) } cached @@ -249,7 +354,12 @@ private module Cached { cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) { exists(OldBlock oldBlock | - instr = getPhi(oldBlock, _) and + ( + instr = getPhi(oldBlock, _) + or + // Any `Phi` that we propagated from the previous iteration stays in the same block. + getOldInstruction(instr).getBlock() = oldBlock + ) and result = getNewInstruction(oldBlock.getFirstInstruction()) ) } @@ -335,18 +445,27 @@ private module Cached { result = vvar.getType() ) or + instr = reusedPhiInstruction(_) and + result = instr.(OldInstruction).getResultLanguageType() + or instr = unreachedInstruction(_) and result = Language::getVoidType() } + /** + * Holds if `opcode` is the opcode that specifies the operation performed by `instr`. + * + * The parameters are ordered such that they produce a clean join (with no need for reordering) + * in the characteristic predicates of the `Instruction` subclasses. + */ cached - Opcode getInstructionOpcode(Instruction instr) { - result = getOldInstruction(instr).getOpcode() + predicate getInstructionOpcode(Opcode opcode, Instruction instr) { + opcode = getOldInstruction(instr).getOpcode() or - instr = phiInstruction(_, _) and result instanceof Opcode::Phi + instr = phiInstruction(_, _) and opcode instanceof Opcode::Phi or - instr = chiInstruction(_) and result instanceof Opcode::Chi + instr = chiInstruction(_) and opcode instanceof Opcode::Chi or - instr = unreachedInstruction(_) and result instanceof Opcode::Unreached + instr = unreachedInstruction(_) and opcode instanceof Opcode::Unreached } cached @@ -856,6 +975,26 @@ module DefUse { } } +predicate canReuseSSAForMemoryResult(Instruction instruction) { + exists(OldInstruction oldInstruction | + oldInstruction = getOldInstruction(instruction) and + ( + // The previous iteration said it was reusable, so we should mark it as reusable as well. + Alias::canReuseSSAForOldResult(oldInstruction) + or + // The current alias analysis says it is reusable. + Alias::getResultMemoryLocation(oldInstruction).canReuseSSA() + ) + ) + or + exists(Alias::MemoryLocation defLocation | + // This is a `Phi` for a reusable location, so the result of the `Phi` is reusable as well. + instruction = phiInstruction(_, defLocation) and + defLocation.canReuseSSA() + ) + // We don't support reusing SSA for any location that could create a `Chi` instruction. +} + /** * Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the * `DebugSSA` module, which is then imported by PrintSSA. @@ -934,7 +1073,10 @@ module SSAConsistency { locationCount > 1 and func = operand.getEnclosingIRFunction() and funcText = Language::getIdentityString(func.getFunction()) and - message = "Operand has " + locationCount.toString() + " memory accesses in function '$@'." + message = + operand.getUse().toString() + " " + "Operand has " + locationCount.toString() + + " memory accesses in function '$@': " + + strictconcat(Alias::getOperandMemoryLocation(operand).toString(), ", ") ) } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll index a7b9160bdc7..f3e02c9f6a8 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll @@ -17,7 +17,7 @@ private predicate isTotalAccess(Allocation var, AddressOperand addrOperand, IRTy * variable if its address never escapes and all reads and writes of that variable access the entire * variable using the original type of the variable. */ -private predicate isVariableModeled(Allocation var) { +predicate isVariableModeled(Allocation var) { not allocationEscapes(var) and forall(Instruction instr, AddressOperand addrOperand, IRType type | addrOperand = instr.getResultAddressOperand() and @@ -35,6 +35,17 @@ private predicate isVariableModeled(Allocation var) { ) } +/** + * Holds if the SSA use/def chain for the specified variable can be safely reused by later + * iterations of SSA construction. This will hold only if we modeled the variable soundly, so that + * subsequent iterations will recompute SSA for any variable that we assumed did not escape, but + * actually would have escaped if we had used a sound escape analysis. + */ +predicate canReuseSSAForVariable(IRAutomaticVariable var) { + isVariableModeled(var) and + not allocationEscapes(var) +} + private newtype TMemoryLocation = MkMemoryLocation(Allocation var) { isVariableModeled(var) } private MemoryLocation getMemoryLocation(Allocation var) { result.getAllocation() = var } @@ -57,8 +68,12 @@ class MemoryLocation extends TMemoryLocation { final Language::LanguageType getType() { result = var.getLanguageType() } final string getUniqueId() { result = var.getUniqueId() } + + final predicate canReuseSSA() { canReuseSSAForVariable(var) } } +predicate canReuseSSAForOldResult(Instruction instr) { none() } + /** * Represents a set of `MemoryLocation`s that cannot overlap with * `MemoryLocation`s outside of the set. The `VirtualVariable` will be diff --git a/csharp/ql/src/experimental/ir/internal/IRGuards.qll b/csharp/ql/src/experimental/ir/internal/IRGuards.qll index a505e54c37e..d01dcbed1e1 100644 --- a/csharp/ql/src/experimental/ir/internal/IRGuards.qll +++ b/csharp/ql/src/experimental/ir/internal/IRGuards.qll @@ -151,17 +151,17 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition { ) } - override predicate comparesEq(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) { + override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) { exists(boolean partIsTrue, GuardCondition part | impliesValue(this.(BinaryLogicalOperation), part, partIsTrue, testIsTrue) | - part.comparesEq(left, right, k, isLessThan, partIsTrue) + part.comparesEq(left, right, k, areEqual, partIsTrue) ) } - override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) { + override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) { exists(boolean testIsTrue | - comparesEq(left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue) + comparesEq(left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue) ) } } @@ -180,20 +180,20 @@ private class GuardConditionFromShortCircuitNot extends GuardCondition, LogicalN getOperand().(GuardCondition).controls(controlled, testIsTrue.booleanNot()) } - override predicate comparesLt(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) { - getOperand().(GuardCondition).comparesLt(left, right, k, areEqual, testIsTrue.booleanNot()) + override predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) { + getOperand().(GuardCondition).comparesLt(left, right, k, isLessThan, testIsTrue.booleanNot()) } - override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean testIsTrue) { - getOperand().(GuardCondition).ensuresLt(left, right, k, block, testIsTrue.booleanNot()) + override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) { + getOperand().(GuardCondition).ensuresLt(left, right, k, block, isLessThan.booleanNot()) } override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) { getOperand().(GuardCondition).comparesEq(left, right, k, areEqual, testIsTrue.booleanNot()) } - override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean testIsTrue) { - getOperand().(GuardCondition).ensuresEq(left, right, k, block, testIsTrue.booleanNot()) + override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) { + getOperand().(GuardCondition).ensuresEq(left, right, k, block, areEqual.booleanNot()) } } diff --git a/csharp/ql/src/experimental/ir/internal/Overlap.qll b/csharp/ql/src/experimental/ir/internal/Overlap.qll index f9a0c574f8c..ca643b56cbb 100644 --- a/csharp/ql/src/experimental/ir/internal/Overlap.qll +++ b/csharp/ql/src/experimental/ir/internal/Overlap.qll @@ -8,6 +8,16 @@ private newtype TOverlap = */ abstract class Overlap extends TOverlap { abstract string toString(); + + /** + * Gets a value representing how precise this overlap is. The higher the value, the more precise + * the overlap. The precision values are ordered as + * follows, from most to least precise: + * `MustExactlyOverlap` + * `MustTotallyOverlap` + * `MayPartiallyOverlap` + */ + abstract int getPrecision(); } /** @@ -16,6 +26,8 @@ abstract class Overlap extends TOverlap { */ class MayPartiallyOverlap extends Overlap, TMayPartiallyOverlap { final override string toString() { result = "MayPartiallyOverlap" } + + final override int getPrecision() { result = 0 } } /** @@ -24,6 +36,8 @@ class MayPartiallyOverlap extends Overlap, TMayPartiallyOverlap { */ class MustTotallyOverlap extends Overlap, TMustTotallyOverlap { final override string toString() { result = "MustTotallyOverlap" } + + final override int getPrecision() { result = 1 } } /** @@ -32,4 +46,25 @@ class MustTotallyOverlap extends Overlap, TMustTotallyOverlap { */ class MustExactlyOverlap extends Overlap, TMustExactlyOverlap { final override string toString() { result = "MustExactlyOverlap" } + + final override int getPrecision() { result = 2 } +} + +/** + * Gets the `Overlap` that best represents the relationship between two memory locations `a` and + * `c`, where `getOverlap(a, b) = previousOverlap` and `getOverlap(b, c) = newOverlap`, for some + * intermediate memory location `b`. + */ +Overlap combineOverlap(Overlap previousOverlap, Overlap newOverlap) { + // Note that it's possible that two less precise overlaps could combine to result in a more + // precise overlap. For example, both `previousOverlap` and `newOverlap` could be + // `MustTotallyOverlap` even though the actual relationship between `a` and `c` is + // `MustExactlyOverlap`. We will still return `MustTotallyOverlap` as the best conservative + // approximation we can make without additional input information. + result = + min(Overlap overlap | + overlap = [previousOverlap, newOverlap] + | + overlap order by overlap.getPrecision() + ) } diff --git a/csharp/ql/src/semmle/code/cil/internal/SsaImplCommon.qll b/csharp/ql/src/semmle/code/cil/internal/SsaImplCommon.qll index f828419a440..f37a4f2d074 100644 --- a/csharp/ql/src/semmle/code/cil/internal/SsaImplCommon.qll +++ b/csharp/ql/src/semmle/code/cil/internal/SsaImplCommon.qll @@ -316,6 +316,15 @@ private module SsaDefReaches { ) } + /** + * Holds if the reference to `def` at index `i` in basic block `bb` is the + * last reference to `v` inside `bb`. + */ + pragma[noinline] + predicate lastSsaRef(Definition def, SourceVariable v, BasicBlock bb, int i) { + ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) + } + predicate defOccursInBlock(Definition def, BasicBlock bb, SourceVariable v) { exists(ssaDefRank(def, v, bb, _, _)) } @@ -351,8 +360,7 @@ private module SsaDefReaches { */ predicate defAdjacentRead(Definition def, BasicBlock bb1, BasicBlock bb2, int i2) { varBlockReaches(def, bb1, bb2) and - ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 and - variableRead(bb2, i2, _, _) + ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 } } @@ -434,15 +442,22 @@ predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2 bb2 = bb1 ) or - exists(SourceVariable v | ssaDefRank(def, v, bb1, i1, _) = maxSsaRefRank(bb1, v)) and + lastSsaRef(def, _, bb1, i1) and defAdjacentRead(def, bb1, bb2, i2) } +pragma[noinline] +private predicate adjacentDefRead( + Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v +) { + adjacentDefRead(def, bb1, i1, bb2, i2) and + v = def.getSourceVariable() +} + private predicate adjacentDefReachesRead( Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2 ) { - adjacentDefRead(def, bb1, i1, bb2, i2) and - exists(SourceVariable v | v = def.getSourceVariable() | + exists(SourceVariable v | adjacentDefRead(def, bb1, i1, bb2, i2, v) | ssaRef(bb1, i1, v, SsaDef()) or variableRead(bb1, i1, v, true) @@ -475,17 +490,19 @@ predicate adjacentDefNoUncertainReads(Definition def, BasicBlock bb1, int i1, Ba */ pragma[nomagic] predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) { - exists(int rnk, SourceVariable v, int j | rnk = ssaDefRank(def, v, bb, i, _) | + exists(SourceVariable v | // Next reference to `v` inside `bb` is a write - next.definesAt(v, bb, j) and - rnk + 1 = ssaRefRank(bb, j, v, SsaDef()) + exists(int rnk, int j | + rnk = ssaDefRank(def, v, bb, i, _) and + next.definesAt(v, bb, j) and + rnk + 1 = ssaRefRank(bb, j, v, SsaDef()) + ) or // Can reach a write using one or more steps - rnk = maxSsaRefRank(bb, v) and + lastSsaRef(def, v, bb, i) and exists(BasicBlock bb2 | varBlockReaches(def, bb, bb2) and - next.definesAt(v, bb2, j) and - 1 = ssaRefRank(bb2, j, v, SsaDef()) + 1 = ssaDefRank(next, v, bb2, _, SsaDef()) ) ) } @@ -539,7 +556,8 @@ pragma[nomagic] predicate lastRef(Definition def, BasicBlock bb, int i) { lastRefRedef(def, bb, i, _) or - exists(SourceVariable v | ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) | + lastSsaRef(def, _, bb, i) and + ( // Can reach exit directly bb instanceof ExitBasicBlock or diff --git a/csharp/ql/src/semmle/code/csharp/Caching.qll b/csharp/ql/src/semmle/code/csharp/Caching.qll index 95ffbf9ffc9..92417e34586 100644 --- a/csharp/ql/src/semmle/code/csharp/Caching.qll +++ b/csharp/ql/src/semmle/code/csharp/Caching.qll @@ -47,41 +47,6 @@ module Stages { } } - cached - module DataFlowStage { - private import semmle.code.csharp.dataflow.internal.DataFlowPrivate - private import semmle.code.csharp.dataflow.internal.DataFlowImplCommon - private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate - - cached - predicate forceCachingInSameStage() { any() } - - cached - private predicate forceCachingInSameStageRev() { - defaultAdditionalTaintStep(_, _) - or - any(ArgumentNode n).argumentOf(_, _) - or - exists(any(DataFlow::Node n).getEnclosingCallable()) - or - exists(any(DataFlow::Node n).getControlFlowNode()) - or - exists(any(DataFlow::Node n).getType()) - or - exists(any(NodeImpl n).getDataFlowType()) - or - exists(any(DataFlow::Node n).getLocation()) - or - exists(any(DataFlow::Node n).toString()) - or - exists(any(OutNode n).getCall(_)) - or - exists(CallContext cc) - or - forceCachingInSameStageRev() - } - } - cached module UnificationStage { private import semmle.code.csharp.Unification diff --git a/csharp/ql/src/semmle/code/csharp/Element.qll b/csharp/ql/src/semmle/code/csharp/Element.qll index 521138db1b5..fbd96f6086d 100644 --- a/csharp/ql/src/semmle/code/csharp/Element.qll +++ b/csharp/ql/src/semmle/code/csharp/Element.qll @@ -27,9 +27,6 @@ class Element extends DotNet::Element, @element { /** Gets a location of this element, including sources and assemblies. */ override Location getALocation() { none() } - /** Holds if this element is from an assembly. */ - predicate fromLibrary() { this.getFile().fromLibrary() } - /** Gets the parent of this element, if any. */ Element getParent() { result.getAChild() = this } diff --git a/csharp/ql/src/semmle/code/csharp/Enclosing.qll b/csharp/ql/src/semmle/code/csharp/Enclosing.qll deleted file mode 100644 index ab106421196..00000000000 --- a/csharp/ql/src/semmle/code/csharp/Enclosing.qll +++ /dev/null @@ -1,83 +0,0 @@ -/** - * INTERNAL: Do not use. - * - * Provides efficient cached predicates for finding enclosing statements and callables. - * - * There are a number of difficulties. There can be expressions without - * enclosing statements (for example initialisers for fields and constructors) - * or enclosing callables (even if we consider constructor initialisers - * to be enclosed by constructors, field initialisers don't have callables). - * - * The only cases where a `Stmt` has an `Expr` parent are delegate and lambda - * expressions, which are both callable. - */ - -import Stmt -private import semmle.code.csharp.ExprOrStmtParent - -/** - * INTERNAL: Do not use. - */ -cached -module Internal { - /** - * INTERNAL: Do not use. - * - * Holds if `c` is the enclosing callable of statement `s`. - */ - cached - predicate enclosingCallable(Stmt s, Callable c) { - // Compute the enclosing callable for a statement. This walks up through - // enclosing statements until it hits a callable. It's unambiguous, since - // if a statement has no parent statement, it's either the method body - // or the body of an anonymous function declaration, in each of which cases the - // non-statement parent is in fact the enclosing callable. - c.getAChildStmt+() = s - } - - private Expr getAChildExpr(ExprOrStmtParent p) { - result = p.getAChildExpr() or - result = p.(AssignOperation).getExpandedAssignment() - } - - /** - * INTERNAL: Do not use. - * - * Holds if `s` is the enclosing statement of expression `e`. - */ - cached - predicate enclosingStmt(Expr e, Stmt s) { - // Compute the enclosing statement for an expression. Note that this need - // not exist, since expressions can occur in contexts where they have no - // enclosing statement (examples include field initialisers, both inline - // and explicit on constructor definitions, and annotation arguments). - getAChildExpr+(s) = e - } - - private predicate childExprOfCallable(Callable parent, Expr child) { - child = getAChildExpr(parent) - or - exists(Expr mid | childExprOfCallable(parent, mid) | - not mid instanceof Callable and - child = getAChildExpr(mid) - ) - } - - /** - * INTERNAL: Do not use. - * - * Holds if `c` is the enclosing callable of expression `e`. - */ - cached - predicate exprEnclosingCallable(Expr e, Callable c) { - // Compute the enclosing callable of an expression. Note that expressions in - // lambda functions should have the lambdas as enclosing callables, and their - // enclosing statement may be the same as the enclosing statement of the - // lambda; thus, it is *not* safe to go up to the enclosing statement and - // take its own enclosing callable. - childExprOfCallable(c, e) - or - not childExprOfCallable(_, e) and - exists(Stmt s | enclosingStmt(e, s) | enclosingCallable(s, c)) - } -} diff --git a/csharp/ql/src/semmle/code/csharp/ExprOrStmtParent.qll b/csharp/ql/src/semmle/code/csharp/ExprOrStmtParent.qll index c7c7bfe00e8..b6501477117 100644 --- a/csharp/ql/src/semmle/code/csharp/ExprOrStmtParent.qll +++ b/csharp/ql/src/semmle/code/csharp/ExprOrStmtParent.qll @@ -138,6 +138,54 @@ private module Cached { ) else expr_parent(child, i, parent) } + + private Expr getAChildExpr(ExprOrStmtParent parent) { + result = parent.getAChildExpr() or + result = parent.(AssignOperation).getExpandedAssignment() + } + + private ControlFlowElement getAChild(ExprOrStmtParent parent) { + result = getAChildExpr(parent) + or + result = parent.getAChildStmt() + } + + pragma[inline] + private ControlFlowElement enclosingStart(ControlFlowElement cfe) { + result = cfe + or + getAChild(result).(AnonymousFunctionExpr) = cfe + } + + private predicate parent(ControlFlowElement child, ExprOrStmtParent parent) { + child = getAChild(parent) and + not child instanceof Callable + } + + /** Holds if the enclosing body of `cfe` is `body`. */ + cached + predicate enclosingBody(ControlFlowElement cfe, ControlFlowElement body) { + body = any(Callable c).getBody() and + parent*(enclosingStart(cfe), body) + } + + /** Holds if the enclosing callable of `cfe` is `c`. */ + cached + predicate enclosingCallable(ControlFlowElement cfe, Callable c) { + enclosingBody(cfe, c.getBody()) + or + parent*(enclosingStart(cfe), c.(Constructor).getInitializer()) + } + + /** Holds if the enclosing statement of expression `e` is `s`. */ + cached + predicate enclosingStmt(Expr e, Stmt s) { + // Compute the enclosing statement for an expression. Note that this need + // not exist, since expressions can occur in contexts where they have no + // enclosing statement (examples include field initialisers, both inline + // and explicit on constructor definitions, and annotation arguments). + getAChildExpr+(s) = e + } } import Cached diff --git a/csharp/ql/src/semmle/code/csharp/File.qll b/csharp/ql/src/semmle/code/csharp/File.qll index 86656af4124..d8b23bb61f4 100644 --- a/csharp/ql/src/semmle/code/csharp/File.qll +++ b/csharp/ql/src/semmle/code/csharp/File.qll @@ -192,7 +192,7 @@ class File extends Container, @file { override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" } /** Holds if this file contains source code. */ - predicate fromSource() { this.getNumberOfLinesOfCode() > 0 } + predicate fromSource() { files(this, _, _, "cs", _) } /** Holds if this file is a library. */ predicate fromLibrary() { diff --git a/csharp/ql/src/semmle/code/csharp/Member.qll b/csharp/ql/src/semmle/code/csharp/Member.qll index 520df43cfb8..65fe94a0c50 100644 --- a/csharp/ql/src/semmle/code/csharp/Member.qll +++ b/csharp/ql/src/semmle/code/csharp/Member.qll @@ -86,7 +86,12 @@ class Modifiable extends Declaration, @modifiable { predicate isConst() { this.hasModifier("const") } /** Holds if this declaration is `unsafe`. */ - predicate isUnsafe() { this.hasModifier("unsafe") } + predicate isUnsafe() { + this.hasModifier("unsafe") or + this.(Parameterizable).getAParameter().getType() instanceof PointerType or + this.(Property).getType() instanceof PointerType or + this.(Callable).getReturnType() instanceof PointerType + } /** Holds if this declaration is `async`. */ predicate isAsync() { this.hasModifier("async") } diff --git a/csharp/ql/src/semmle/code/csharp/Stmt.qll b/csharp/ql/src/semmle/code/csharp/Stmt.qll index 375e698d1cb..2ccd57078db 100644 --- a/csharp/ql/src/semmle/code/csharp/Stmt.qll +++ b/csharp/ql/src/semmle/code/csharp/Stmt.qll @@ -8,7 +8,7 @@ import Element import Location import Member import exprs.Expr -private import semmle.code.csharp.Enclosing::Internal +private import semmle.code.csharp.ExprOrStmtParent private import semmle.code.csharp.frameworks.System private import TypeRef diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/BasicBlocks.qll b/csharp/ql/src/semmle/code/csharp/controlflow/BasicBlocks.qll index 8b20ccb3c22..b4448a71380 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/BasicBlocks.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/BasicBlocks.qll @@ -403,7 +403,7 @@ private module JoinBlockPredecessors { private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl int getId(JoinBlockPredecessor jbp) { - exists(ControlFlowTree::Range t | ControlFlowTree::idOf(t, result) | + exists(ControlFlowTree::Range_ t | ControlFlowTree::idOf(t, result) | t = jbp.getFirstNode().getElement() or t = jbp.(EntryBasicBlock).getCallable() diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowElement.qll b/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowElement.qll index dcae798813a..7601b83f6b8 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowElement.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowElement.qll @@ -2,6 +2,7 @@ import csharp private import semmle.code.csharp.ExprOrStmtParent +private import semmle.code.csharp.commons.Compilation private import ControlFlow private import ControlFlow::BasicBlocks private import SuccessorTypes @@ -22,7 +23,12 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element { Callable getEnclosingCallable() { none() } /** Gets the assembly that this element was compiled into. */ - Assembly getAssembly() { result = this.getEnclosingCallable().getDeclaringType().getALocation() } + Assembly getAssembly() { + exists(Compilation c | + c.getAFileCompiled() = this.getFile() and + result = c.getOutputAssembly() + ) + } /** * Gets a control flow node for this element. That is, a node in the diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll b/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll index bdcfa6ac4b7..5a402717401 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll @@ -36,7 +36,7 @@ class Guard extends Expr { * Holds if basic block `bb` is guarded by this expression having value `v`. */ predicate controlsBasicBlock(BasicBlock bb, AbstractValue v) { - Internal::guardControls(this, _, bb, v) + Internal::guardControls(this, bb, v) } /** @@ -50,6 +50,12 @@ class Guard extends Expr { polarity = v.getValue() ) } + + /** + * Gets a valid value for this guard. For example, if this guard is a test, then + * it can have Boolean values `true` and `false`. + */ + AbstractValue getAValue() { isGuard(this, result) } } /** An abstract value. */ @@ -967,13 +973,6 @@ module Internal { e = any(BinaryArithmeticOperation bao | result = bao.getAnOperand()) } - /** Same as `this.getAChildExpr*()`, but avoids `fastTC`. */ - private Expr getAChildExprStar(Guard g) { - result = g - or - result = getAChildExprStar(g).getAChildExpr() - } - private Expr stripConditionalExpr(Expr e) { e = any(ConditionalExpr ce | @@ -1009,8 +1008,11 @@ module Internal { /** Holds if pre-basic-block `bb` only is reached when guard `g` has abstract value `v`. */ predicate preControls(Guard g, PreBasicBlocks::PreBasicBlock bb, AbstractValue v) { - exists(AbstractValue v0, Guard g0 | preControlsDirect(g0, bb, v0) | - preImpliesSteps(g0, v0, g, v) + preControlsDirect(g, bb, v) + or + exists(AbstractValue v0, Guard g0 | + preControls(g0, bb, v0) and + preImpliesStep(g0, v0, g, v) ) } @@ -1038,6 +1040,23 @@ module Internal { } } + private predicate canReturnBool(Callable c, Expr ret) { + canReturn(c, ret) and + c.getReturnType() instanceof BoolType + } + + private predicate boolReturnImplies(Expr ret, BooleanValue retVal, Guard g, AbstractValue v) { + canReturnBool(_, ret) and + isGuard(ret, retVal) and + g = ret and + v = retVal + or + exists(Guard g0, AbstractValue v0 | + boolReturnImplies(ret, retVal, g0, v0) and + preImpliesStep(g0, v0, g, v) + ) + } + /** * Holds if `ret` is an expression returned by the callable to which parameter * `p` belongs, and `ret` having Boolean value `retVal` allows the conclusion @@ -1046,14 +1065,14 @@ module Internal { private predicate validReturnInCustomNullCheck( Expr ret, Parameter p, BooleanValue retVal, boolean isNull ) { - exists(Callable c | canReturn(c, ret) | - p.getCallable() = c and - c.getReturnType() instanceof BoolType + exists(Callable c | + canReturnBool(c, ret) and + p.getCallable() = c ) and exists(PreSsaImplicitParameterDefinition def | p = def.getParameter() | def.nullGuardedReturn(ret, isNull) or - exists(NullValue nv | preImpliesSteps(ret, retVal, def.getARead(), nv) | + exists(NullValue nv | boolReturnImplies(ret, retVal, def.getARead(), nv) | if nv.isNull() then isNull = true else isNull = false ) ) @@ -1447,8 +1466,10 @@ module Internal { PreSsa::Definition def, AssignableRead read ) { read = def.getAFirstRead() and - not exists(AssignableRead other | PreSsa::adjacentReadPairSameVar(other, read) | - other != read + ( + not PreSsa::adjacentReadPairSameVar(_, read) + or + read = unique(AssignableRead read0 | PreSsa::adjacentReadPairSameVar(read0, read)) ) } @@ -1632,10 +1653,14 @@ module Internal { AssignableRead read1, AssignableRead read2 ) { PreSsa::adjacentReadPairSameVar(read1, read2) and - not exists(AssignableRead other | - PreSsa::adjacentReadPairSameVar(other, read2) and - other != read1 and - other != read2 + ( + read1 = read2 and + read1 = unique(AssignableRead other | PreSsa::adjacentReadPairSameVar(other, read2)) + or + read1 = + unique(AssignableRead other | + PreSsa::adjacentReadPairSameVar(other, read2) and other != read2 + ) ) } @@ -1691,6 +1716,79 @@ module Internal { import PreCFG + private predicate interestingDescendantCandidate(Expr e) { + guardControls(e, _, _) + or + e instanceof AccessOrCallExpr + } + + /** + * An (interesting) descendant of a guard that controls some basic block. + * + * This class exists purely for performance reasons: It allows us to big-step + * through the child hierarchy in `guardControlsSub()` instead of using + * `getAChildExpr()`. + */ + private class ControlGuardDescendant extends Expr { + ControlGuardDescendant() { + guardControls(this, _, _) + or + any(ControlGuardDescendant other).interestingDescendant(this) + } + + private predicate descendant(Expr e) { + e = this.getAChildExpr() + or + exists(Expr mid | + descendant(mid) and + not interestingDescendantCandidate(mid) and + e = mid.getAChildExpr() + ) + } + + /** Holds if `e` is an interesting descendant of this descendant. */ + predicate interestingDescendant(Expr e) { + descendant(e) and + interestingDescendantCandidate(e) + } + } + + /** + * Holds if `g` controls basic block `bb`, and `sub` is some (interesting) + * sub expression of `g`. + * + * Sub expressions inside nested logical operations that themselve control `bb` + * are not included, since these will be sub expressions of their immediately + * enclosing logical operation. (This restriction avoids a quadratic blow-up.) + * + * For example, in + * + * ```csharp + * if (a && (b && c)) + * BLOCK + * ``` + * + * `a` is included as a sub expression of `a && (b && c)` (which controls `BLOCK`), + * while `b` and `c` are only included as sub expressions of `b && c` (which also + * controls `BLOCK`). + */ + pragma[nomagic] + private predicate guardControlsSub(Guard g, BasicBlock bb, ControlGuardDescendant sub) { + guardControls(g, bb, _) and + sub = g + or + exists(ControlGuardDescendant mid | + guardControlsSub(g, bb, mid) and + mid.interestingDescendant(sub) + | + not guardControls(sub, bb, _) + or + not mid instanceof UnaryLogicalOperation and + not mid instanceof BinaryLogicalOperation and + not mid instanceof BitwiseOperation + ) + } + /** * A helper class for calculating structurally equal access/call expressions. */ @@ -1710,13 +1808,13 @@ module Internal { /** * Holds if access/call expression `e` (targeting declaration `target`) - * is a sub expression of a condition that controls whether basic block + * is a sub expression of a guard that controls whether basic block * `bb` is reached. */ pragma[noinline] private predicate candidateAux(AccessOrCallExpr e, Declaration target, BasicBlock bb) { target = e.getTarget() and - exists(Guard g | e = getAChildExprStar(g) | guardControls(g, _, bb, _)) + guardControlsSub(_, bb, e) } } @@ -1727,49 +1825,67 @@ module Internal { /** * Holds if basic block `bb` only is reached when guard `g` has abstract value `v`. - * - * `cb` records all of the possible condition blocks for `g` that a path from the - * callable entry point to `bb` may go through. */ cached - predicate guardControls(Guard g, ConditionBlock cb, BasicBlock bb, AbstractValue v) { + predicate guardControls(Guard g, BasicBlock bb, AbstractValue v) { + exists(ControlFlowElement cfe, ConditionalSuccessor cs | + v.branch(cfe, cs, g) and cfe.controlsBlock(bb, cs, _) + ) + or exists(AbstractValue v0, Guard g0 | - impliesSteps(g0, v0, g, v) and - exists(ControlFlowElement cfe, ConditionalSuccessor cs | - v0.branch(cfe, cs, g0) and cfe.controlsBlock(bb, cs, cb) - ) + guardControls(g0, bb, v0) and + impliesStep(g0, v0, g, v) ) } - pragma[noinline] + pragma[nomagic] + private predicate guardControlsSubSame(Guard g, BasicBlock bb, ControlGuardDescendant sub) { + guardControlsSub(g, bb, sub) and + any(ConditionOnExprComparisonConfig c).same(sub, _) + } + + pragma[nomagic] private predicate nodeIsGuardedBySameSubExpr0( - ControlFlow::Node guardedCfn, AccessOrCallExpr guarded, Guard g, ConditionBlock cb, + ControlFlow::Node guardedCfn, BasicBlock guardedBB, AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v ) { Stages::GuardsStage::forceCachingInSameStage() and guardedCfn = guarded.getAControlFlowNode() and - guardControls(g, cb, guardedCfn.getBasicBlock(), v) and - exists(ConditionOnExprComparisonConfig c | c.same(sub, guarded)) + guardedBB = guardedCfn.getBasicBlock() and + guardControls(g, guardedBB, v) and + guardControlsSubSame(g, guardedBB, sub) and + any(ConditionOnExprComparisonConfig c).same(sub, guarded) } - pragma[noinline] + pragma[nomagic] private predicate nodeIsGuardedBySameSubExpr( - ControlFlow::Node guardedCfn, AccessOrCallExpr guarded, Guard g, ConditionBlock cb, + ControlFlow::Node guardedCfn, BasicBlock guardedBB, AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v ) { - nodeIsGuardedBySameSubExpr0(guardedCfn, guarded, g, cb, sub, v) and - sub = getAChildExprStar(g) + nodeIsGuardedBySameSubExpr0(guardedCfn, guardedBB, guarded, g, sub, v) and + guardControlsSub(g, guardedBB, sub) } - pragma[noinline] + pragma[nomagic] + private predicate nodeIsGuardedBySameSubExprSsaDef0( + ControlFlow::Node cfn, BasicBlock guardedBB, AccessOrCallExpr guarded, Guard g, + ControlFlow::Node subCfn, BasicBlock subCfnBB, AccessOrCallExpr sub, AbstractValue v, + Ssa::Definition def + ) { + nodeIsGuardedBySameSubExpr(cfn, guardedBB, guarded, g, sub, v) and + def = sub.getAnSsaQualifier(subCfn) and + subCfnBB = subCfn.getBasicBlock() + } + + pragma[nomagic] private predicate nodeIsGuardedBySameSubExprSsaDef( - ControlFlow::Node cfn, AccessOrCallExpr guarded, Guard g, ControlFlow::Node subCfn, + ControlFlow::Node guardedCfn, AccessOrCallExpr guarded, Guard g, ControlFlow::Node subCfn, AccessOrCallExpr sub, AbstractValue v, Ssa::Definition def ) { - exists(ConditionBlock cb | - nodeIsGuardedBySameSubExpr(cfn, guarded, g, cb, sub, v) and - subCfn.getBasicBlock().dominates(cb) and - def = sub.getAnSsaQualifier(subCfn) + exists(BasicBlock guardedBB, BasicBlock subCfnBB | + nodeIsGuardedBySameSubExprSsaDef0(guardedCfn, guardedBB, guarded, g, subCfn, subCfnBB, sub, + v, def) and + subCfnBB.getASuccessor*() = guardedBB ) } @@ -1777,10 +1893,14 @@ module Internal { Ssa::Definition def, ControlFlow::Node cfn1, ControlFlow::Node cfn2 ) { SsaImpl::adjacentReadPairSameVar(def, cfn1, cfn2) and - not exists(ControlFlow::Node other | - SsaImpl::adjacentReadPairSameVar(def, other, cfn2) and - other != cfn1 and - other != cfn2 + ( + cfn1 = cfn2 and + cfn1 = unique(ControlFlow::Node other | SsaImpl::adjacentReadPairSameVar(def, other, cfn2)) + or + cfn1 = + unique(ControlFlow::Node other | + SsaImpl::adjacentReadPairSameVar(def, other, cfn2) and other != cfn2 + ) ) } @@ -1789,7 +1909,7 @@ module Internal { AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v ) { forex(ControlFlow::Node cfn | cfn = guarded.getAControlFlowNode() | - nodeIsGuardedBySameSubExpr(cfn, guarded, g, _, sub, v) + nodeIsGuardedBySameSubExpr(cfn, _, guarded, g, sub, v) ) } @@ -1814,7 +1934,7 @@ module Internal { predicate isGuardedByNode( ControlFlow::Nodes::ElementNode guarded, Guard g, AccessOrCallExpr sub, AbstractValue v ) { - nodeIsGuardedBySameSubExpr(guarded, _, g, _, sub, v) and + nodeIsGuardedBySameSubExpr(guarded, _, _, g, sub, v) and forall(ControlFlow::Node subCfn, Ssa::Definition def | nodeIsGuardedBySameSubExprSsaDef(guarded, _, g, subCfn, sub, v, def) | @@ -1860,40 +1980,6 @@ module Internal { } import Cached - - /** - * Holds if the assumption that `g1` has abstract value `v1` implies that - * `g2` has abstract value `v2`, using zero or more steps of reasoning. That is, - * the evaluation of `g2` to `v2` dominates the evaluation of `g1` to `v1`. - * - * This predicate does not rely on the control flow graph. - */ - predicate preImpliesSteps(Guard g1, AbstractValue v1, Guard g2, AbstractValue v2) { - g1 = g2 and - v1 = v2 and - isGuard(g1, v1) - or - exists(Expr mid, AbstractValue vMid | preImpliesSteps(g1, v1, mid, vMid) | - preImpliesStep(mid, vMid, g2, v2) - ) - } - - /** - * Holds if the assumption that `g1` has abstract value `v1` implies that - * `g2` has abstract value `v2`, using zero or more steps of reasoning. That is, - * the evaluation of `g2` to `v2` dominates the evaluation of `g1` to `v1`. - * - * This predicate relies on the control flow graph. - */ - predicate impliesSteps(Guard g1, AbstractValue v1, Guard g2, AbstractValue v2) { - g1 = g2 and - v1 = v2 and - isGuard(g1, v1) - or - exists(Expr mid, AbstractValue vMid | impliesSteps(g1, v1, mid, vMid) | - impliesStep(mid, vMid, g2, v2) - ) - } } private import Internal diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/internal/Completion.qll b/csharp/ql/src/semmle/code/csharp/controlflow/internal/Completion.qll index 089339ba1b8..bda14e0b4ae 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/internal/Completion.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/internal/Completion.qll @@ -293,18 +293,6 @@ private class Overflowable extends UnaryOperation { } } -private class CoreLib extends Assembly { - CoreLib() { this = any(SystemExceptionClass c).getALocation() } -} - -/** - * Holds if assembly `a` was definitely compiled with core library `core`. - */ -pragma[noinline] -private predicate assemblyCompiledWithCoreLib(Assembly a, CoreLib core) { - a.getAnAttribute().getType().getBaseClass*().(SystemAttributeClass).getALocation() = core -} - /** A control flow element that is inside a `try` block. */ private class TriedControlFlowElement extends ControlFlowElement { TryStmt try; @@ -317,7 +305,7 @@ private class TriedControlFlowElement extends ControlFlowElement { /** * Gets an exception class that is potentially thrown by this element, if any. */ - private Class getAThrownException0() { + Class getAThrownException() { this instanceof Overflowable and result instanceof SystemOverflowExceptionClass or @@ -376,41 +364,6 @@ private class TriedControlFlowElement extends ControlFlowElement { this instanceof DynamicExpr and result instanceof SystemExceptionClass } - - private CoreLib getCoreLibFromACatchClause() { - exists(SpecificCatchClause scc | scc = try.getACatchClause() | - result = scc.getCaughtExceptionType().getBaseClass*().(SystemExceptionClass).getALocation() - ) - } - - private CoreLib getCoreLib() { - result = this.getCoreLibFromACatchClause() - or - not exists(this.getCoreLibFromACatchClause()) and - assemblyCompiledWithCoreLib(this.getAssembly(), result) - } - - pragma[noinline] - private Class getAThrownExceptionFromPlausibleCoreLib(string name) { - result = this.getAThrownException0() and - name = result.getQualifiedName() and - ( - not exists(this.getCoreLib()) - or - this.getCoreLib() = result.getALocation() - ) - } - - Class getAThrownException() { - exists(string name | result = this.getAThrownExceptionFromPlausibleCoreLib(name) | - result = - min(Class c | - c = this.getAThrownExceptionFromPlausibleCoreLib(name) - | - c order by c.getLocation().(Assembly).getFullName() - ) - ) - } } pragma[nomagic] diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll b/csharp/ql/src/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll index a0c0784c010..63f6943a29c 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll @@ -49,6 +49,27 @@ private import SuccessorType private import SuccessorTypes private import Splitting private import semmle.code.csharp.ExprOrStmtParent +private import semmle.code.csharp.commons.Compilation + +/** + * A compilation. + * + * Unlike the standard `Compilation` class, this class also supports buildless + * extraction. + */ +newtype CompilationExt = + TCompilation(Compilation c) { not extractionIsStandalone() } or + TBuildless() { extractionIsStandalone() } + +/** Gets the compilation that source file `f` belongs to. */ +CompilationExt getCompilation(SourceFile f) { + exists(Compilation c | + f = c.getAFileCompiled() and + result = TCompilation(c) + ) + or + result = TBuildless() +} /** An element that defines a new CFG scope. */ class CfgScope extends Element, @top_level_exprorstmt_parent { @@ -56,7 +77,7 @@ class CfgScope extends Element, @top_level_exprorstmt_parent { } module ControlFlowTree { - private class Range_ = @callable or @control_flow_element; + class Range_ = @callable or @control_flow_element; class Range extends Element, Range_ { Range() { this = getAChild*(any(CfgScope scope)) } @@ -67,9 +88,9 @@ module ControlFlowTree { result = p.(AssignOperation).getExpandedAssignment() } - private predicate id(Range x, Range y) { x = y } + private predicate id(Range_ x, Range_ y) { x = y } - predicate idOf(Range x, int y) = equivalenceRelation(id/2)(x, y) + predicate idOf(Range_ x, int y) = equivalenceRelation(id/2)(x, y) } abstract private class ControlFlowTree extends ControlFlowTree::Range { @@ -135,10 +156,7 @@ predicate scopeFirst(CfgScope scope, ControlFlowElement first) { then first(c.(Constructor).getInitializer(), first) else if InitializerSplitting::constructorInitializes(c, _) - then - first(any(InitializerSplitting::InitializedInstanceMember m | - InitializerSplitting::constructorInitializeOrder(c, m, 0) - ).getInitializer(), first) + then first(InitializerSplitting::constructorInitializeOrder(c, _, 0), first) else first(c.getBody(), first) ) or @@ -152,36 +170,36 @@ predicate scopeLast(Callable scope, ControlFlowElement last, Completion c) { last(scope.getBody(), last, c) and not c instanceof GotoCompletion or - exists(InitializerSplitting::InitializedInstanceMember m | - m = InitializerSplitting::lastConstructorInitializer(scope) and - last(m.getInitializer(), last, c) and - not scope.hasBody() - ) + last(InitializerSplitting::lastConstructorInitializer(scope, _), last, c) and + not scope.hasBody() } -private class CallableTree extends ControlFlowTree, Callable { +private class ConstructorTree extends ControlFlowTree, Constructor { final override predicate propagatesAbnormal(ControlFlowElement child) { none() } final override predicate first(ControlFlowElement first) { none() } final override predicate last(ControlFlowElement last, Completion c) { none() } + /** Gets the body of this constructor belonging to compilation `comp`. */ + pragma[noinline] + ControlFlowElement getBody(CompilationExt comp) { + result = this.getBody() and + comp = getCompilation(result.getFile()) + } + final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - exists(Constructor con, InitializerSplitting::InitializedInstanceMember m, int i | - this = con and - last(m.getInitializer(), pred, c) and - c instanceof NormalCompletion and - InitializerSplitting::constructorInitializeOrder(con, m, i) + exists(CompilationExt comp, int i, AssignExpr ae | + ae = InitializerSplitting::constructorInitializeOrder(this, comp, i) and + last(ae, pred, c) and + c instanceof NormalCompletion | // Flow from one member initializer to the next - exists(InitializerSplitting::InitializedInstanceMember next | - InitializerSplitting::constructorInitializeOrder(con, next, i + 1) and - first(next.getInitializer(), succ) - ) + first(InitializerSplitting::constructorInitializeOrder(this, comp, i + 1), succ) or // Flow from last member initializer to constructor body - m = InitializerSplitting::lastConstructorInitializer(con) and - first(con.getBody(), succ) + ae = InitializerSplitting::lastConstructorInitializer(this, comp) and + first(this.getBody(comp), succ) ) } } @@ -884,21 +902,18 @@ module Expressions { first(this.getChildElement(i + 1), succ) ) or - exists(Constructor con | + exists(ConstructorTree con, CompilationExt comp | last(this, pred, c) and con = this.getConstructor() and + comp = getCompilation(this.getFile()) and c instanceof NormalCompletion | // Flow from constructor initializer to first member initializer - exists(InitializerSplitting::InitializedInstanceMember m | - InitializerSplitting::constructorInitializeOrder(con, m, 0) - | - first(m.getInitializer(), succ) - ) + first(InitializerSplitting::constructorInitializeOrder(con, comp, 0), succ) or // Flow from constructor initializer to first element of constructor body - not InitializerSplitting::constructorInitializeOrder(con, _, _) and - first(con.getBody(), succ) + not exists(InitializerSplitting::constructorInitializeOrder(con, comp, _)) and + first(con.getBody(comp), succ) ) } } diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/internal/NonReturning.qll b/csharp/ql/src/semmle/code/csharp/controlflow/internal/NonReturning.qll index 6d075d031f5..fe9f1148a6d 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/internal/NonReturning.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/internal/NonReturning.qll @@ -39,8 +39,10 @@ private class ThrowingCall extends NonReturningCall { or this.(FailingAssertion).getAssertionFailure().isException(c.getExceptionClass()) or - exists(CIL::Method m, CIL::Type ex | - this.getTarget().matchesHandle(m) and + exists(Callable target, CIL::Method m, CIL::Type ex | + target = this.getTarget() and + not target.hasBody() and + target.matchesHandle(m) and alwaysThrowsException(m, ex) and c.getExceptionClass().matchesHandle(ex) and not m.isVirtual() diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/internal/Splitting.qll b/csharp/ql/src/semmle/code/csharp/controlflow/internal/Splitting.qll index 0947059d873..8ca77a4775e 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/internal/Splitting.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/internal/Splitting.qll @@ -209,6 +209,13 @@ abstract class SplitImpl extends Split { or exists(ControlFlowElement pred | this.appliesTo(pred) | this.hasSuccessor(pred, cfe, _)) } + + /** The `succ` relation restricted to predecessors `pred` that this split applies to. */ + pragma[noinline] + final predicate appliesSucc(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + this.appliesTo(pred) and + succ(pred, succ, c) + } } module InitializerSplitting { @@ -218,23 +225,23 @@ module InitializerSplitting { * A non-static member with an initializer, for example a field `int Field = 0`. */ class InitializedInstanceMember extends Member { - private AssignExpr ae; - InitializedInstanceMember() { - not this.isStatic() and - expr_parent_top_level_adjusted(ae, _, this) and - not ae = any(Callable c).getExpressionBody() + exists(AssignExpr ae | + not this.isStatic() and + expr_parent_top_level(ae, _, this) and + not ae = any(Callable c).getExpressionBody() + ) } /** Gets the initializer expression. */ - AssignExpr getInitializer() { result = ae } + AssignExpr getInitializer() { expr_parent_top_level(result, _, this) } /** * Gets a control flow element that is a syntactic descendant of the * initializer expression. */ ControlFlowElement getAnInitializerDescendant() { - result = ae + result = this.getInitializer() or result = this.getAnInitializerDescendant().getAChild() } @@ -242,43 +249,41 @@ module InitializerSplitting { /** * Holds if `c` is a non-static constructor that performs the initialization - * of member `m`. + * of a member via assignment `init`. */ - predicate constructorInitializes(Constructor c, InitializedInstanceMember m) { - c.isUnboundDeclaration() and - not c.isStatic() and - c.getDeclaringType().hasMember(m) and - ( - not c.hasInitializer() - or - // Members belonging to the base class are initialized via calls to the - // base constructor - c.getInitializer().isBase() and - m.getDeclaringType() = c.getDeclaringType() + predicate constructorInitializes(InstanceConstructor c, AssignExpr init) { + exists(InitializedInstanceMember m | + c.isUnboundDeclaration() and + c.getDeclaringType().getAMember() = m and + not c.getInitializer().isThis() and + init = m.getInitializer() ) } /** - * Holds if `m` is the `i`th member initialized by non-static constructor `c`. + * Gets the `i`th member initializer expression for non-static constructor `c` + * in compilation `comp`. */ - predicate constructorInitializeOrder(Constructor c, InitializedInstanceMember m, int i) { - constructorInitializes(c, m) and - m = - rank[i + 1](InitializedInstanceMember m0 | - constructorInitializes(c, m0) + AssignExpr constructorInitializeOrder(Constructor c, CompilationExt comp, int i) { + constructorInitializes(c, result) and + result = + rank[i + 1](AssignExpr ae0, Location l | + constructorInitializes(c, ae0) and + l = ae0.getLocation() and + getCompilation(l.getFile()) = comp | - m0 - order by - m0.getLocation().getStartLine(), m0.getLocation().getStartColumn(), - m0.getLocation().getFile().getAbsolutePath() + ae0 order by l.getStartLine(), l.getStartColumn(), l.getFile().getAbsolutePath() ) } - /** Gets the last member initialized by non-static constructor `c`. */ - InitializedInstanceMember lastConstructorInitializer(Constructor c) { + /** + * Gets the last member initializer expression for non-static constructor `c` + * in compilation `comp`. + */ + AssignExpr lastConstructorInitializer(Constructor c, CompilationExt comp) { exists(int i | - constructorInitializeOrder(c, result, i) and - not constructorInitializeOrder(c, _, i + 1) + result = constructorInitializeOrder(c, comp, i) and + not exists(constructorInitializeOrder(c, comp, i + 1)) ) } @@ -384,11 +389,11 @@ module InitializerSplitting { } override predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - this.appliesTo(pred) and - succ(pred, succ, c) and + this.appliesSucc(pred, succ, c) and succ = - any(InitializedInstanceMember m | constructorInitializes(this.getConstructor(), m)) - .getAnInitializerDescendant() + any(InitializedInstanceMember m | + constructorInitializes(this.getConstructor(), m.getInitializer()) + ).getAnInitializerDescendant() } } } @@ -621,8 +626,7 @@ module AssertionSplitting { } override predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - this.appliesTo(pred) and - succ(pred, succ, c) and + this.appliesSucc(pred, succ, c) and succ = getAnAssertionDescendant(a) } } @@ -859,8 +863,7 @@ module FinallySplitting { } override predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - this.appliesToPredecessor(pred) and - succ(pred, succ, c) and + this.appliesSucc(pred, succ, c) and succ = any(FinallyControlFlowElement fcfe | if fcfe.isEntryNode() @@ -1038,7 +1041,7 @@ module ExceptionHandlerSplitting { override predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c) { this.appliesToPredecessor(pred, c) and - succ(pred, succ, c) and + this.appliesSucc(pred, succ, c) and not first(any(SpecificCatchClause scc).getBlock(), succ) and not succ instanceof GeneralCatchClause and not exists(TryStmt ts, SpecificCatchClause scc, int last | @@ -1212,14 +1215,12 @@ module BooleanSplitting { exists(Callable c, int r | c = kind.getEnclosingCallable() | result = r + ExceptionHandlerSplitting::getNextListOrder() - 1 and kind = - rank[r](BooleanSplitSubKind kind0 | + rank[r](BooleanSplitSubKind kind0, Location l | kind0.getEnclosingCallable() = c and - kind0.startsSplit(_) + kind0.startsSplit(_) and + l = kind0.getLocation() | - kind0 - order by - kind0.getLocation().getStartLine(), kind0.getLocation().getStartColumn(), - kind0.toString() + kind0 order by l.getStartLine(), l.getStartColumn(), kind0.toString() ) ) } @@ -1301,7 +1302,7 @@ module BooleanSplitting { override predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c) { exists(PreBasicBlock bb, Completion c0 | this.appliesToBlock(bb, c0) | pred = bb.getAnElement() and - succ(pred, succ, c) and + this.appliesSucc(pred, succ, c) and ( pred = bb.getLastElement() implies @@ -1438,10 +1439,11 @@ module LoopSplitting { exists(Callable c, int r | c = enclosingCallable(loop) | result = r + BooleanSplitting::getNextListOrder() - 1 and loop = - rank[r](AnalyzableLoopStmt loop0 | - enclosingCallable(loop0) = c + rank[r](AnalyzableLoopStmt loop0, Location l | + enclosingCallable(loop0) = c and + l = loop0.getLocation() | - loop0 order by loop0.getLocation().getStartLine(), loop0.getLocation().getStartColumn() + loop0 order by l.getStartLine(), l.getStartColumn() ) ) } @@ -1491,7 +1493,7 @@ module LoopSplitting { override predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c) { this.appliesToPredecessor(pred, c) and - succ(pred, succ, c) and + this.appliesSucc(pred, succ, c) and not loop.stop(pred, succ, c) } } diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll b/csharp/ql/src/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll index f828419a440..f37a4f2d074 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll @@ -316,6 +316,15 @@ private module SsaDefReaches { ) } + /** + * Holds if the reference to `def` at index `i` in basic block `bb` is the + * last reference to `v` inside `bb`. + */ + pragma[noinline] + predicate lastSsaRef(Definition def, SourceVariable v, BasicBlock bb, int i) { + ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) + } + predicate defOccursInBlock(Definition def, BasicBlock bb, SourceVariable v) { exists(ssaDefRank(def, v, bb, _, _)) } @@ -351,8 +360,7 @@ private module SsaDefReaches { */ predicate defAdjacentRead(Definition def, BasicBlock bb1, BasicBlock bb2, int i2) { varBlockReaches(def, bb1, bb2) and - ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 and - variableRead(bb2, i2, _, _) + ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 } } @@ -434,15 +442,22 @@ predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2 bb2 = bb1 ) or - exists(SourceVariable v | ssaDefRank(def, v, bb1, i1, _) = maxSsaRefRank(bb1, v)) and + lastSsaRef(def, _, bb1, i1) and defAdjacentRead(def, bb1, bb2, i2) } +pragma[noinline] +private predicate adjacentDefRead( + Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v +) { + adjacentDefRead(def, bb1, i1, bb2, i2) and + v = def.getSourceVariable() +} + private predicate adjacentDefReachesRead( Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2 ) { - adjacentDefRead(def, bb1, i1, bb2, i2) and - exists(SourceVariable v | v = def.getSourceVariable() | + exists(SourceVariable v | adjacentDefRead(def, bb1, i1, bb2, i2, v) | ssaRef(bb1, i1, v, SsaDef()) or variableRead(bb1, i1, v, true) @@ -475,17 +490,19 @@ predicate adjacentDefNoUncertainReads(Definition def, BasicBlock bb1, int i1, Ba */ pragma[nomagic] predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) { - exists(int rnk, SourceVariable v, int j | rnk = ssaDefRank(def, v, bb, i, _) | + exists(SourceVariable v | // Next reference to `v` inside `bb` is a write - next.definesAt(v, bb, j) and - rnk + 1 = ssaRefRank(bb, j, v, SsaDef()) + exists(int rnk, int j | + rnk = ssaDefRank(def, v, bb, i, _) and + next.definesAt(v, bb, j) and + rnk + 1 = ssaRefRank(bb, j, v, SsaDef()) + ) or // Can reach a write using one or more steps - rnk = maxSsaRefRank(bb, v) and + lastSsaRef(def, v, bb, i) and exists(BasicBlock bb2 | varBlockReaches(def, bb, bb2) and - next.definesAt(v, bb2, j) and - 1 = ssaRefRank(bb2, j, v, SsaDef()) + 1 = ssaDefRank(next, v, bb2, _, SsaDef()) ) ) } @@ -539,7 +556,8 @@ pragma[nomagic] predicate lastRef(Definition def, BasicBlock bb, int i) { lastRefRedef(def, bb, i, _) or - exists(SourceVariable v | ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) | + lastSsaRef(def, _, bb, i) and + ( // Can reach exit directly bb instanceof ExitBasicBlock or diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/internal/pressa/SsaImplSpecific.qll b/csharp/ql/src/semmle/code/csharp/controlflow/internal/pressa/SsaImplSpecific.qll index 610e808a640..ad64c38973a 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/internal/pressa/SsaImplSpecific.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/internal/pressa/SsaImplSpecific.qll @@ -15,13 +15,37 @@ class ExitBasicBlock extends BasicBlock { ExitBasicBlock() { scopeLast(_, this.getLastElement(), _) } } -pragma[noinline] +/** Holds if `a` is assigned in non-constructor callable `c`. */ +pragma[nomagic] +private predicate assignableDefinition(Assignable a, Callable c) { + exists(AssignableDefinition def | def.getTarget() = a | + c = def.getEnclosingCallable() and + not c instanceof Constructor + ) +} + +/** Holds if `a` is accessed in callable `c`. */ +pragma[nomagic] +private predicate assignableAccess(Assignable a, Callable c) { + exists(AssignableAccess aa | aa.getTarget() = a | c = aa.getEnclosingCallable()) +} + +pragma[nomagic] private predicate assignableNoCapturing(Assignable a, Callable c) { - exists(AssignableAccess aa | aa.getTarget() = a | c = aa.getEnclosingCallable()) and - forall(AssignableDefinition def | def.getTarget() = a | - c = def.getEnclosingCallable() + assignableAccess(a, c) and + /* + * The code below is equivalent to + * ```ql + * not exists(Callable other | assignableDefinition(a, other) | other != c) + * ``` + * but it avoids a Cartesian product in the compiler generated antijoin + * predicate. + */ + + ( + not assignableDefinition(a, _) or - def.getEnclosingCallable() instanceof Constructor + c = unique(Callable c0 | assignableDefinition(a, c0) | c0) ) } @@ -41,7 +65,7 @@ class SourceVariable extends Assignable { ( this instanceof LocalScopeVariable or - this instanceof Field + this = any(Field f | not f.isVolatile()) or this = any(TrivialProperty tp | not tp.isOverridableOrImplementable()) ) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll b/csharp/ql/src/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll index 5798080a98a..a7c6869a4cc 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll @@ -807,17 +807,29 @@ class SystemTextStringBuilderFlow extends LibraryTypeDataFlow, SystemTextStringB sinkAp = AccessPath::empty() and preservesValue = false or - exists(int i, Type t | - name.regexpMatch("Append(Format|Line)?") and - t = m.getParameter(i).getType() and - source = TCallableFlowSourceArg(i) and + name.regexpMatch("Append(Format|Line|Join)?") and + preservesValue = true and + ( + exists(int i, Type t | + t = m.getParameter(i).getType() and + source = TCallableFlowSourceArg(i) and + sink = TCallableFlowSinkQualifier() and + sinkAp = AccessPath::element() + | + ( + t instanceof StringType or + t instanceof ObjectType + ) and + sourceAp = AccessPath::empty() + or + isCollectionType(t) and + sourceAp = AccessPath::element() + ) + or + source = TCallableFlowSourceQualifier() and sourceAp = AccessPath::empty() and - sink = [TCallableFlowSinkQualifier().(TCallableFlowSink), TCallableFlowSinkReturn()] and - sinkAp = AccessPath::element() and - preservesValue = true - | - t instanceof StringType or - t instanceof ObjectType + sink = TCallableFlowSinkReturn() and + sinkAp = AccessPath::empty() ) ) } @@ -1836,8 +1848,15 @@ class SystemTupleFlow extends LibraryTypeDataFlow, ValueOrRefType { c.(Constructor).getDeclaringType() = this and t = this or - c = this.getAMethod(any(string name | name.regexpMatch("Create(<,*>)?"))) and - t = c.getReturnType().getUnboundDeclaration() + exists(ValueOrRefType namedType | + namedType = this or namedType = this.(TupleType).getUnderlyingType() + | + c = namedType.getAMethod(any(string name | name.regexpMatch("Create(<,*>)?"))) and + ( + t = c.getReturnType().getUnboundDeclaration() or + t = c.getReturnType().(TupleType).getUnderlyingType().getUnboundDeclaration() + ) + ) ) or c = diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll b/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll index 15133668d00..131480a8b59 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll @@ -128,12 +128,19 @@ private predicate dereferenceAt(BasicBlock bb, int i, Ssa::Definition def, Deref * has abstract value `vDef`. */ private predicate exprImpliesSsaDef( - Expr e, G::AbstractValue vExpr, Ssa::Definition def, G::AbstractValue vDef + G::Guard e, G::AbstractValue vExpr, Ssa::Definition def, G::AbstractValue vDef ) { - exists(G::Guard g | G::Internal::impliesSteps(e, vExpr, g, vDef) | - g = def.getARead() + vExpr = e.getAValue() and + vExpr = vDef and + ( + e = def.getARead() or - g = def.(Ssa::ExplicitDefinition).getADefinition().getTargetAccess() + e = def.(Ssa::ExplicitDefinition).getADefinition().getTargetAccess() + ) + or + exists(Expr e0, G::AbstractValue vExpr0 | + exprImpliesSsaDef(e0, vExpr0, def, vDef) and + G::Internal::impliesStep(e, vExpr, e0, vExpr0) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll index 4eac274b04f..04465f5ae9e 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll @@ -1,6 +1,7 @@ private import csharp private import cil private import dotnet +private import DataFlowImplCommon as DataFlowImplCommon private import DataFlowPublic private import DataFlowPrivate private import FlowSummaryImpl as FlowSummaryImpl @@ -67,33 +68,30 @@ private predicate transitiveCapturedCallTarget(ControlFlow::Nodes::ElementNode c ) } -cached +newtype TReturnKind = + TNormalReturnKind() or + TOutReturnKind(int i) { i = any(Parameter p | p.isOut()).getPosition() } or + TRefReturnKind(int i) { i = any(Parameter p | p.isRef()).getPosition() } or + TImplicitCapturedReturnKind(LocalScopeVariable v) { + exists(Ssa::ExplicitDefinition def | def.isCapturedVariableDefinitionFlowOut(_, _) | + v = def.getSourceVariable().getAssignable() + ) + } or + TJumpReturnKind(DataFlowCallable target, ReturnKind rk) { + rk instanceof NormalReturnKind and + ( + target instanceof Constructor or + not target.getReturnType() instanceof VoidType + ) + or + exists(target.getParameter(rk.(OutRefReturnKind).getPosition())) + } + private module Cached { - private import semmle.code.csharp.Caching - - cached - newtype TReturnKind = - TNormalReturnKind() { Stages::DataFlowStage::forceCachingInSameStage() } or - TOutReturnKind(int i) { i = any(Parameter p | p.isOut()).getPosition() } or - TRefReturnKind(int i) { i = any(Parameter p | p.isRef()).getPosition() } or - TImplicitCapturedReturnKind(LocalScopeVariable v) { - exists(Ssa::ExplicitDefinition def | def.isCapturedVariableDefinitionFlowOut(_, _) | - v = def.getSourceVariable().getAssignable() - ) - } or - TJumpReturnKind(DataFlowCallable target, ReturnKind rk) { - rk instanceof NormalReturnKind and - ( - target instanceof Constructor or - not target.getReturnType() instanceof VoidType - ) - or - exists(target.getParameter(rk.(OutRefReturnKind).getPosition())) - } - cached newtype TDataFlowCall = TNonDelegateCall(ControlFlow::Nodes::ElementNode cfn, DispatchCall dc) { + DataFlowImplCommon::forceCachingInSameStage() and cfn.getElement() = dc.getCall() } or TExplicitDelegateLikeCall(ControlFlow::Nodes::ElementNode cfn, DelegateLikeCall dc) { diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 8b446d28b86..9b14db7ef88 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,29 +512,29 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } /** - * Holds if an output from `call` is reached in the flow covered by `revFlow`. + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. */ pragma[nomagic] private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { @@ -597,7 +594,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -610,6 +607,15 @@ private module Stage1 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(ArgNode arg, boolean toReturn | + revFlow(arg, toReturn, config) and + revFlowInToReturn(call, arg, config) and + revFlowIsReturned(call, toReturn, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, config)) and @@ -663,7 +669,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +681,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +741,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -833,6 +838,16 @@ private module Stage2 { PrevStage::revFlow(node, _, _, apa, config) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -899,13 +914,11 @@ private module Stage2 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -944,10 +957,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -956,17 +969,14 @@ private module Stage2 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -981,7 +991,13 @@ private module Stage2 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -992,7 +1008,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1012,6 +1028,27 @@ private module Stage2 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1077,14 +1114,12 @@ private module Stage2 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1132,13 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1146,9 +1178,14 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1199,13 +1236,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1218,6 +1255,15 @@ private module Stage2 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -1245,8 +1291,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1260,8 +1305,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1320,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1366,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1395,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1429,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1488,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1465,6 +1512,16 @@ private module Stage3 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -1538,13 +1595,11 @@ private module Stage3 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -1583,10 +1638,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1595,17 +1650,14 @@ private module Stage3 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -1620,7 +1672,13 @@ private module Stage3 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1631,7 +1689,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1651,6 +1709,27 @@ private module Stage3 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1716,14 +1795,12 @@ private module Stage3 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1771,13 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1785,9 +1859,14 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1838,13 +1917,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1857,6 +1936,15 @@ private module Stage3 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2088,7 +2176,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2127,17 +2215,12 @@ private module Stage4 { bindingset[innercc, inner, call] private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) { resolveReturn(innercc, inner, call) - or - innercc.(CallContextCall).matchesCall(call) } bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -2158,8 +2241,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2185,6 +2267,16 @@ private module Stage4 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -2258,13 +2350,11 @@ private module Stage4 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -2303,10 +2393,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2315,17 +2405,14 @@ private module Stage4 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -2340,7 +2427,13 @@ private module Stage4 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2351,7 +2444,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2371,6 +2464,27 @@ private module Stage4 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -2436,14 +2550,12 @@ private module Stage4 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -2491,13 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2505,9 +2614,14 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2558,13 +2672,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2577,6 +2691,15 @@ private module Stage4 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2609,7 +2732,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2630,7 +2753,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2761,7 +2884,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2982,7 +3105,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3132,7 +3255,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, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and @@ -3151,7 +3274,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3238,7 +3361,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3251,7 +3374,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3275,7 +3398,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3571,7 +3694,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3594,7 +3717,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3614,7 +3737,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3628,9 +3751,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3786,7 +3909,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3800,7 +3923,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3816,7 +3939,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3830,7 +3953,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3927,7 +4050,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3946,7 +4069,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3983,7 +4106,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4040,7 +4163,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4118,7 +4241,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4141,7 +4264,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index 8b446d28b86..9b14db7ef88 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,29 +512,29 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } /** - * Holds if an output from `call` is reached in the flow covered by `revFlow`. + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. */ pragma[nomagic] private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { @@ -597,7 +594,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -610,6 +607,15 @@ private module Stage1 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(ArgNode arg, boolean toReturn | + revFlow(arg, toReturn, config) and + revFlowInToReturn(call, arg, config) and + revFlowIsReturned(call, toReturn, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, config)) and @@ -663,7 +669,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +681,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +741,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -833,6 +838,16 @@ private module Stage2 { PrevStage::revFlow(node, _, _, apa, config) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -899,13 +914,11 @@ private module Stage2 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -944,10 +957,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -956,17 +969,14 @@ private module Stage2 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -981,7 +991,13 @@ private module Stage2 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -992,7 +1008,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1012,6 +1028,27 @@ private module Stage2 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1077,14 +1114,12 @@ private module Stage2 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1132,13 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1146,9 +1178,14 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1199,13 +1236,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1218,6 +1255,15 @@ private module Stage2 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -1245,8 +1291,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1260,8 +1305,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1320,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1366,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1395,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1429,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1488,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1465,6 +1512,16 @@ private module Stage3 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -1538,13 +1595,11 @@ private module Stage3 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -1583,10 +1638,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1595,17 +1650,14 @@ private module Stage3 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -1620,7 +1672,13 @@ private module Stage3 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1631,7 +1689,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1651,6 +1709,27 @@ private module Stage3 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1716,14 +1795,12 @@ private module Stage3 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1771,13 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1785,9 +1859,14 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1838,13 +1917,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1857,6 +1936,15 @@ private module Stage3 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2088,7 +2176,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2127,17 +2215,12 @@ private module Stage4 { bindingset[innercc, inner, call] private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) { resolveReturn(innercc, inner, call) - or - innercc.(CallContextCall).matchesCall(call) } bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -2158,8 +2241,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2185,6 +2267,16 @@ private module Stage4 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -2258,13 +2350,11 @@ private module Stage4 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -2303,10 +2393,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2315,17 +2405,14 @@ private module Stage4 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -2340,7 +2427,13 @@ private module Stage4 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2351,7 +2444,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2371,6 +2464,27 @@ private module Stage4 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -2436,14 +2550,12 @@ private module Stage4 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -2491,13 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2505,9 +2614,14 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2558,13 +2672,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2577,6 +2691,15 @@ private module Stage4 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2609,7 +2732,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2630,7 +2753,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2761,7 +2884,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2982,7 +3105,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3132,7 +3255,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, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and @@ -3151,7 +3274,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3238,7 +3361,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3251,7 +3374,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3275,7 +3398,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3571,7 +3694,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3594,7 +3717,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3614,7 +3737,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3628,9 +3751,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3786,7 +3909,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3800,7 +3923,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3816,7 +3939,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3830,7 +3953,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3927,7 +4050,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3946,7 +4069,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3983,7 +4106,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4040,7 +4163,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4118,7 +4241,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4141,7 +4264,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index 8b446d28b86..9b14db7ef88 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,29 +512,29 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } /** - * Holds if an output from `call` is reached in the flow covered by `revFlow`. + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. */ pragma[nomagic] private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { @@ -597,7 +594,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -610,6 +607,15 @@ private module Stage1 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(ArgNode arg, boolean toReturn | + revFlow(arg, toReturn, config) and + revFlowInToReturn(call, arg, config) and + revFlowIsReturned(call, toReturn, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, config)) and @@ -663,7 +669,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +681,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +741,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -833,6 +838,16 @@ private module Stage2 { PrevStage::revFlow(node, _, _, apa, config) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -899,13 +914,11 @@ private module Stage2 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -944,10 +957,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -956,17 +969,14 @@ private module Stage2 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -981,7 +991,13 @@ private module Stage2 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -992,7 +1008,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1012,6 +1028,27 @@ private module Stage2 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1077,14 +1114,12 @@ private module Stage2 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1132,13 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1146,9 +1178,14 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1199,13 +1236,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1218,6 +1255,15 @@ private module Stage2 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -1245,8 +1291,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1260,8 +1305,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1320,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1366,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1395,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1429,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1488,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1465,6 +1512,16 @@ private module Stage3 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -1538,13 +1595,11 @@ private module Stage3 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -1583,10 +1638,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1595,17 +1650,14 @@ private module Stage3 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -1620,7 +1672,13 @@ private module Stage3 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1631,7 +1689,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1651,6 +1709,27 @@ private module Stage3 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1716,14 +1795,12 @@ private module Stage3 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1771,13 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1785,9 +1859,14 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1838,13 +1917,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1857,6 +1936,15 @@ private module Stage3 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2088,7 +2176,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2127,17 +2215,12 @@ private module Stage4 { bindingset[innercc, inner, call] private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) { resolveReturn(innercc, inner, call) - or - innercc.(CallContextCall).matchesCall(call) } bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -2158,8 +2241,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2185,6 +2267,16 @@ private module Stage4 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -2258,13 +2350,11 @@ private module Stage4 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -2303,10 +2393,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2315,17 +2405,14 @@ private module Stage4 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -2340,7 +2427,13 @@ private module Stage4 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2351,7 +2444,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2371,6 +2464,27 @@ private module Stage4 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -2436,14 +2550,12 @@ private module Stage4 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -2491,13 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2505,9 +2614,14 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2558,13 +2672,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2577,6 +2691,15 @@ private module Stage4 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2609,7 +2732,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2630,7 +2753,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2761,7 +2884,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2982,7 +3105,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3132,7 +3255,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, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and @@ -3151,7 +3274,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3238,7 +3361,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3251,7 +3374,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3275,7 +3398,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3571,7 +3694,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3594,7 +3717,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3614,7 +3737,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3628,9 +3751,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3786,7 +3909,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3800,7 +3923,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3816,7 +3939,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3830,7 +3953,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3927,7 +4050,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3946,7 +4069,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3983,7 +4106,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4040,7 +4163,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4118,7 +4241,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4141,7 +4264,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index 8b446d28b86..9b14db7ef88 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,29 +512,29 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } /** - * Holds if an output from `call` is reached in the flow covered by `revFlow`. + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. */ pragma[nomagic] private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { @@ -597,7 +594,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -610,6 +607,15 @@ private module Stage1 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(ArgNode arg, boolean toReturn | + revFlow(arg, toReturn, config) and + revFlowInToReturn(call, arg, config) and + revFlowIsReturned(call, toReturn, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, config)) and @@ -663,7 +669,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +681,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +741,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -833,6 +838,16 @@ private module Stage2 { PrevStage::revFlow(node, _, _, apa, config) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -899,13 +914,11 @@ private module Stage2 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -944,10 +957,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -956,17 +969,14 @@ private module Stage2 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -981,7 +991,13 @@ private module Stage2 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -992,7 +1008,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1012,6 +1028,27 @@ private module Stage2 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1077,14 +1114,12 @@ private module Stage2 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1132,13 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1146,9 +1178,14 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1199,13 +1236,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1218,6 +1255,15 @@ private module Stage2 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -1245,8 +1291,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1260,8 +1305,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1320,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1366,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1395,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1429,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1488,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1465,6 +1512,16 @@ private module Stage3 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -1538,13 +1595,11 @@ private module Stage3 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -1583,10 +1638,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1595,17 +1650,14 @@ private module Stage3 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -1620,7 +1672,13 @@ private module Stage3 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1631,7 +1689,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1651,6 +1709,27 @@ private module Stage3 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1716,14 +1795,12 @@ private module Stage3 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1771,13 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1785,9 +1859,14 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1838,13 +1917,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1857,6 +1936,15 @@ private module Stage3 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2088,7 +2176,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2127,17 +2215,12 @@ private module Stage4 { bindingset[innercc, inner, call] private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) { resolveReturn(innercc, inner, call) - or - innercc.(CallContextCall).matchesCall(call) } bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -2158,8 +2241,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2185,6 +2267,16 @@ private module Stage4 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -2258,13 +2350,11 @@ private module Stage4 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -2303,10 +2393,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2315,17 +2405,14 @@ private module Stage4 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -2340,7 +2427,13 @@ private module Stage4 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2351,7 +2444,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2371,6 +2464,27 @@ private module Stage4 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -2436,14 +2550,12 @@ private module Stage4 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -2491,13 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2505,9 +2614,14 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2558,13 +2672,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2577,6 +2691,15 @@ private module Stage4 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2609,7 +2732,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2630,7 +2753,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2761,7 +2884,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2982,7 +3105,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3132,7 +3255,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, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and @@ -3151,7 +3274,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3238,7 +3361,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3251,7 +3374,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3275,7 +3398,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3571,7 +3694,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3594,7 +3717,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3614,7 +3737,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3628,9 +3751,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3786,7 +3909,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3800,7 +3923,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3816,7 +3939,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3830,7 +3953,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3927,7 +4050,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3946,7 +4069,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3983,7 +4106,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4040,7 +4163,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4118,7 +4241,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4141,7 +4264,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index 8b446d28b86..9b14db7ef88 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -211,10 +211,7 @@ private predicate fullBarrier(Node node, Configuration config) { * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(Node node1, Node node2, Configuration config) { - ( - simpleLocalFlowStep(node1, node2) or - reverseStepThroughInputOutputAlias(node1, node2) - ) and + simpleLocalFlowStepExt(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -237,7 +234,7 @@ private predicate additionalLocalFlowStep(Node node1, Node node2, Configuration * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ private predicate jumpStep(Node node1, Node node2, Configuration config) { - jumpStep(node1, node2) and + jumpStepCached(node1, node2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and @@ -388,7 +385,7 @@ private module Stage1 { */ pragma[nomagic] private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { - exists(ArgumentNode arg | + exists(ArgNode arg | fwdFlow(arg, cc, config) and viableParamArg(call, _, arg) ) @@ -515,29 +512,29 @@ private module Stage1 { pragma[nomagic] predicate viableParamArgNodeCandFwd1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { viableParamArg(call, p, arg) and fwdFlow(arg, config) } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config - ) { - exists(ParameterNode p | + private predicate revFlowIn(DataFlowCall call, ArgNode arg, boolean toReturn, Configuration config) { + exists(ParamNode p | revFlow(p, toReturn, config) and viableParamArgNodeCandFwd1(call, p, arg, config) ) } pragma[nomagic] - private predicate revFlowInToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) { + private predicate revFlowInToReturn(DataFlowCall call, ArgNode arg, Configuration config) { revFlowIn(call, arg, true, config) } /** - * Holds if an output from `call` is reached in the flow covered by `revFlow`. + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. */ pragma[nomagic] private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { @@ -597,7 +594,7 @@ private module Stage1 { * Holds if flow may enter through `p` and reach a return node making `p` a * candidate for the origin of a summary. */ - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnKindExt kind | throughFlowNodeCand(p, config) and returnFlowCallableNodeCand(c, kind, config) and @@ -610,6 +607,15 @@ private module Stage1 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(ArgNode arg, boolean toReturn | + revFlow(arg, toReturn, config) and + revFlowInToReturn(call, arg, config) and + revFlowIsReturned(call, toReturn, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, config)) and @@ -663,7 +669,7 @@ private predicate flowOutOfCallNodeCand1( pragma[nomagic] private predicate viableParamArgNodeCand1( - DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config + DataFlowCall call, ParamNode p, ArgNode arg, Configuration config ) { Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and Stage1::revFlow(arg, config) @@ -675,7 +681,7 @@ private predicate viableParamArgNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, Configuration config ) { viableParamArgNodeCand1(call, p, arg, config) and Stage1::revFlow(p, config) and @@ -735,8 +741,7 @@ private predicate flowOutOfCallNodeCand1( */ pragma[nomagic] private predicate flowIntoCallNodeCand1( - DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, arg, p, config) and exists(int b, int j | @@ -833,6 +838,16 @@ private module Stage2 { PrevStage::revFlow(node, _, _, apa, config) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -899,13 +914,11 @@ private module Stage2 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -944,10 +957,10 @@ private module Stage2 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -956,17 +969,14 @@ private module Stage2 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -981,7 +991,13 @@ private module Stage2 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -992,7 +1008,7 @@ private module Stage2 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config) ) @@ -1012,6 +1028,27 @@ private module Stage2 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1077,14 +1114,12 @@ private module Stage2 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1132,13 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1146,9 +1178,14 @@ private module Stage2 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1199,13 +1236,13 @@ private module Stage2 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1218,6 +1255,15 @@ private module Stage2 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -1245,8 +1291,7 @@ private predicate flowOutOfCallNodeCand2( pragma[nomagic] private predicate flowIntoCallNodeCand2( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and Stage2::revFlow(node2, pragma[only_bind_into](config)) and @@ -1260,8 +1305,8 @@ private module LocalFlowBigStep { */ private class FlowCheckNode extends Node { FlowCheckNode() { - this instanceof CastNode or - clearsContent(this, _) + castNode(this) or + clearsContentCached(this, _) } } @@ -1275,7 +1320,7 @@ private module LocalFlowBigStep { config.isSource(node) or jumpStep(_, node, config) or additionalJumpStep(_, node, config) or - node instanceof ParameterNode or + node instanceof ParamNode or node instanceof OutNodeExt or store(_, _, node, _) or read(_, _, node) or @@ -1321,21 +1366,21 @@ private module LocalFlowBigStep { Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config, LocalCallContext cc ) { - not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node2, cc.(LocalCallContextSpecificCall).getCall()) and ( localFlowEntry(node1, pragma[only_bind_into](config)) and ( localFlowStepNodeCand1(node1, node2, config) and preservesValue = true and - t = getNodeType(node1) + t = getNodeDataFlowType(node1) or additionalLocalFlowStepNodeCand2(node1, node2, config) and preservesValue = false and - t = getNodeType(node2) + t = getNodeDataFlowType(node2) ) and node1 != node2 and cc.relevantFor(getNodeEnclosingCallable(node1)) and - not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node1, cc.(LocalCallContextSpecificCall).getCall()) and Stage2::revFlow(node2, pragma[only_bind_into](config)) or exists(Node mid | @@ -1350,7 +1395,7 @@ private module LocalFlowBigStep { additionalLocalFlowStepNodeCand2(mid, node2, config) and not mid instanceof FlowCheckNode and preservesValue = false and - t = getNodeType(node2) and + t = getNodeDataFlowType(node2) and Stage2::revFlow(node2, pragma[only_bind_into](config)) ) ) @@ -1384,7 +1429,7 @@ private module Stage3 { private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TFrontNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TFrontNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -1443,7 +1488,9 @@ private module Stage3 { bindingset[node, ap] private predicate filter(Node node, Ap ap) { not ap.isClearedAt(node) and - if node instanceof CastingNode then compatibleTypes(getNodeType(node), ap.getType()) else any() + if node instanceof CastingNode + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) + else any() } bindingset[ap, contentType] @@ -1465,6 +1512,16 @@ private module Stage3 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -1538,13 +1595,11 @@ private module Stage3 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -1583,10 +1638,10 @@ private module Stage3 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -1595,17 +1650,14 @@ private module Stage3 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -1620,7 +1672,13 @@ private module Stage3 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1631,7 +1689,7 @@ private module Stage3 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -1651,6 +1709,27 @@ private module Stage3 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -1716,14 +1795,12 @@ private module Stage3 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -1771,13 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1785,9 +1859,14 @@ private module Stage3 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -1838,13 +1917,13 @@ private module Stage3 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -1857,6 +1936,15 @@ private module Stage3 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2088,7 +2176,7 @@ private module Stage4 { private ApApprox getApprox(Ap ap) { result = ap.getFront() } private ApNil getApNil(Node node) { - PrevStage::revFlow(node, _) and result = TNil(getNodeType(node)) + PrevStage::revFlow(node, _) and result = TNil(getNodeDataFlowType(node)) } bindingset[tc, tail] @@ -2127,17 +2215,12 @@ private module Stage4 { bindingset[innercc, inner, call] private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) { resolveReturn(innercc, inner, call) - or - innercc.(CallContextCall).matchesCall(call) } bindingset[node, cc, config] private LocalCc getLocalCc(Node node, Cc cc, Configuration config) { - exists(Cc cc0 | - cc = pragma[only_bind_into](cc0) and - localFlowEntry(node, config) and - result = getLocalCallContext(cc0, getNodeEnclosingCallable(node)) - ) + localFlowEntry(node, config) and + result = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(node)) } private predicate localStep( @@ -2158,8 +2241,7 @@ private module Stage4 { pragma[nomagic] private predicate flowIntoCall( - DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow, - Configuration config + DataFlowCall call, ArgNode node1, ParamNode node2, boolean allowsFieldFlow, Configuration config ) { flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and PrevStage::revFlow(node2, _, _, _, pragma[only_bind_into](config)) and @@ -2185,6 +2267,16 @@ private module Stage4 { ) } + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, getNodeEnclosingCallable(ret), _, + pragma[only_bind_into](config)) + } + /** * Holds if `node` is reachable with access path `ap` from a source in the * configuration `config`. @@ -2258,13 +2350,11 @@ private module Stage4 { ) or // flow out of a callable - exists(DataFlowCall call | - fwdFlowOut(call, node, any(CcNoCall innercc), cc, argAp, ap, config) - or - exists(Ap argAp0 | - fwdFlowOutFromArg(call, node, argAp0, ap, config) and - fwdFlowIsEntered(call, cc, argAp, argAp0, config) - ) + fwdFlowOutNotFromArg(node, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) ) } @@ -2303,10 +2393,10 @@ private module Stage4 { pragma[nomagic] private predicate fwdFlowIn( - DataFlowCall call, ParameterNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, + DataFlowCall call, ParamNode p, Cc outercc, Cc innercc, ApOption argAp, Ap ap, Configuration config ) { - exists(ArgumentNode arg, boolean allowsFieldFlow | + exists(ArgNode arg, boolean allowsFieldFlow | fwdFlow(arg, outercc, argAp, ap, config) and flowIntoCall(call, arg, p, allowsFieldFlow, config) and innercc = getCallContextCall(call, getNodeEnclosingCallable(p), outercc) @@ -2315,17 +2405,14 @@ private module Stage4 { ) } - /** - * Holds if flow may exit from `call` at `out` with access path `ap`. The - * inner call context is `innercc`, but `ccOut` is just the call context - * based on the return step. In the case of through-flow `ccOut` is discarded - * and replaced by the outer call context as tracked by `fwdFlowIsEntered`. - */ pragma[nomagic] - private predicate fwdFlowOut( - DataFlowCall call, Node out, Cc innercc, Cc ccOut, ApOption argAp, Ap ap, Configuration config + private predicate fwdFlowOutNotFromArg( + Node out, Cc ccOut, ApOption argAp, Ap ap, Configuration config ) { - exists(ReturnNodeExt ret, boolean allowsFieldFlow, DataFlowCallable inner | + exists( + DataFlowCall call, ReturnNodeExt ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | fwdFlow(ret, innercc, argAp, ap, config) and flowOutOfCall(call, ret, out, allowsFieldFlow, config) and inner = getNodeEnclosingCallable(ret) and @@ -2340,7 +2427,13 @@ private module Stage4 { private predicate fwdFlowOutFromArg( DataFlowCall call, Node out, Ap argAp, Ap ap, Configuration config ) { - fwdFlowOut(call, out, any(CcCall ccc), _, apSome(argAp), ap, config) + exists(ReturnNodeExt ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and + ccc.matchesCall(call) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2351,7 +2444,7 @@ private module Stage4 { private predicate fwdFlowIsEntered( DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | fwdFlowIn(call, p, cc, _, argAp, ap, config) and PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) ) @@ -2371,6 +2464,27 @@ private module Stage4 { fwdFlowConsCand(ap1, c, ap2, config) } + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, Node out, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, argAp0, ap, config) and + fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNode arg, ParamNode p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + /** * Holds if `node` with access path `ap` is part of a path from a source to a * sink in the configuration `config`. @@ -2436,14 +2550,12 @@ private module Stage4 { ) or // flow into a callable - exists(DataFlowCall call | - revFlowIn(call, node, toReturn, returnAp, ap, config) and - toReturn = false - or - exists(Ap returnAp0 | - revFlowInToReturn(call, node, returnAp0, ap, config) and - revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) - ) + revFlowInNotToReturn(node, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) ) or // flow out of a callable @@ -2491,13 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, ApOption returnAp, Ap ap, - Configuration config - ) { - exists(ParameterNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2505,9 +2614,14 @@ private module Stage4 { pragma[nomagic] private predicate revFlowInToReturn( - DataFlowCall call, ArgumentNode arg, Ap returnAp, Ap ap, Configuration config + DataFlowCall call, ArgNode arg, Ap returnAp, Ap ap, Configuration config ) { - revFlowIn(call, arg, true, apSome(returnAp), ap, config) + exists(ParamNode p, boolean allowsFieldFlow | + revFlow(p, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) + | + ap instanceof ApNil or allowsFieldFlow = true + ) } /** @@ -2558,13 +2672,13 @@ private module Stage4 { pragma[noinline] private predicate parameterFlow( - ParameterNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ParamNode p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ) { revFlow(p, true, apSome(ap0), ap, config) and c = getNodeEnclosingCallable(p) } - predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, Ap ap, Configuration config) { + predicate parameterMayFlowThrough(ParamNode p, DataFlowCallable c, Ap ap, Configuration config) { exists(ReturnNodeExt ret, Ap ap0, ReturnKindExt kind, int pos | parameterFlow(p, ap, ap0, c, config) and c = getNodeEnclosingCallable(ret) and @@ -2577,6 +2691,15 @@ private module Stage4 { ) } + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(Ap returnAp0, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap | + revFlow(arg, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + predicate stats(boolean fwd, int nodes, int fields, int conscand, int tuples, Configuration config) { fwd = true and nodes = count(Node node | fwdFlow(node, _, _, _, config)) and @@ -2609,7 +2732,7 @@ private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { + TSummaryCtxSome(ParamNode p, AccessPath ap) { Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), _) } @@ -2630,7 +2753,7 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { - private ParameterNode p; + private ParamNode p; private AccessPath ap; SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } @@ -2761,7 +2884,7 @@ private newtype TPathNode = config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | @@ -2982,7 +3105,7 @@ class PathNode extends TPathNode { Configuration getConfiguration() { none() } private predicate isHidden() { - nodeIsHidden(this.getNode()) and + hiddenNode(this.getNode()) and not this.isSource() and not this instanceof PathNodeSink } @@ -3132,7 +3255,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, getNodeEnclosingCallable(midnode)) and + localCC = getLocalCallContext(pragma[only_bind_out](cc), getNodeEnclosingCallable(midnode)) and ap0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and @@ -3151,7 +3274,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt cc instanceof CallContextAny and sc instanceof SummaryCtxNone and mid.getAp() instanceof AccessPathNil and - ap = TAccessPathNil(getNodeType(node)) + ap = TAccessPathNil(getNodeDataFlowType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -3238,7 +3361,7 @@ pragma[noinline] private predicate pathIntoArg( PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3251,7 +3374,7 @@ pragma[noinline] private predicate parameterCand( DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { - exists(ParameterNode p | + exists(ParamNode p | Stage4::revFlow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) @@ -3275,7 +3398,7 @@ private predicate pathIntoCallable0( * respectively. */ private predicate pathIntoCallable( - PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, + PathNodeMid mid, ParamNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { exists(int i, DataFlowCallable callable, AccessPath ap | @@ -3571,7 +3694,7 @@ private module FlowExploration { private newtype TSummaryCtx1 = TSummaryCtx1None() or - TSummaryCtx1Param(ParameterNode p) + TSummaryCtx1Param(ParamNode p) private newtype TSummaryCtx2 = TSummaryCtx2None() or @@ -3594,7 +3717,7 @@ private module FlowExploration { cc instanceof CallContextAny and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and not fullBarrier(node, config) and exists(config.explorationLimit()) or @@ -3614,7 +3737,7 @@ private module FlowExploration { or exists(PartialPathNodeRev mid | revPartialPathStep(mid, node, sc1, sc2, ap, config) and - not clearsContent(node, ap.getHead()) and + not clearsContentCached(node, ap.getHead()) and not fullBarrier(node, config) and distSink(getNodeEnclosingCallable(node), config) <= config.explorationLimit() ) @@ -3628,9 +3751,9 @@ private module FlowExploration { exists(PartialPathNodeFwd mid | partialPathStep(mid, node, cc, sc1, sc2, ap, config) and not fullBarrier(node, config) and - not clearsContent(node, ap.getHead().getContent()) and + not clearsContentCached(node, ap.getHead().getContent()) and if node instanceof CastingNode - then compatibleTypes(getNodeType(node), ap.getType()) + then compatibleTypes(getNodeDataFlowType(node), ap.getType()) else any() ) } @@ -3786,7 +3909,7 @@ private module FlowExploration { PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap, Configuration config ) { - not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + not isUnreachableInCallCached(node, cc.(CallContextSpecificCall).getCall()) and ( localFlowStep(mid.getNode(), node, config) and cc = mid.getCallContext() and @@ -3800,7 +3923,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() ) or @@ -3816,7 +3939,7 @@ private module FlowExploration { sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and mid.getAp() instanceof PartialAccessPathNil and - ap = TPartialNil(getNodeType(node)) and + ap = TPartialNil(getNodeDataFlowType(node)) and config = mid.getConfiguration() or partialPathStoreStep(mid, _, _, node, ap) and @@ -3830,7 +3953,7 @@ private module FlowExploration { sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and apConsFwd(ap, tc, ap0, config) and - compatibleTypes(ap.getType(), getNodeType(node)) + compatibleTypes(ap.getType(), getNodeDataFlowType(node)) ) or partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config) @@ -3927,7 +4050,7 @@ private module FlowExploration { PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap, Configuration config ) { - exists(ArgumentNode arg | + exists(ArgNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and @@ -3946,7 +4069,7 @@ private module FlowExploration { } private predicate partialPathIntoCallable( - PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc, + PartialPathNodeFwd mid, ParamNode p, CallContext outercc, CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap, Configuration config ) { @@ -3983,7 +4106,7 @@ private module FlowExploration { DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc, PartialAccessPath ap, Configuration config ) { - exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | + exists(ParamNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 | partialPathIntoCallable(mid, p, cc, innercc, sc1, sc2, call, _, config) and paramFlowsThroughInPartialPath(kind, innercc, sc1, sc2, ap, config) ) @@ -4040,7 +4163,7 @@ private module FlowExploration { apConsRev(ap, c, ap0, config) ) or - exists(ParameterNode p | + exists(ParamNode p | mid.getNode() = p and viableParamArg(_, p, node) and sc1 = mid.getSummaryCtx1() and @@ -4118,7 +4241,7 @@ private module FlowExploration { int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap, Configuration config ) { - exists(PartialPathNodeRev mid, ParameterNode p | + exists(PartialPathNodeRev mid, ParamNode p | mid.getNode() = p and p.isParameterOf(_, pos) and sc1 = mid.getSummaryCtx1() and @@ -4141,7 +4264,7 @@ private module FlowExploration { pragma[nomagic] private predicate revPartialPathThroughCallable( - PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config + PartialPathNodeRev mid, ArgNode node, RevPartialAccessPath ap, Configuration config ) { exists(DataFlowCall call, int pos | revPartialPathThroughCallable0(call, mid, pos, ap, config) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index a51c20c2288..462e89ac9ed 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -31,26 +31,26 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) { * currently excludes read-steps, store-steps, and flow-through. * * The analysis uses non-linear recursion: When computing a flow path in or out - * of a call, we use the results of the analysis recursively to resolve lamba + * of a call, we use the results of the analysis recursively to resolve lambda * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. */ private module LambdaFlow { - private predicate viableParamNonLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallable(call), i) } - private predicate viableParamLambda(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableLambda(call, _), i) } - private predicate viableParamArgNonLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamNonLambda(call, i, p) and arg.argumentOf(call, i) ) } - private predicate viableParamArgLambda(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParamLambda(call, i, p) and arg.argumentOf(call, i) @@ -118,8 +118,8 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and - if node instanceof CastNode or node instanceof ArgumentNode or node instanceof ReturnNode - then compatibleTypes(t, getNodeType(node)) + if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode + then compatibleTypes(t, getNodeDataFlowType(node)) else any() } @@ -129,7 +129,7 @@ private module LambdaFlow { boolean toJump, DataFlowCallOption lastCall ) { lambdaCall(lambdaCall, kind, node) and - t = getNodeType(node) and + t = getNodeDataFlowType(node) and toReturn = false and toJump = false and lastCall = TDataFlowCallNone() @@ -146,7 +146,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -160,7 +160,7 @@ private module LambdaFlow { toJump = true and lastCall = TDataFlowCallNone() | - jumpStep(node, mid) and + jumpStepCached(node, mid) and t = t0 or exists(boolean preservesValue | @@ -168,7 +168,7 @@ private module LambdaFlow { getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) | preservesValue = false and - t = getNodeType(node) + t = getNodeDataFlowType(node) or preservesValue = true and t = t0 @@ -176,7 +176,7 @@ private module LambdaFlow { ) or // flow into a callable - exists(ParameterNode p, DataFlowCallOption lastCall0, DataFlowCall call | + exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and ( if lastCall0 = TDataFlowCallNone() and toJump = false @@ -227,7 +227,7 @@ private module LambdaFlow { pragma[nomagic] predicate revLambdaFlowIn( - DataFlowCall lambdaCall, LambdaCallKind kind, ParameterNode p, DataFlowType t, boolean toJump, + DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, DataFlowCallOption lastCall ) { revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) @@ -242,6 +242,89 @@ private DataFlowCallable viableCallableExt(DataFlowCall call) { cached private module Cached { + /** + * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to + * force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby + * collapsing the two stages. + */ + cached + predicate forceCachingInSameStage() { any() } + + cached + predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() } + + cached + predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { + c = call.getEnclosingCallable() + } + + cached + predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } + + cached + predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } + + cached + predicate clearsContentCached(Node n, Content c) { clearsContent(n, c) } + + cached + predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } + + cached + predicate outNodeExt(Node n) { + n instanceof OutNode + or + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode + } + + cached + predicate hiddenNode(Node n) { nodeIsHidden(n) } + + cached + OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { + result = getAnOutNode(call, k.(ValueReturnKind).getKind()) + or + exists(ArgNode arg | + result.(PostUpdateNode).getPreUpdateNode() = arg and + arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition()) + ) + } + + cached + predicate returnNodeExt(Node n, ReturnKindExt k) { + k = TValueReturn(n.(ReturnNode).getKind()) + or + exists(ParamNode p, int pos | + parameterValueFlowsToPreUpdate(p, n) and + p.isParameterOf(_, pos) and + k = TParamUpdate(pos) + ) + } + + cached + predicate castNode(Node n) { n instanceof CastNode } + + cached + predicate castingNode(Node n) { + castNode(n) or + n instanceof ParamNode or + n instanceof OutNodeExt or + // For reads, `x.f`, we want to check that the tracked type after the read (which + // is obtained by popping the head of the access path stack) is compatible with + // the type of `x.f`. + read(_, _, n) + } + + cached + predicate parameterNode(Node n, DataFlowCallable c, int i) { + n.(ParameterNode).isParameterOf(c, i) + } + + cached + predicate argumentNode(Node n, DataFlowCall call, int pos) { + n.(ArgumentNode).argumentOf(call, pos) + } + /** * Gets a viable target for the lambda call `call`. * @@ -261,7 +344,7 @@ private module Cached { * The instance parameter is considered to have index `-1`. */ pragma[nomagic] - private predicate viableParam(DataFlowCall call, int i, ParameterNode p) { + private predicate viableParam(DataFlowCall call, int i, ParamNode p) { p.isParameterOf(viableCallableExt(call), i) } @@ -270,11 +353,11 @@ private module Cached { * dispatch into account. */ cached - predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) { + predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { exists(int i | viableParam(call, i, p) and arg.argumentOf(call, i) and - compatibleTypes(getNodeType(arg), getNodeType(p)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) ) } @@ -312,7 +395,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to `node`. */ pragma[nomagic] - private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) { + private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { p = node and read = false or @@ -325,30 +408,30 @@ private module Cached { // read exists(Node mid | parameterValueFlowCand(p, mid, false) and - readStep(mid, _, node) and + read(mid, _, node) and read = true ) or // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, false) and argumentValueFlowsThroughCand(arg, node, read) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArgCand(p, arg, read) and argumentValueFlowsThroughCand(arg, node, false) ) } pragma[nomagic] - private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) { + private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { parameterValueFlowCand(p, arg, read) } pragma[nomagic] - predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) { + predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { parameterValueFlowCand(p, n.getPreUpdateNode(), false) } @@ -360,7 +443,7 @@ private module Cached { * `read` indicates whether it is contents of `p` that can flow to the return * node. */ - predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) { + predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { exists(ReturnNode ret | parameterValueFlowCand(p, ret, read) and kind = ret.getKind() @@ -369,9 +452,9 @@ private module Cached { pragma[nomagic] private predicate argumentValueFlowsThroughCand0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read + DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturnCand(param, kind, read) ) } @@ -382,14 +465,14 @@ private module Cached { * * `read` indicates whether it is contents of `arg` that can flow to `out`. */ - predicate argumentValueFlowsThroughCand(ArgumentNode arg, Node out, boolean read) { + predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughCand0(call, arg, kind, read) and out = getAnOutNode(call, kind) ) } - predicate cand(ParameterNode p, Node n) { + predicate cand(ParamNode p, Node n) { parameterValueFlowCand(p, n, _) and ( parameterValueFlowReturnCand(p, _, _) @@ -416,21 +499,21 @@ private module Cached { * If a read step was taken, then `read` captures the `Content`, the * container type, and the content type. */ - predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) { + predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { parameterValueFlow0(p, node, read) and if node instanceof CastingNode then // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(p), getNodeType(node)) + compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) or // getter - compatibleTypes(read.getContentType(), getNodeType(node)) + compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) else any() } pragma[nomagic] - private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) { + private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { p = node and Cand::cand(p, _) and read = TReadStepTypesNone() @@ -447,7 +530,7 @@ private module Cached { readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, read.getContentType()) and Cand::parameterValueFlowReturnCand(p, _, true) and - compatibleTypes(getNodeType(p), read.getContainerType()) + compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) ) or parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) @@ -455,34 +538,32 @@ private module Cached { pragma[nomagic] private predicate parameterValueFlow0_0( - ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read + ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read ) { // flow through: no prior read - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, mustBeNone) and argumentValueFlowsThrough(arg, read, node) ) or // flow through: no read inside method - exists(ArgumentNode arg | + exists(ArgNode arg | parameterValueFlowArg(p, arg, read) and argumentValueFlowsThrough(arg, mustBeNone, node) ) } pragma[nomagic] - private predicate parameterValueFlowArg( - ParameterNode p, ArgumentNode arg, ReadStepTypesOption read - ) { + private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { parameterValueFlow(p, arg, read) and Cand::argumentValueFlowsThroughCand(arg, _, _) } pragma[nomagic] private predicate argumentValueFlowsThrough0( - DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read + DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read ) { - exists(ParameterNode param | viableParamArg(call, param, arg) | + exists(ParamNode param | viableParamArg(call, param, arg) | parameterValueFlowReturn(param, kind, read) ) } @@ -496,18 +577,18 @@ private module Cached { * container type, and the content type. */ pragma[nomagic] - predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) { + predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, read) and out = getAnOutNode(call, kind) | // normal flow through read = TReadStepTypesNone() and - compatibleTypes(getNodeType(arg), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) or // getter - compatibleTypes(getNodeType(arg), read.getContainerType()) and - compatibleTypes(read.getContentType(), getNodeType(out)) + compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) ) } @@ -516,7 +597,7 @@ private module Cached { * value-preserving steps and a single read step, not taking call * contexts into account, thus representing a getter-step. */ - predicate getterStep(ArgumentNode arg, Content c, Node out) { + predicate getterStep(ArgNode arg, Content c, Node out) { argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) } @@ -529,7 +610,7 @@ private module Cached { * container type, and the content type. */ private predicate parameterValueFlowReturn( - ParameterNode p, ReturnKind kind, ReadStepTypesOption read + ParamNode p, ReturnKind kind, ReadStepTypesOption read ) { exists(ReturnNode ret | parameterValueFlow(p, ret, read) and @@ -553,7 +634,7 @@ private module Cached { private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { mayBenefitFromCallContext(call, callable) or - callable = call.getEnclosingCallable() and + callEnclosingCallable(call, callable) and exists(viableCallableLambda(call, TDataFlowCallSome(_))) } @@ -611,7 +692,7 @@ private module Cached { mayBenefitFromCallContextExt(call, _) and c = viableCallableExt(call) and ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and - tgts = strictcount(DataFlowCall ctx | viableCallableExt(ctx) = call.getEnclosingCallable()) and + tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and ctxtgts < tgts ) } @@ -635,8 +716,7 @@ private module Cached { * Holds if `p` can flow to the pre-update node associated with post-update * node `n`, in the same callable, using only value-preserving steps. */ - cached - predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) { + private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) } @@ -644,9 +724,9 @@ private module Cached { Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType ) { storeStep(node1, c, node2) and - readStep(_, c, _) and - contentType = getNodeType(node1) and - containerType = getNodeType(node2) + read(_, c, _) and + contentType = getNodeDataFlowType(node1) and + containerType = getNodeDataFlowType(node2) or exists(Node n1, Node n2 | n1 = node1.(PostUpdateNode).getPreUpdateNode() and @@ -654,12 +734,15 @@ private module Cached { | argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) or - readStep(n2, c, n1) and - contentType = getNodeType(n1) and - containerType = getNodeType(n2) + read(n2, c, n1) and + contentType = getNodeDataFlowType(n1) and + containerType = getNodeDataFlowType(n2) ) } + cached + predicate read(Node node1, Content c, Node node2) { readStep(node1, c, node2) } + /** * Holds if data can flow from `node1` to `node2` via a direct assignment to * `f`. @@ -678,8 +761,9 @@ private module Cached { * are aliases. A typical example is a function returning `this`, implementing a fluent * interface. */ - cached - predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) { + private predicate reverseStepThroughInputOutputAlias( + PostUpdateNode fromNode, PostUpdateNode toNode + ) { exists(Node fromPre, Node toPre | fromPre = fromNode.getPreUpdateNode() and toPre = toNode.getPreUpdateNode() @@ -688,14 +772,20 @@ private module Cached { // Does the language-specific simpleLocalFlowStep already model flow // from function input to output? fromPre = getAnOutNode(c, _) and - toPre.(ArgumentNode).argumentOf(c, _) and - simpleLocalFlowStep(toPre.(ArgumentNode), fromPre) + toPre.(ArgNode).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgNode), fromPre) ) or argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) ) } + cached + predicate simpleLocalFlowStepExt(Node node1, Node node2) { + simpleLocalFlowStep(node1, node2) or + reverseStepThroughInputOutputAlias(node1, node2) + } + /** * Holds if the call context `call` either improves virtual dispatch in * `callable` or if it allows us to prune unreachable nodes in `callable`. @@ -704,7 +794,7 @@ private module Cached { predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { reducedViableImplInCallContext(_, callable, call) or - exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) } cached @@ -726,12 +816,12 @@ private module Cached { cached newtype TLocalFlowCallContext = TAnyLocalCall() or - TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) } + TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } cached newtype TReturnKindExt = TValueReturn(ReturnKind kind) or - TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) } + TParamUpdate(int pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } cached newtype TBooleanOption = @@ -761,23 +851,15 @@ private module Cached { * A `Node` at which a cast can occur such that the type should be checked. */ class CastingNode extends Node { - CastingNode() { - this instanceof ParameterNode or - this instanceof CastNode or - this instanceof OutNodeExt or - // For reads, `x.f`, we want to check that the tracked type after the read (which - // is obtained by popping the head of the access path stack) is compatible with - // the type of `x.f`. - readStep(_, _, this) - } + CastingNode() { castingNode(this) } } private predicate readStepWithTypes( Node n1, DataFlowType container, Content c, Node n2, DataFlowType content ) { - readStep(n1, c, n2) and - container = getNodeType(n1) and - content = getNodeType(n2) + read(n1, c, n2) and + container = getNodeDataFlowType(n1) and + content = getNodeDataFlowType(n2) } private newtype TReadStepTypesOption = @@ -854,7 +936,7 @@ class CallContextSomeCall extends CallContextCall, TSomeCall { override string toString() { result = "CcSomeCall" } override predicate relevantFor(DataFlowCallable callable) { - exists(ParameterNode p | getNodeEnclosingCallable(p) = callable) + exists(ParamNode p | getNodeEnclosingCallable(p) = callable) } override predicate matchesCall(DataFlowCall call) { any() } @@ -866,7 +948,7 @@ class CallContextReturn extends CallContextNoCall, TReturn { } override predicate relevantFor(DataFlowCallable callable) { - exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable) + exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) } } @@ -899,7 +981,7 @@ class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall } private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { - exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCall(n, call)) + exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) } /** @@ -913,26 +995,37 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) else result instanceof LocalCallContextAny } +/** + * The value of a parameter at function entry, viewed as a node in a data + * flow graph. + */ +class ParamNode extends Node { + ParamNode() { parameterNode(this, _, _) } + + /** + * Holds if this node is the parameter of callable `c` at the specified + * (zero-based) position. + */ + predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) } +} + +/** A data-flow node that represents a call argument. */ +class ArgNode extends Node { + ArgNode() { argumentNode(this, _, _) } + + /** Holds if this argument occurs at the given position in the given call. */ + final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) } +} + /** * A node from which flow can return to the caller. This is either a regular * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. */ class ReturnNodeExt extends Node { - ReturnNodeExt() { - this instanceof ReturnNode or - parameterValueFlowsToPreUpdate(_, this) - } + ReturnNodeExt() { returnNodeExt(this, _) } /** Gets the kind of this returned value. */ - ReturnKindExt getKind() { - result = TValueReturn(this.(ReturnNode).getKind()) - or - exists(ParameterNode p, int pos | - parameterValueFlowsToPreUpdate(p, this) and - p.isParameterOf(_, pos) and - result = TParamUpdate(pos) - ) - } + ReturnKindExt getKind() { returnNodeExt(this, result) } } /** @@ -940,11 +1033,7 @@ class ReturnNodeExt extends Node { * or a post-update node associated with a call argument. */ class OutNodeExt extends Node { - OutNodeExt() { - this instanceof OutNode - or - this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode - } + OutNodeExt() { outNodeExt(this) } } /** @@ -957,7 +1046,7 @@ abstract class ReturnKindExt extends TReturnKindExt { abstract string toString(); /** Gets a node corresponding to data flow out of `call`. */ - abstract OutNodeExt getAnOutNode(DataFlowCall call); + final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } } class ValueReturnKind extends ReturnKindExt, TValueReturn { @@ -968,10 +1057,6 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn { ReturnKind getKind() { result = kind } override string toString() { result = kind.toString() } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - result = getAnOutNode(call, this.getKind()) - } } class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { @@ -982,13 +1067,6 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { int getPosition() { result = pos } override string toString() { result = "param update " + pos } - - override OutNodeExt getAnOutNode(DataFlowCall call) { - exists(ArgumentNode arg | - result.(PostUpdateNode).getPreUpdateNode() = arg and - arg.argumentOf(call, this.getPosition()) - ) - } } /** A callable tagged with a relevant return kind. */ @@ -1015,10 +1093,13 @@ class ReturnPosition extends TReturnPosition0 { */ pragma[inline] DataFlowCallable getNodeEnclosingCallable(Node n) { - exists(Node n0 | - pragma[only_bind_into](n0) = n and - pragma[only_bind_into](result) = n0.getEnclosingCallable() - ) + nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) +} + +/** Gets the type of `n` used for type pruning. */ +pragma[inline] +DataFlowType getNodeDataFlowType(Node n) { + nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) } pragma[noinline] @@ -1042,7 +1123,7 @@ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall cc instanceof CallContextAny and callable = viableCallableExt(call) or exists(DataFlowCallable c0, DataFlowCall call0 | - call0.getEnclosingCallable() = callable and + callEnclosingCallable(call0, callable) and cc = TReturn(c0, call0) and c0 = prunedViableImplInCallContextReverse(call0, call) ) @@ -1063,8 +1144,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { result = viableCallableExt(call) and cc instanceof CallContextReturn } -predicate read = readStep/3; - /** An optional Boolean value. */ class BooleanOption extends TBooleanOption { string toString() { @@ -1116,7 +1195,7 @@ abstract class AccessPathFront extends TAccessPathFront { TypedContent getHead() { this = TFrontHead(result) } - predicate isClearedAt(Node n) { clearsContent(n, getHead().getContent()) } + predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) } } class AccessPathFrontNil extends AccessPathFront, TFrontNil { diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll index 3b646973e28..6373382a204 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -7,7 +7,6 @@ private import DataFlowImplCommon private import ControlFlowReachability private import FlowSummaryImpl as FlowSummaryImpl private import semmle.code.csharp.dataflow.FlowSummary -private import semmle.code.csharp.Caching private import semmle.code.csharp.Conversion private import semmle.code.csharp.dataflow.internal.SsaImpl as SsaImpl private import semmle.code.csharp.ExprOrStmtParent @@ -24,12 +23,12 @@ abstract class NodeImpl extends Node { abstract DataFlowCallable getEnclosingCallableImpl(); /** Do not call: use `getType()` instead. */ + cached abstract DotNet::Type getTypeImpl(); /** Gets the type of this node used for type pruning. */ - cached Gvn::GvnType getDataFlowType() { - Stages::DataFlowStage::forceCachingInSameStage() and + forceCachingInSameStage() and exists(Type t0 | result = Gvn::getGlobalValueNumber(t0) | t0 = getCSharpType(this.getType()) or @@ -39,12 +38,15 @@ abstract class NodeImpl extends Node { } /** Do not call: use `getControlFlowNode()` instead. */ + cached abstract ControlFlow::Node getControlFlowNodeImpl(); /** Do not call: use `getLocation()` instead. */ + cached abstract Location getLocationImpl(); /** Do not call: use `toString()` instead. */ + cached abstract string toStringImpl(); } @@ -53,14 +55,22 @@ private class ExprNodeImpl extends ExprNode, NodeImpl { result = this.getExpr().getEnclosingCallable() } - override DotNet::Type getTypeImpl() { result = this.getExpr().getType() } + override DotNet::Type getTypeImpl() { + forceCachingInSameStage() and + result = this.getExpr().getType() + } - override ControlFlow::Nodes::ElementNode getControlFlowNodeImpl() { this = TExprNode(result) } + override ControlFlow::Nodes::ElementNode getControlFlowNodeImpl() { + forceCachingInSameStage() and this = TExprNode(result) + } - override Location getLocationImpl() { result = this.getExpr().getLocation() } + override Location getLocationImpl() { + forceCachingInSameStage() and result = this.getExpr().getLocation() + } override string toStringImpl() { - result = this.getControlFlowNode().toString() + forceCachingInSameStage() and + result = this.getControlFlowNodeImpl().toString() or exists(CIL::Expr e | this = TCilExprNode(e) and @@ -380,6 +390,22 @@ module LocalFlow { } } +/** + * This is the local flow predicate that is used as a building block in global + * data flow. It excludes SSA flow through instance fields, as flow through fields + * is handled by the global data-flow library, but includes various other steps + * that are only relevant for global flow. + */ +predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { + LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) + or + LocalFlow::localFlowCapturedVarStep(nodeFrom, nodeTo) + or + FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true) + or + nodeTo.(ObjectCreationNode).getPreUpdateNode() = nodeFrom.(ObjectInitializerNode) +} + pragma[noinline] private Expr getImplicitArgument(Call c, int pos) { result = c.getArgument(pos) and @@ -568,12 +594,16 @@ private Type getCSharpType(DotNet::Type t) { result.matchesHandle(t) } +private class RelevantDataFlowType extends DataFlowType { + RelevantDataFlowType() { this = any(NodeImpl n).getDataFlowType() } +} + /** A GVN type that is either a `DataFlowType` or unifiable with a `DataFlowType`. */ private class DataFlowTypeOrUnifiable extends Gvn::GvnType { pragma[nomagic] DataFlowTypeOrUnifiable() { - this instanceof DataFlowType or - Gvn::unifiable(any(DataFlowType t), this) + this instanceof RelevantDataFlowType or + Gvn::unifiable(any(RelevantDataFlowType t), this) } } @@ -584,7 +614,7 @@ private TypeParameter getATypeParameterSubType(DataFlowTypeOrUnifiable t) { } pragma[noinline] -private TypeParameter getATypeParameterSubTypeRestricted(DataFlowType t) { +private TypeParameter getATypeParameterSubTypeRestricted(RelevantDataFlowType t) { result = getATypeParameterSubType(t) } @@ -600,17 +630,30 @@ private Gvn::GvnType getANonTypeParameterSubType(DataFlowTypeOrUnifiable t) { } pragma[noinline] -private Gvn::GvnType getANonTypeParameterSubTypeRestricted(DataFlowType t) { +private Gvn::GvnType getANonTypeParameterSubTypeRestricted(RelevantDataFlowType t) { result = getANonTypeParameterSubType(t) } /** A collection of cached types and predicates to be evaluated in the same stage. */ cached private module Cached { + private import TaintTrackingPrivate as TaintTrackingPrivate + + // Add artificial dependencies to enforce all cached predicates are evaluated + // in the "DataFlowImplCommon stage" + private predicate forceCaching() { + TaintTrackingPrivate::forceCachingInSameStage() or + exists(any(NodeImpl n).getTypeImpl()) or + exists(any(NodeImpl n).getControlFlowNodeImpl()) or + exists(any(NodeImpl n).getLocationImpl()) or + exists(any(NodeImpl n).toStringImpl()) + } + cached newtype TNode = TExprNode(ControlFlow::Nodes::ElementNode cfn) { - Stages::DataFlowStage::forceCachingInSameStage() and cfn.getElement() instanceof Expr + forceCaching() and + cfn.getElement() instanceof Expr } or TCilExprNode(CIL::Expr e) { e.getImplementation() instanceof CIL::BestImplementation } or TSsaDefinitionNode(Ssa::Definition def) { @@ -665,23 +708,6 @@ private module Cached { callCfn = any(Call c | isParamsArg(c, _, _)).getAControlFlowNode() } - /** - * This is the local flow predicate that is used as a building block in global - * data flow. It excludes SSA flow through instance fields, as flow through fields - * is handled by the global data-flow library, but includes various other steps - * that are only relevant for global flow. - */ - cached - predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { - LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) - or - LocalFlow::localFlowCapturedVarStep(nodeFrom, nodeTo) - or - FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true) - or - nodeTo.(ObjectCreationNode).getPreUpdateNode() = nodeFrom.(ObjectInitializerNode) - } - /** * Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local * (intra-procedural) step. @@ -700,178 +726,14 @@ private module Cached { FlowSummaryImpl::Private::Steps::summaryThroughStep(nodeFrom, nodeTo, true) } - /** - * Holds if `pred` can flow to `succ`, by jumping from one callable to - * another. Additional steps specified by the configuration are *not* - * taken into account. - */ - cached - predicate jumpStepImpl(Node pred, Node succ) { - pred.(NonLocalJumpNode).getAJumpSuccessor(true) = succ - or - exists(FieldOrProperty fl, FieldOrPropertyRead flr | - fl.isStatic() and - fl.isFieldLike() and - fl.getAnAssignedValue() = pred.asExpr() and - fl.getAnAccess() = flr and - flr = succ.asExpr() and - flr.hasNonlocalValue() - ) - or - exists(JumpReturnKind jrk, DataFlowCall call | - FlowSummaryImpl::Private::summaryReturnNode(pred, jrk) and - viableCallable(call) = jrk.getTarget() and - succ = getAnOutNode(call, jrk.getTargetReturnKind()) - ) - } - cached newtype TContent = TFieldContent(Field f) { f.isUnboundDeclaration() } or TPropertyContent(Property p) { p.isUnboundDeclaration() } or TElementContent() - /** - * Holds if data can flow from `node1` to `node2` via an assignment to - * content `c`. - */ - cached - predicate storeStepImpl(Node node1, Content c, Node node2) { - exists(StoreStepConfiguration x, ExprNode node, boolean postUpdate | - hasNodePath(x, node1, node) and - if postUpdate = true then node = node2.(PostUpdateNode).getPreUpdateNode() else node = node2 - | - fieldOrPropertyStore(_, c, node1.asExpr(), node.getExpr(), postUpdate) - or - arrayStore(_, node1.asExpr(), node.getExpr(), postUpdate) and c instanceof ElementContent - ) - or - exists(StoreStepConfiguration x, Expr arg, ControlFlow::Node callCfn | - x.hasExprPath(arg, node1.(ExprNode).getControlFlowNode(), _, callCfn) and - node2 = TParamsArgumentNode(callCfn) and - isParamsArg(_, arg, _) and - c instanceof ElementContent - ) - or - exists(Expr e | - e = node1.asExpr() and - node2.(YieldReturnNode).getYieldReturnStmt().getExpr() = e and - c instanceof ElementContent - ) - or - exists(Expr e | - e = node1.asExpr() and - node2.(AsyncReturnNode).getExpr() = e and - c = getResultContent() - ) - or - FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, c, node2) - } - pragma[nomagic] - private PropertyContent getResultContent() { - result.getProperty() = any(SystemThreadingTasksTaskTClass c_).getResultProperty() - } - - /** - * Holds if data can flow from `node1` to `node2` via a read of content `c`. - */ - cached - predicate readStepImpl(Node node1, Content c, Node node2) { - exists(ReadStepConfiguration x | - hasNodePath(x, node1, node2) and - fieldOrPropertyRead(node1.asExpr(), c, node2.asExpr()) - or - hasNodePath(x, node1, node2) and - arrayRead(node1.asExpr(), node2.asExpr()) and - c instanceof ElementContent - or - exists(ForeachStmt fs, Ssa::ExplicitDefinition def | - x.hasDefPath(fs.getIterableExpr(), node1.getControlFlowNode(), def.getADefinition(), - def.getControlFlowNode()) and - node2.(SsaDefinitionNode).getDefinition() = def and - c instanceof ElementContent - ) - or - hasNodePath(x, node1, node2) and - node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and - c = getResultContent() - or - // node1 = (..., node2, ...) - // node1.ItemX flows to node2 - exists(TupleExpr te, int i, Expr item | - te = node1.asExpr() and - not te.isConstruction() and - c.(FieldContent).getField() = te.getType().(TupleType).getElement(i).getUnboundDeclaration() and - // node1 = (..., item, ...) - te.getArgument(i) = item - | - // item = (..., ..., ...) in node1 = (..., (..., ..., ...), ...) - node2.asExpr().(TupleExpr) = item and - hasNodePath(x, node1, node2) - or - // item = variable in node1 = (..., variable, ...) - exists(AssignableDefinitions::TupleAssignmentDefinition tad, Ssa::ExplicitDefinition def | - node2.(SsaDefinitionNode).getDefinition() = def and - def.getADefinition() = tad and - tad.getLeaf() = item and - hasNodePath(x, node1, node2) - ) - or - // item = variable in node1 = (..., variable, ...) in a case/is var (..., ...) - te = any(PatternExpr pe).getAChildExpr*() and - exists(AssignableDefinitions::LocalVariableDefinition lvd, Ssa::ExplicitDefinition def | - node2.(SsaDefinitionNode).getDefinition() = def and - def.getADefinition() = lvd and - lvd.getDeclaration() = item and - hasNodePath(x, node1, node2) - ) - ) - ) - or - FlowSummaryImpl::Private::Steps::summaryReadStep(node1, c, node2) - } - - /** - * Holds if values stored inside content `c` are cleared at node `n`. For example, - * any value stored inside `f` is cleared at the pre-update node associated with `x` - * in `x.f = newValue`. - */ - cached - predicate clearsContent(Node n, Content c) { - fieldOrPropertyStore(_, c, _, n.asExpr(), true) - or - fieldOrPropertyStore(_, c, _, n.(ObjectInitializerNode).getInitializer(), false) - or - FlowSummaryImpl::Private::Steps::summaryStoresIntoArg(c, n) and - not c instanceof ElementContent - or - FlowSummaryImpl::Private::Steps::summaryClearsContent(n, c) - or - exists(WithExpr we, ObjectInitializer oi, FieldOrProperty f | - oi = we.getInitializer() and - n.asExpr() = oi and - f = oi.getAMemberInitializer().getInitializedMember() and - c = f.getContent() - ) - } - - /** - * Holds if the node `n` is unreachable when the call context is `call`. - */ - cached - predicate isUnreachableInCall(Node n, DataFlowCall call) { - exists( - ExplicitParameterNode paramNode, Guard guard, ControlFlow::SuccessorTypes::BooleanSuccessor bs - | - viableConstantBooleanParamArg(paramNode, bs.getValue().booleanNot(), call) and - paramNode.getSsaDefinition().getARead() = guard and - guard.controlsBlock(n.getControlFlowNode().getBasicBlock(), bs, _) - ) - } - - pragma[nomagic] - private predicate commonSubTypeGeneral(DataFlowTypeOrUnifiable t1, DataFlowType t2) { + private predicate commonSubTypeGeneral(DataFlowTypeOrUnifiable t1, RelevantDataFlowType t2) { not t1 instanceof Gvn::TypeParameterGvnType and t1 = t2 or @@ -885,92 +747,53 @@ private module Cached { * `t2` are allowed to be type parameters. */ cached - predicate commonSubType(DataFlowType t1, DataFlowType t2) { commonSubTypeGeneral(t1, t2) } + predicate commonSubType(RelevantDataFlowType t1, RelevantDataFlowType t2) { + commonSubTypeGeneral(t1, t2) + } cached - predicate commonSubTypeUnifiableLeft(DataFlowType t1, DataFlowType t2) { + predicate commonSubTypeUnifiableLeft(RelevantDataFlowType t1, RelevantDataFlowType t2) { exists(Gvn::GvnType t | Gvn::unifiable(t1, t) and commonSubTypeGeneral(t, t2) ) } - - cached - predicate outRefReturnNode(Ssa::ExplicitDefinition def, OutRefReturnKind kind) { - exists(Parameter p | - def.isLiveOutRefParameterDefinition(p) and - kind.getPosition() = p.getPosition() - | - p.isOut() and kind instanceof OutReturnKind - or - p.isRef() and kind instanceof RefReturnKind - ) - } - - cached - predicate summaryOutNodeCached(DataFlowCall c, Node out, ReturnKind rk) { - FlowSummaryImpl::Private::summaryOutNode(c, out, rk) - } - - cached - predicate summaryArgumentNodeCached(DataFlowCall c, Node arg, int i) { - FlowSummaryImpl::Private::summaryArgumentNode(c, arg, i) - } - - cached - predicate summaryPostUpdateNodeCached(Node post, ParameterNode pre) { - FlowSummaryImpl::Private::summaryPostUpdateNode(post, pre) - } - - cached - predicate summaryReturnNodeCached(Node ret, ReturnKind rk) { - FlowSummaryImpl::Private::summaryReturnNode(ret, rk) and - not rk instanceof JumpReturnKind - } - - cached - predicate castNode(Node n) { - n.asExpr() instanceof Cast - or - n.(AssignableDefinitionNode).getDefinition() instanceof AssignableDefinitions::PatternDefinition - } - - /** Holds if `n` should be hidden from path explanations. */ - cached - predicate nodeIsHidden(Node n) { - exists(Ssa::Definition def | def = n.(SsaDefinitionNode).getDefinition() | - def instanceof Ssa::PhiNode - or - def instanceof Ssa::ImplicitEntryDefinition - or - def instanceof Ssa::ImplicitCallDefinition - ) - or - exists(Parameter p | - p = n.(ParameterNode).getParameter() and - not p.fromSource() - ) - or - n = TInstanceParameterNode(any(Callable c | not c.fromSource())) - or - n instanceof YieldReturnNode - or - n instanceof AsyncReturnNode - or - n instanceof ImplicitCapturedArgumentNode - or - n instanceof MallocNode - or - n instanceof SummaryNode - or - n instanceof ParamsArgumentNode - or - n.asExpr() = any(WithExpr we).getInitializer() - } } import Cached +/** Holds if `n` should be hidden from path explanations. */ +predicate nodeIsHidden(Node n) { + exists(Ssa::Definition def | def = n.(SsaDefinitionNode).getDefinition() | + def instanceof Ssa::PhiNode + or + def instanceof Ssa::ImplicitEntryDefinition + or + def instanceof Ssa::ImplicitCallDefinition + ) + or + exists(Parameter p | + p = n.(ParameterNode).getParameter() and + not p.fromSource() + ) + or + n = TInstanceParameterNode(any(Callable c | not c.fromSource())) + or + n instanceof YieldReturnNode + or + n instanceof AsyncReturnNode + or + n instanceof ImplicitCapturedArgumentNode + or + n instanceof MallocNode + or + n instanceof SummaryNode + or + n instanceof ParamsArgumentNode + or + n.asExpr() = any(WithExpr we).getInitializer() +} + /** An SSA definition, viewed as a node in a data flow graph. */ class SsaDefinitionNode extends NodeImpl, TSsaDefinitionNode { Ssa::Definition def; @@ -992,8 +815,6 @@ class SsaDefinitionNode extends NodeImpl, TSsaDefinitionNode { } abstract class ParameterNodeImpl extends NodeImpl { - abstract DotNet::Parameter getParameter(); - abstract predicate isParameterOf(DataFlowCallable c, int i); } @@ -1010,11 +831,9 @@ private module ParameterNodes { /** Gets the SSA definition corresponding to this parameter, if any. */ Ssa::ExplicitDefinition getSsaDefinition() { result.getADefinition().(AssignableDefinitions::ImplicitParameterDefinition).getParameter() = - this.getParameter() + parameter } - override DotNet::Parameter getParameter() { result = parameter } - override predicate isParameterOf(DataFlowCallable c, int i) { c.getParameter(i) = parameter } override DataFlowCallable getEnclosingCallableImpl() { result = parameter.getCallable() } @@ -1037,8 +856,6 @@ private module ParameterNodes { /** Gets the callable containing this implicit instance parameter. */ Callable getCallable() { result = callable } - override DotNet::Parameter getParameter() { none() } - override predicate isParameterOf(DataFlowCallable c, int pos) { callable = c and pos = -1 } override DataFlowCallable getEnclosingCallableImpl() { result = callable } @@ -1113,8 +930,6 @@ private module ParameterNodes { /** Gets the captured variable that this implicit parameter models. */ LocalScopeVariable getVariable() { result = def.getVariable() } - override DotNet::Parameter getParameter() { none() } - override predicate isParameterOf(DataFlowCallable c, int i) { i = getParameterPosition(def) and c = this.getEnclosingCallable() @@ -1125,13 +940,17 @@ private module ParameterNodes { import ParameterNodes /** A data-flow node that represents a call argument. */ -abstract class ArgumentNode extends Node { - /** Holds if this argument occurs at the given position in the given call. */ - cached - abstract predicate argumentOf(DataFlowCall call, int pos); +class ArgumentNode extends Node { + ArgumentNode() { this instanceof ArgumentNodeImpl } - /** Gets the call in which this node is an argument. */ - final DataFlowCall getCall() { this.argumentOf(result, _) } + /** Holds if this argument occurs at the given position in the given call. */ + final predicate argumentOf(DataFlowCall call, int pos) { + this.(ArgumentNodeImpl).argumentOf(call, pos) + } +} + +abstract private class ArgumentNodeImpl extends Node { + abstract predicate argumentOf(DataFlowCall call, int pos); } private module ArgumentNodes { @@ -1149,7 +968,7 @@ private module ArgumentNodes { } /** A data-flow node that represents an explicit call argument. */ - class ExplicitArgumentNode extends ArgumentNode { + class ExplicitArgumentNode extends ArgumentNodeImpl { ExplicitArgumentNode() { this.asExpr() instanceof Argument or @@ -1157,7 +976,6 @@ private module ArgumentNodes { } override predicate argumentOf(DataFlowCall call, int pos) { - Stages::DataFlowStage::forceCachingInSameStage() and exists(ArgumentConfiguration x, Expr c, Argument arg | arg = this.asExpr() and c = call.getExpr() and @@ -1189,7 +1007,8 @@ private module ArgumentNodes { * } } * ``` */ - class ImplicitCapturedArgumentNode extends ArgumentNode, NodeImpl, TImplicitCapturedArgumentNode { + class ImplicitCapturedArgumentNode extends ArgumentNodeImpl, NodeImpl, + TImplicitCapturedArgumentNode { private LocalScopeVariable v; private ControlFlow::Nodes::ElementNode cfn; @@ -1231,7 +1050,7 @@ private module ArgumentNodes { * A node that corresponds to the value of an object creation (`new C()`) before * the constructor has run. */ - class MallocNode extends ArgumentNode, NodeImpl, TMallocNode { + class MallocNode extends ArgumentNodeImpl, NodeImpl, TMallocNode { private ControlFlow::Nodes::ElementNode cfn; MallocNode() { this = TMallocNode(cfn) } @@ -1266,7 +1085,7 @@ private module ArgumentNodes { * and that argument is itself a compatible array, for example * `Foo(new[] { "a", "b", "c" })`. */ - class ParamsArgumentNode extends ArgumentNode, NodeImpl, TParamsArgumentNode { + class ParamsArgumentNode extends ArgumentNodeImpl, NodeImpl, TParamsArgumentNode { private ControlFlow::Node callCfn; ParamsArgumentNode() { this = TParamsArgumentNode(callCfn) } @@ -1291,15 +1110,11 @@ private module ArgumentNodes { override string toStringImpl() { result = "[implicit array creation] " + callCfn } } - private class SummaryArgumentNode extends SummaryNode, ArgumentNode { - private DataFlowCall c; - private int i; - - SummaryArgumentNode() { summaryArgumentNodeCached(c, this, i) } + private class SummaryArgumentNode extends SummaryNode, ArgumentNodeImpl { + SummaryArgumentNode() { FlowSummaryImpl::Private::summaryArgumentNode(_, this, _) } override predicate argumentOf(DataFlowCall call, int pos) { - call = c and - i = pos + FlowSummaryImpl::Private::summaryArgumentNode(call, this, pos) } } } @@ -1324,10 +1139,7 @@ private module ReturnNodes { ) } - override NormalReturnKind getKind() { - any(DotNet::Callable c).canReturn(this.getExpr()) and - exists(result) - } + override NormalReturnKind getKind() { exists(result) } } /** @@ -1337,7 +1149,16 @@ private module ReturnNodes { class OutRefReturnNode extends ReturnNode, SsaDefinitionNode { OutRefReturnKind kind; - OutRefReturnNode() { outRefReturnNode(this.getDefinition(), kind) } + OutRefReturnNode() { + exists(Parameter p | + this.getDefinition().isLiveOutRefParameterDefinition(p) and + kind.getPosition() = p.getPosition() + | + p.isOut() and kind instanceof OutReturnKind + or + p.isRef() and kind instanceof RefReturnKind + ) + } override ReturnKind getKind() { result = kind } } @@ -1434,7 +1255,10 @@ private module ReturnNodes { private class SummaryReturnNode extends SummaryNode, ReturnNode { private ReturnKind rk; - SummaryReturnNode() { summaryReturnNodeCached(this, rk) } + SummaryReturnNode() { + FlowSummaryImpl::Private::summaryReturnNode(this, rk) and + not rk instanceof JumpReturnKind + } override ReturnKind getKind() { result = rk } } @@ -1445,7 +1269,6 @@ import ReturnNodes /** A data-flow node that represents the output of a call. */ abstract class OutNode extends Node { /** Gets the underlying call, where this node is a corresponding output of kind `kind`. */ - cached abstract DataFlowCall getCall(ReturnKind kind); } @@ -1473,7 +1296,6 @@ private module OutNodes { } override DataFlowCall getCall(ReturnKind kind) { - Stages::DataFlowStage::forceCachingInSameStage() and result = call and ( kind instanceof NormalReturnKind and @@ -1549,14 +1371,10 @@ private module OutNodes { } private class SummaryOutNode extends SummaryNode, OutNode { - private DataFlowCall c; - private ReturnKind rk; - - SummaryOutNode() { summaryOutNodeCached(c, this, rk) } + SummaryOutNode() { FlowSummaryImpl::Private::summaryOutNode(_, this, _) } override DataFlowCall getCall(ReturnKind kind) { - result = c and - kind = rk + FlowSummaryImpl::Private::summaryOutNode(result, this, kind) } } } @@ -1639,7 +1457,29 @@ private class FieldOrPropertyRead extends FieldOrPropertyAccess, AssignableRead } } -predicate jumpStep = jumpStepImpl/2; +/** + * Holds if `pred` can flow to `succ`, by jumping from one callable to + * another. Additional steps specified by the configuration are *not* + * taken into account. + */ +predicate jumpStep(Node pred, Node succ) { + pred.(NonLocalJumpNode).getAJumpSuccessor(true) = succ + or + exists(FieldOrProperty fl, FieldOrPropertyRead flr | + fl.isStatic() and + fl.isFieldLike() and + fl.getAnAssignedValue() = pred.asExpr() and + fl.getAnAccess() = flr and + flr = succ.asExpr() and + flr.hasNonlocalValue() + ) + or + exists(JumpReturnKind jrk, DataFlowCall call | + FlowSummaryImpl::Private::summaryReturnNode(pred, jrk) and + viableCallable(call) = jrk.getTarget() and + succ = getAnOutNode(call, jrk.getTargetReturnKind()) + ) +} private class StoreStepConfiguration extends ControlFlowReachabilityConfiguration { StoreStepConfiguration() { this = "StoreStepConfiguration" } @@ -1660,7 +1500,46 @@ private class StoreStepConfiguration extends ControlFlowReachabilityConfiguratio } } -predicate storeStep = storeStepImpl/3; +pragma[nomagic] +private PropertyContent getResultContent() { + result.getProperty() = any(SystemThreadingTasksTaskTClass c_).getResultProperty() +} + +/** + * Holds if data can flow from `node1` to `node2` via an assignment to + * content `c`. + */ +predicate storeStep(Node node1, Content c, Node node2) { + exists(StoreStepConfiguration x, ExprNode node, boolean postUpdate | + hasNodePath(x, node1, node) and + if postUpdate = true then node = node2.(PostUpdateNode).getPreUpdateNode() else node = node2 + | + fieldOrPropertyStore(_, c, node1.asExpr(), node.getExpr(), postUpdate) + or + arrayStore(_, node1.asExpr(), node.getExpr(), postUpdate) and c instanceof ElementContent + ) + or + exists(StoreStepConfiguration x, Expr arg, ControlFlow::Node callCfn | + x.hasExprPath(arg, node1.(ExprNode).getControlFlowNode(), _, callCfn) and + node2 = TParamsArgumentNode(callCfn) and + isParamsArg(_, arg, _) and + c instanceof ElementContent + ) + or + exists(Expr e | + e = node1.asExpr() and + node2.(YieldReturnNode).getYieldReturnStmt().getExpr() = e and + c instanceof ElementContent + ) + or + exists(Expr e | + e = node1.asExpr() and + node2.(AsyncReturnNode).getExpr() = e and + c = getResultContent() + ) + or + FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, c, node2) +} private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration { ReadStepConfiguration() { this = "ReadStepConfiguration" } @@ -1727,7 +1606,99 @@ private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration } } -predicate readStep = readStepImpl/3; +/** + * Holds if data can flow from `node1` to `node2` via a read of content `c`. + */ +predicate readStep(Node node1, Content c, Node node2) { + exists(ReadStepConfiguration x | + hasNodePath(x, node1, node2) and + fieldOrPropertyRead(node1.asExpr(), c, node2.asExpr()) + or + hasNodePath(x, node1, node2) and + arrayRead(node1.asExpr(), node2.asExpr()) and + c instanceof ElementContent + or + exists(ForeachStmt fs, Ssa::ExplicitDefinition def | + x.hasDefPath(fs.getIterableExpr(), node1.getControlFlowNode(), def.getADefinition(), + def.getControlFlowNode()) and + node2.(SsaDefinitionNode).getDefinition() = def and + c instanceof ElementContent + ) + or + hasNodePath(x, node1, node2) and + node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and + c = getResultContent() + or + // node1 = (..., node2, ...) + // node1.ItemX flows to node2 + exists(TupleExpr te, int i, Expr item | + te = node1.asExpr() and + not te.isConstruction() and + c.(FieldContent).getField() = te.getType().(TupleType).getElement(i).getUnboundDeclaration() and + // node1 = (..., item, ...) + te.getArgument(i) = item + | + // item = (..., ..., ...) in node1 = (..., (..., ..., ...), ...) + node2.asExpr().(TupleExpr) = item and + hasNodePath(x, node1, node2) + or + // item = variable in node1 = (..., variable, ...) + exists(AssignableDefinitions::TupleAssignmentDefinition tad, Ssa::ExplicitDefinition def | + node2.(SsaDefinitionNode).getDefinition() = def and + def.getADefinition() = tad and + tad.getLeaf() = item and + hasNodePath(x, node1, node2) + ) + or + // item = variable in node1 = (..., variable, ...) in a case/is var (..., ...) + te = any(PatternExpr pe).getAChildExpr*() and + exists(AssignableDefinitions::LocalVariableDefinition lvd, Ssa::ExplicitDefinition def | + node2.(SsaDefinitionNode).getDefinition() = def and + def.getADefinition() = lvd and + lvd.getDeclaration() = item and + hasNodePath(x, node1, node2) + ) + ) + ) + or + FlowSummaryImpl::Private::Steps::summaryReadStep(node1, c, node2) +} + +/** + * Holds if values stored inside content `c` are cleared at node `n`. For example, + * any value stored inside `f` is cleared at the pre-update node associated with `x` + * in `x.f = newValue`. + */ +predicate clearsContent(Node n, Content c) { + fieldOrPropertyStore(_, c, _, n.asExpr(), true) + or + fieldOrPropertyStore(_, c, _, n.(ObjectInitializerNode).getInitializer(), false) + or + FlowSummaryImpl::Private::Steps::summaryStoresIntoArg(c, n) and + not c instanceof ElementContent + or + FlowSummaryImpl::Private::Steps::summaryClearsContent(n, c) + or + exists(WithExpr we, ObjectInitializer oi, FieldOrProperty f | + oi = we.getInitializer() and + n.asExpr() = oi and + f = oi.getAMemberInitializer().getInitializedMember() and + c = f.getContent() + ) +} + +/** + * Holds if the node `n` is unreachable when the call context is `call`. + */ +predicate isUnreachableInCall(Node n, DataFlowCall call) { + exists( + ExplicitParameterNode paramNode, Guard guard, ControlFlow::SuccessorTypes::BooleanSuccessor bs + | + viableConstantBooleanParamArg(paramNode, bs.getValue().booleanNot(), call) and + paramNode.getSsaDefinition().getARead() = guard and + guard.controlsBlock(n.getControlFlowNode().getBasicBlock(), bs, _) + ) +} /** * An entity used to represent the type of data-flow node. Two nodes will have @@ -1738,13 +1709,13 @@ predicate readStep = readStepImpl/3; * `DataFlowType`, while `Func` and `Func` are not, because * `string` is not a type parameter. */ -class DataFlowType extends Gvn::GvnType { - pragma[nomagic] - DataFlowType() { this = any(NodeImpl n).getDataFlowType() } -} +class DataFlowType = Gvn::GvnType; /** Gets the type of `n` used for type pruning. */ -DataFlowType getNodeType(NodeImpl n) { result = n.getDataFlowType() } +pragma[inline] +Gvn::GvnType getNodeType(NodeImpl n) { + pragma[only_bind_into](result) = pragma[only_bind_out](n).getDataFlowType() +} /** Gets a string representation of a `DataFlowType`. */ string ppReprType(DataFlowType t) { result = t.toString() } @@ -1819,7 +1790,8 @@ private module PostUpdateNodes { * Such a node acts as both a post-update node for the `MallocNode`, as well as * a pre-update node for the `ObjectCreationNode`. */ - class ObjectInitializerNode extends PostUpdateNode, NodeImpl, ArgumentNode, TObjectInitializerNode { + class ObjectInitializerNode extends PostUpdateNode, NodeImpl, ArgumentNodeImpl, + TObjectInitializerNode { private ObjectCreation oc; private ControlFlow::Nodes::ElementNode cfn; @@ -1869,11 +1841,11 @@ private module PostUpdateNodes { } private class SummaryPostUpdateNode extends SummaryNode, PostUpdateNode { - private Node pre; + SummaryPostUpdateNode() { FlowSummaryImpl::Private::summaryPostUpdateNode(this, _) } - SummaryPostUpdateNode() { summaryPostUpdateNodeCached(this, pre) } - - override Node getPreUpdateNode() { result = pre } + override Node getPreUpdateNode() { + FlowSummaryImpl::Private::summaryPostUpdateNode(this, result) + } } } @@ -1881,7 +1853,12 @@ private import PostUpdateNodes /** A node that performs a type cast. */ class CastNode extends Node { - CastNode() { castNode(this) } + CastNode() { + this.asExpr() instanceof Cast + or + this.(AssignableDefinitionNode).getDefinition() instanceof + AssignableDefinitions::PatternDefinition + } } class DataFlowExpr = DotNet::Expr; @@ -1980,7 +1957,7 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { kind = TMkUnit() } -/** Extra data-flow steps needed for lamba flow analysis. */ +/** Extra data-flow steps needed for lambda flow analysis. */ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { exists(Ssa::Definition def | LocalFlow::localSsaFlowStep(def, nodeFrom, nodeTo) and diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll index 40853f7316a..bfc2f5469d0 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll @@ -3,7 +3,6 @@ private import cil private import dotnet private import DataFlowDispatch private import DataFlowPrivate -private import semmle.code.csharp.Caching private import semmle.code.csharp.controlflow.Guards private import semmle.code.csharp.Unification @@ -38,38 +37,21 @@ class Node extends TNode { } /** Gets the type of this node. */ - cached - final DotNet::Type getType() { - Stages::DataFlowStage::forceCachingInSameStage() and result = this.(NodeImpl).getTypeImpl() - } + final DotNet::Type getType() { result = this.(NodeImpl).getTypeImpl() } /** Gets the enclosing callable of this node. */ - cached final DataFlowCallable getEnclosingCallable() { - Stages::DataFlowStage::forceCachingInSameStage() and - result = unique(DataFlowCallable c | c = this.(NodeImpl).getEnclosingCallableImpl() | c) + result = this.(NodeImpl).getEnclosingCallableImpl() } /** Gets the control flow node corresponding to this node, if any. */ - cached - final ControlFlow::Node getControlFlowNode() { - Stages::DataFlowStage::forceCachingInSameStage() and - result = unique(ControlFlow::Node n | n = this.(NodeImpl).getControlFlowNodeImpl() | n) - } + final ControlFlow::Node getControlFlowNode() { result = this.(NodeImpl).getControlFlowNodeImpl() } /** Gets a textual representation of this node. */ - cached - final string toString() { - Stages::DataFlowStage::forceCachingInSameStage() and - result = this.(NodeImpl).toStringImpl() - } + final string toString() { result = this.(NodeImpl).toStringImpl() } /** Gets the location of this node. */ - cached - final Location getLocation() { - Stages::DataFlowStage::forceCachingInSameStage() and - result = this.(NodeImpl).getLocationImpl() - } + final Location getLocation() { result = this.(NodeImpl).getLocationImpl() } /** * Holds if this element is at the specified location. @@ -81,10 +63,12 @@ class Node extends TNode { predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { - getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) } } +private class TExprNode_ = TExprNode or TCilExprNode; + /** * An expression, viewed as a node in a data flow graph. * @@ -92,9 +76,7 @@ class Node extends TNode { * to multiple `ExprNode`s, just like it may correspond to multiple * `ControlFlow::Node`s. */ -class ExprNode extends Node { - ExprNode() { this = TExprNode(_) or this = TCilExprNode(_) } - +class ExprNode extends Node, TExprNode_ { /** Gets the expression corresponding to this node. */ DotNet::Expr getExpr() { result = this.getExprAtNode(_) @@ -117,18 +99,20 @@ class ExprNode extends Node { * flow graph. */ class ParameterNode extends Node { - private ParameterNodeImpl p; - - ParameterNode() { this = p } + ParameterNode() { this instanceof ParameterNodeImpl } /** Gets the parameter corresponding to this node, if any. */ - DotNet::Parameter getParameter() { result = p.getParameter() } + DotNet::Parameter getParameter() { + exists(DataFlowCallable c, int i | this.isParameterOf(c, i) and result = c.getParameter(i)) + } /** * Holds if this node is the parameter of callable `c` at the specified * (zero-based) position. */ - predicate isParameterOf(DataFlowCallable c, int i) { p.isParameterOf(c, i) } + predicate isParameterOf(DataFlowCallable c, int i) { + this.(ParameterNodeImpl).isParameterOf(c, i) + } } /** A definition, viewed as a node in a data flow graph. */ @@ -166,6 +150,7 @@ predicate localFlowStep = localFlowStepImpl/2; * Holds if data flows from `source` to `sink` in zero or more local * (intra-procedural) steps. */ +pragma[inline] predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) } /** diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll index 3c8c94c913c..ddafb23274b 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll @@ -9,6 +9,7 @@ private import FlowSummaryImplSpecific private import DataFlowImplSpecific::Private private import DataFlowImplSpecific::Public +private import DataFlowImplCommon as DataFlowImplCommon /** Provides classes and predicates for defining flow summaries. */ module Public { @@ -178,7 +179,6 @@ module Public { */ module Private { private import Public - private import DataFlowImplCommon as DataFlowImplCommon newtype TSummaryComponent = TContentSummaryComponent(Content c) or @@ -446,7 +446,19 @@ module Private { summary(c, inputContents, outputContents, preservesValue) and pred = summaryNodeInputState(c, inputContents) and succ = summaryNodeOutputState(c, outputContents) + | + preservesValue = true + or + preservesValue = false and not summary(c, inputContents, outputContents, true) ) + or + // If flow through a method updates a parameter from some input A, and that + // parameter also is returned through B, then we'd like a combined flow from A + // to B as well. As an example, this simplifies modeling of fluent methods: + // for `StringBuilder.append(x)` with a specified value flow from qualifier to + // return value and taint flow from argument 0 to the qualifier, then this + // allows us to infer taint flow from argument 0 to the return value. + summaryPostUpdateNode(pred, succ) and preservesValue = true } /** @@ -568,88 +580,227 @@ module Private { * summaries into a `SummarizedCallable`s. */ module External { - /** - * Provides a means of translating an externally (e.g., CSV) defined flow - * summary into a `SummarizedCallable`. - */ - abstract class ExternalSummaryCompilation extends string { - bindingset[this] - ExternalSummaryCompilation() { any() } - - /** Holds if this flow summary is for callable `c`. */ - abstract predicate callable(DataFlowCallable c, boolean preservesValue); - - /** Holds if the `i`th input component is `c`. */ - abstract predicate input(int i, SummaryComponent c); - - /** Holds if the `i`th output component is `c`. */ - abstract predicate output(int i, SummaryComponent c); - - /** - * Holds if the input components starting from index `i` translate into `suffix`. - */ - final predicate translateInput(int i, SummaryComponentStack suffix) { - exists(SummaryComponent comp | this.input(i, comp) | - i = max(int j | this.input(j, _)) and - suffix = TSingletonSummaryComponentStack(comp) - or - exists(TSummaryComponent head, SummaryComponentStack tail | - this.translateInputCons(i, head, tail) and - suffix = TConsSummaryComponentStack(head, tail) - ) - ) - } - - final predicate translateInputCons(int i, SummaryComponent head, SummaryComponentStack tail) { - this.input(i, head) and - this.translateInput(i + 1, tail) - } - - /** - * Holds if the output components starting from index `i` translate into `suffix`. - */ - predicate translateOutput(int i, SummaryComponentStack suffix) { - exists(SummaryComponent comp | this.output(i, comp) | - i = max(int j | this.output(j, _)) and - suffix = TSingletonSummaryComponentStack(comp) - or - exists(TSummaryComponent head, SummaryComponentStack tail | - this.translateOutputCons(i, head, tail) and - suffix = TConsSummaryComponentStack(head, tail) - ) - ) - } - - predicate translateOutputCons(int i, SummaryComponent head, SummaryComponentStack tail) { - this.output(i, head) and - this.translateOutput(i + 1, tail) - } + /** Holds if `spec` is a relevant external specification. */ + private predicate relevantSpec(string spec) { + summaryElement(_, spec, _, _) or + summaryElement(_, _, spec, _) or + sourceElement(_, spec, _) or + sinkElement(_, spec, _) } - private class ExternalRequiredSummaryComponentStack extends RequiredSummaryComponentStack { - private SummaryComponent head; + /** Holds if the `n`th component of specification `s` is `c`. */ + predicate specSplit(string s, string c, int n) { relevantSpec(s) and s.splitAt(" of ", n) = c } - ExternalRequiredSummaryComponentStack() { - any(ExternalSummaryCompilation s).translateInputCons(_, head, this) or - any(ExternalSummaryCompilation s).translateOutputCons(_, head, this) - } + /** Holds if specification `s` has length `len`. */ + predicate specLength(string s, int len) { len = 1 + max(int n | specSplit(s, _, n)) } - override predicate required(SummaryComponent c) { c = head } + /** Gets the last component of specification `s`. */ + string specLast(string s) { + exists(int len | + specLength(s, len) and + specSplit(s, result, len - 1) + ) } - class ExternalSummarizedCallableAdaptor extends SummarizedCallable { - ExternalSummarizedCallableAdaptor() { any(ExternalSummaryCompilation s).callable(this, _) } + /** Holds if specification component `c` parses as parameter `n`. */ + predicate parseParam(string c, int n) { + specSplit(_, c, _) and + ( + c.regexpCapture("Parameter\\[([-0-9]+)\\]", 1).toInt() = n + or + exists(int n1, int n2 | + c.regexpCapture("Parameter\\[([-0-9]+)\\.\\.([0-9]+)\\]", 1).toInt() = n1 and + c.regexpCapture("Parameter\\[([-0-9]+)\\.\\.([0-9]+)\\]", 2).toInt() = n2 and + n = [n1 .. n2] + ) + ) + } + + /** Holds if specification component `c` parses as argument `n`. */ + predicate parseArg(string c, int n) { + specSplit(_, c, _) and + ( + c.regexpCapture("Argument\\[([-0-9]+)\\]", 1).toInt() = n + or + exists(int n1, int n2 | + c.regexpCapture("Argument\\[([-0-9]+)\\.\\.([0-9]+)\\]", 1).toInt() = n1 and + c.regexpCapture("Argument\\[([-0-9]+)\\.\\.([0-9]+)\\]", 2).toInt() = n2 and + n = [n1 .. n2] + ) + ) + } + + private SummaryComponent interpretComponent(string c) { + specSplit(_, c, _) and + ( + exists(int pos | parseArg(c, pos) and result = SummaryComponent::argument(pos)) + or + exists(int pos | parseParam(c, pos) and result = SummaryComponent::parameter(pos)) + or + c = "ReturnValue" and result = SummaryComponent::return(getReturnValueKind()) + or + result = interpretComponentSpecific(c) + ) + } + + private predicate interpretSpec(string spec, int idx, SummaryComponentStack stack) { + exists(string c | + relevantSpec(spec) and + specLength(spec, idx + 1) and + specSplit(spec, c, idx) and + stack = SummaryComponentStack::singleton(interpretComponent(c)) + ) + or + exists(SummaryComponent head, SummaryComponentStack tail | + interpretSpec(spec, idx, head, tail) and + stack = SummaryComponentStack::push(head, tail) + ) + } + + private predicate interpretSpec( + string output, int idx, SummaryComponent head, SummaryComponentStack tail + ) { + exists(string c | + interpretSpec(output, idx + 1, tail) and + specSplit(output, c, idx) and + head = interpretComponent(c) + ) + } + + private class MkStack extends RequiredSummaryComponentStack { + MkStack() { interpretSpec(_, _, _, this) } + + override predicate required(SummaryComponent c) { interpretSpec(_, _, c, this) } + } + + private class SummarizedCallableExternal extends SummarizedCallable { + SummarizedCallableExternal() { summaryElement(this, _, _, _) } override predicate propagatesFlow( SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue ) { - exists(ExternalSummaryCompilation s | - s.callable(this, preservesValue) and - s.translateInput(0, input) and - s.translateOutput(0, output) + exists(string inSpec, string outSpec, string kind | + summaryElement(this, inSpec, outSpec, kind) and + interpretSpec(inSpec, 0, input) and + interpretSpec(outSpec, 0, output) + | + kind = "value" and preservesValue = true + or + kind = "taint" and preservesValue = false ) } } + + /** Holds if component `c` of specification `spec` cannot be parsed. */ + predicate invalidSpecComponent(string spec, string c) { + specSplit(spec, c, _) and + not exists(interpretComponent(c)) + } + + private predicate inputNeedsReference(string c) { + c = "Argument" or + parseArg(c, _) + } + + private predicate outputNeedsReference(string c) { + c = "Argument" or + parseArg(c, _) or + c = "ReturnValue" + } + + private predicate sourceElementRef(InterpretNode ref, string output, string kind) { + exists(SourceOrSinkElement e | + sourceElement(e, output, kind) and + if outputNeedsReference(specLast(output)) + then e = ref.getCallTarget() + else e = ref.asElement() + ) + } + + private predicate sinkElementRef(InterpretNode ref, string input, string kind) { + exists(SourceOrSinkElement e | + sinkElement(e, input, kind) and + if inputNeedsReference(specLast(input)) + then e = ref.getCallTarget() + else e = ref.asElement() + ) + } + + private predicate interpretOutput(string output, int idx, InterpretNode ref, InterpretNode node) { + sourceElementRef(ref, output, _) and + specLength(output, idx) and + node = ref + or + exists(InterpretNode mid, string c | + interpretOutput(output, idx + 1, ref, mid) and + specSplit(output, c, idx) + | + exists(int pos | + node.asNode() + .(PostUpdateNode) + .getPreUpdateNode() + .(ArgumentNode) + .argumentOf(mid.asCall(), pos) + | + c = "Argument" or parseArg(c, pos) + ) + or + exists(int pos | node.asNode().(ParameterNode).isParameterOf(mid.asCallable(), pos) | + c = "Parameter" or parseParam(c, pos) + ) + or + c = "ReturnValue" and + node.asNode() = getAnOutNode(mid.asCall(), getReturnValueKind()) + or + interpretOutputSpecific(c, mid, node) + ) + } + + private predicate interpretInput(string input, int idx, InterpretNode ref, InterpretNode node) { + sinkElementRef(ref, input, _) and + specLength(input, idx) and + node = ref + or + exists(InterpretNode mid, string c | + interpretInput(input, idx + 1, ref, mid) and + specSplit(input, c, idx) + | + exists(int pos | node.asNode().(ArgumentNode).argumentOf(mid.asCall(), pos) | + c = "Argument" or parseArg(c, pos) + ) + or + exists(ReturnNode ret | + c = "ReturnValue" and + ret = node.asNode() and + ret.getKind() = getReturnValueKind() and + mid.asCallable() = DataFlowImplCommon::getNodeEnclosingCallable(ret) + ) + or + interpretInputSpecific(c, mid, node) + ) + } + + /** + * Holds if `node` is specified as a source with the given kind in a CSV flow + * model. + */ + predicate isSourceNode(InterpretNode node, string kind) { + exists(InterpretNode ref, string output | + sourceElementRef(ref, output, kind) and + interpretOutput(output, 0, ref, node) + ) + } + + /** + * Holds if `node` is specified as a sink with the given kind in a CSV flow + * model. + */ + predicate isSinkNode(InterpretNode node, string kind) { + exists(InterpretNode ref, string input | + sinkElementRef(ref, input, kind) and + interpretInput(input, 0, ref, node) + ) + } } /** Provides a query predicate for outputting a set of relevant flow summaries. */ diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll index 01e3a2e1633..efab6eb9e3c 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll @@ -77,3 +77,103 @@ DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) { result = Gvn::getGlobalValueNumber(dt.getDelegateType().getReturnType()) ) } + +/** + * Holds if an external flow summary exists for `c` with input specification + * `input`, output specification `output`, and kind `kind`. + */ +predicate summaryElement(DataFlowCallable c, string input, string output, string kind) { none() } + +/** Gets the summary component for specification component `c`, if any. */ +bindingset[c] +SummaryComponent interpretComponentSpecific(string c) { + c = "Element" and result = SummaryComponent::content(any(ElementContent ec)) + or + exists(Field f | + c.regexpCapture("Field\\[(.+)\\]", 1) = f.getQualifiedName() and + result = SummaryComponent::content(any(FieldContent fc | fc.getField() = f)) + ) + or + exists(Property p | + c.regexpCapture("Property\\[(.+)\\]", 1) = p.getQualifiedName() and + result = SummaryComponent::content(any(PropertyContent pc | pc.getProperty() = p)) + ) +} + +class SourceOrSinkElement = Element; + +/** + * Holds if an external source specification exists for `e` with output specification + * `output` and kind `kind`. + */ +predicate sourceElement(Element e, string output, string kind) { none() } + +/** + * Holds if an external sink specification exists for `n` with input specification + * `input` and kind `kind`. + */ +predicate sinkElement(Element e, string input, string kind) { none() } + +/** Gets the return kind corresponding to specification `"ReturnValue"`. */ +NormalReturnKind getReturnValueKind() { any() } + +private newtype TInterpretNode = + TElement_(Element n) or + TNode_(Node n) or + TDataFlowCall_(DataFlowCall c) + +/** An entity used to interpret a source/sink specification. */ +class InterpretNode extends TInterpretNode { + /** Gets the element that this node corresponds to, if any. */ + SourceOrSinkElement asElement() { this = TElement_(result) } + + /** Gets the data-flow node that this node corresponds to, if any. */ + Node asNode() { this = TNode_(result) } + + /** Gets the call that this node corresponds to, if any. */ + DataFlowCall asCall() { this = TDataFlowCall_(result) } + + /** Gets the callable that this node corresponds to, if any. */ + DataFlowCallable asCallable() { result = this.asElement() } + + /** Gets the target of this call, if any. */ + Callable getCallTarget() { result = this.asCall().getARuntimeTarget() } + + /** Gets a textual representation of this node. */ + string toString() { + result = this.asElement().toString() + or + result = this.asNode().toString() + or + result = this.asCall().toString() + } + + /** Gets the location of this node. */ + Location getLocation() { + result = this.asElement().getLocation() + or + result = this.asNode().getLocation() + or + result = this.asCall().getLocation() + } +} + +/** Provides additional sink specification logic required for attributes. */ +predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) { + exists(Node n | n = node.asNode() | + (c = "Parameter" or c = "") and + n.asParameter() = mid.asElement() + or + c = "" and + n.asExpr().(AssignableRead).getTarget().getUnboundDeclaration() = mid.asElement() + ) +} + +/** Provides additional sink specification logic required for attributes. */ +predicate interpretInputSpecific(string c, InterpretNode mid, InterpretNode n) { + c = "" and + exists(Assignable a | + n.asNode().asExpr() = a.getAnAssignedValue() and + a.getUnboundDeclaration() = mid.asElement() + ) +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImpl.qll index 41da2b8f0b5..63d6c902fed 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImpl.qll @@ -268,56 +268,146 @@ private module CallGraph { ) } + /** + * A simple flow step that does not take flow through fields or flow out + * of callables into account. + */ + pragma[nomagic] private predicate delegateFlowStep(Expr pred, Expr succ) { Steps::stepClosed(pred, succ) or - exists(Call call, Callable callable | - callable.getUnboundDeclaration().canReturn(pred) and - call = succ - | - callable = call.getTarget() or - callable = call.getTarget().(Method).getAnOverrider+() or - callable = call.getTarget().(Method).getAnUltimateImplementor() or - callable = getARuntimeDelegateTarget(call, false) - ) - or pred = succ.(DelegateCreation).getArgument() or - exists(AssignableDefinition def, Assignable a | - a instanceof Field or - a instanceof Property - | - a = def.getTarget() and - succ.(AssignableRead) = a.getAnAccess() and - pred = def.getSource() - ) - or exists(AddEventExpr ae | succ.(EventAccess).getTarget() = ae.getTarget() | pred = ae.getRValue() ) } - private predicate reachesDelegateCall(Expr e) { - delegateCall(_, e, _) + private predicate delegateCreationReaches(Callable c, Expr e) { + delegateCreation(e, c, _) or - exists(Expr mid | reachesDelegateCall(mid) | delegateFlowStep(e, mid)) + exists(Expr mid | + delegateCreationReaches(c, mid) and + delegateFlowStep(mid, e) + ) } - pragma[nomagic] - private predicate delegateFlowStepReaches(Expr pred, Expr succ) { - delegateFlowStep(pred, succ) and - reachesDelegateCall(succ) + private predicate reachesDelegateCall(Expr e, Call c, boolean libraryDelegateCall) { + delegateCall(c, e, libraryDelegateCall) + or + exists(Expr mid | + reachesDelegateCall(mid, c, libraryDelegateCall) and + delegateFlowStep(e, mid) + ) } - private Expr delegateCallSource(Callable c) { - delegateCreation(result, c, _) - or - delegateFlowStepReaches(delegateCallSource(c), result) + /** + * A "busy" flow element, that is, a node in the data-flow graph that typically + * has a large fan-in or a large fan-out (or both). + * + * For such busy elements, we do not track flow directly from all delegate + * creations, but instead we first perform a flow analysis between busy elements, + * and then only in the end join up with delegate creations and delegate calls. + */ + abstract private class BusyFlowElement extends Element { + pragma[nomagic] + abstract Expr getAnInput(); + + pragma[nomagic] + abstract Expr getAnOutput(); + + /** Holds if this element can be reached from expression `e`. */ + pragma[nomagic] + private predicate exprReaches(Expr e) { + this.reachesCall(_) and + e = this.getAnInput() + or + exists(Expr mid | + this.exprReaches(mid) and + delegateFlowStep(e, mid) + ) + } + + /** + * Holds if this element can reach a delegate call `c` directly without + * passing through another busy element. + */ + pragma[nomagic] + predicate delegateCall(Call c, boolean libraryDelegateCall) { + reachesDelegateCall(this.getAnOutput(), c, libraryDelegateCall) + } + + pragma[nomagic] + private BusyFlowElement getASuccessor() { result.exprReaches(this.getAnOutput()) } + + /** + * Holds if this element reaches another busy element `other`, + * which can reach a delegate call directly without passing + * through another busy element. + */ + pragma[nomagic] + private predicate reachesCall(BusyFlowElement other) { + this = other and + other.delegateCall(_, _) + or + this.getASuccessor().reachesCall(other) + } + + /** Holds if this element is reached by a delegate creation for `c`. */ + pragma[nomagic] + predicate isReachedBy(Callable c) { + exists(BusyFlowElement pred | + pred.reachesCall(this) and + delegateCreationReaches(c, pred.getAnInput()) + ) + } + } + + private class TFieldOrProperty = @field or @property; + + private class FieldOrPropertyFlow extends BusyFlowElement, Assignable, TFieldOrProperty { + final override Expr getAnInput() { + exists(Assignable target | + target = this.getUnboundDeclaration() and + result = target.getAnAssignedValue() + ) + } + + final override AssignableRead getAnOutput() { + exists(Assignable target | + target = this.getUnboundDeclaration() and + result = target.getAnAccess() + ) + } + } + + private class CallOutputFlow extends BusyFlowElement, Callable { + final override Expr getAnInput() { this.canReturn(result) } + + final override Call getAnOutput() { + exists(Callable target | this = target.getUnboundDeclaration() | + target = result.getTarget() or + target = result.getTarget().(Method).getAnOverrider+() or + target = result.getTarget().(Method).getAnUltimateImplementor() or + target = getARuntimeDelegateTarget(result, false) + ) + } } /** Gets a run-time target for the delegate call `c`. */ + pragma[nomagic] Callable getARuntimeDelegateTarget(Call c, boolean libraryDelegateCall) { - delegateCall(c, delegateCallSource(result), libraryDelegateCall) + // directly resolvable without going through a "busy" element + exists(Expr e | + delegateCreationReaches(result, e) and + delegateCall(c, e, libraryDelegateCall) + ) + or + // resolvable by going through one or more "busy" elements + exists(BusyFlowElement busy | + busy.isReachedBy(result) and + busy.delegateCall(c, libraryDelegateCall) + ) } } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll index f828419a440..f37a4f2d074 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll @@ -316,6 +316,15 @@ private module SsaDefReaches { ) } + /** + * Holds if the reference to `def` at index `i` in basic block `bb` is the + * last reference to `v` inside `bb`. + */ + pragma[noinline] + predicate lastSsaRef(Definition def, SourceVariable v, BasicBlock bb, int i) { + ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) + } + predicate defOccursInBlock(Definition def, BasicBlock bb, SourceVariable v) { exists(ssaDefRank(def, v, bb, _, _)) } @@ -351,8 +360,7 @@ private module SsaDefReaches { */ predicate defAdjacentRead(Definition def, BasicBlock bb1, BasicBlock bb2, int i2) { varBlockReaches(def, bb1, bb2) and - ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 and - variableRead(bb2, i2, _, _) + ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 } } @@ -434,15 +442,22 @@ predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2 bb2 = bb1 ) or - exists(SourceVariable v | ssaDefRank(def, v, bb1, i1, _) = maxSsaRefRank(bb1, v)) and + lastSsaRef(def, _, bb1, i1) and defAdjacentRead(def, bb1, bb2, i2) } +pragma[noinline] +private predicate adjacentDefRead( + Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v +) { + adjacentDefRead(def, bb1, i1, bb2, i2) and + v = def.getSourceVariable() +} + private predicate adjacentDefReachesRead( Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2 ) { - adjacentDefRead(def, bb1, i1, bb2, i2) and - exists(SourceVariable v | v = def.getSourceVariable() | + exists(SourceVariable v | adjacentDefRead(def, bb1, i1, bb2, i2, v) | ssaRef(bb1, i1, v, SsaDef()) or variableRead(bb1, i1, v, true) @@ -475,17 +490,19 @@ predicate adjacentDefNoUncertainReads(Definition def, BasicBlock bb1, int i1, Ba */ pragma[nomagic] predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) { - exists(int rnk, SourceVariable v, int j | rnk = ssaDefRank(def, v, bb, i, _) | + exists(SourceVariable v | // Next reference to `v` inside `bb` is a write - next.definesAt(v, bb, j) and - rnk + 1 = ssaRefRank(bb, j, v, SsaDef()) + exists(int rnk, int j | + rnk = ssaDefRank(def, v, bb, i, _) and + next.definesAt(v, bb, j) and + rnk + 1 = ssaRefRank(bb, j, v, SsaDef()) + ) or // Can reach a write using one or more steps - rnk = maxSsaRefRank(bb, v) and + lastSsaRef(def, v, bb, i) and exists(BasicBlock bb2 | varBlockReaches(def, bb, bb2) and - next.definesAt(v, bb2, j) and - 1 = ssaRefRank(bb2, j, v, SsaDef()) + 1 = ssaDefRank(next, v, bb2, _, SsaDef()) ) ) } @@ -539,7 +556,8 @@ pragma[nomagic] predicate lastRef(Definition def, BasicBlock bb, int i) { lastRefRedef(def, bb, i, _) or - exists(SourceVariable v | ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) | + lastSsaRef(def, _, bb, i) and + ( // Can reach exit directly bb instanceof ExitBasicBlock or diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll index 72525d3234a..462838abcd1 100755 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll @@ -1,6 +1,5 @@ private import csharp private import TaintTrackingPublic -private import DataFlowImplCommon private import FlowSummaryImpl as FlowSummaryImpl private import semmle.code.csharp.Caching private import semmle.code.csharp.dataflow.internal.DataFlowPrivate @@ -79,7 +78,6 @@ private class LocalTaintExprStepConfiguration extends ControlFlowReachabilityCon } private predicate localTaintStepCommon(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { - Stages::DataFlowStage::forceCachingInSameStage() and hasNodePath(any(LocalTaintExprStepConfiguration x), nodeFrom, nodeTo) or localTaintStepCil(nodeFrom, nodeTo) @@ -87,6 +85,11 @@ private predicate localTaintStepCommon(DataFlow::Node nodeFrom, DataFlow::Node n cached private module Cached { + private import DataFlowImplCommon as DataFlowImplCommon + + cached + predicate forceCachingInSameStage() { DataFlowImplCommon::forceCachingInSameStage() } + /** * Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local * (intra-procedural) step. diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll index f828419a440..f37a4f2d074 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll @@ -316,6 +316,15 @@ private module SsaDefReaches { ) } + /** + * Holds if the reference to `def` at index `i` in basic block `bb` is the + * last reference to `v` inside `bb`. + */ + pragma[noinline] + predicate lastSsaRef(Definition def, SourceVariable v, BasicBlock bb, int i) { + ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) + } + predicate defOccursInBlock(Definition def, BasicBlock bb, SourceVariable v) { exists(ssaDefRank(def, v, bb, _, _)) } @@ -351,8 +360,7 @@ private module SsaDefReaches { */ predicate defAdjacentRead(Definition def, BasicBlock bb1, BasicBlock bb2, int i2) { varBlockReaches(def, bb1, bb2) and - ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 and - variableRead(bb2, i2, _, _) + ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 } } @@ -434,15 +442,22 @@ predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2 bb2 = bb1 ) or - exists(SourceVariable v | ssaDefRank(def, v, bb1, i1, _) = maxSsaRefRank(bb1, v)) and + lastSsaRef(def, _, bb1, i1) and defAdjacentRead(def, bb1, bb2, i2) } +pragma[noinline] +private predicate adjacentDefRead( + Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v +) { + adjacentDefRead(def, bb1, i1, bb2, i2) and + v = def.getSourceVariable() +} + private predicate adjacentDefReachesRead( Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2 ) { - adjacentDefRead(def, bb1, i1, bb2, i2) and - exists(SourceVariable v | v = def.getSourceVariable() | + exists(SourceVariable v | adjacentDefRead(def, bb1, i1, bb2, i2, v) | ssaRef(bb1, i1, v, SsaDef()) or variableRead(bb1, i1, v, true) @@ -475,17 +490,19 @@ predicate adjacentDefNoUncertainReads(Definition def, BasicBlock bb1, int i1, Ba */ pragma[nomagic] predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) { - exists(int rnk, SourceVariable v, int j | rnk = ssaDefRank(def, v, bb, i, _) | + exists(SourceVariable v | // Next reference to `v` inside `bb` is a write - next.definesAt(v, bb, j) and - rnk + 1 = ssaRefRank(bb, j, v, SsaDef()) + exists(int rnk, int j | + rnk = ssaDefRank(def, v, bb, i, _) and + next.definesAt(v, bb, j) and + rnk + 1 = ssaRefRank(bb, j, v, SsaDef()) + ) or // Can reach a write using one or more steps - rnk = maxSsaRefRank(bb, v) and + lastSsaRef(def, v, bb, i) and exists(BasicBlock bb2 | varBlockReaches(def, bb, bb2) and - next.definesAt(v, bb2, j) and - 1 = ssaRefRank(bb2, j, v, SsaDef()) + 1 = ssaDefRank(next, v, bb2, _, SsaDef()) ) ) } @@ -539,7 +556,8 @@ pragma[nomagic] predicate lastRef(Definition def, BasicBlock bb, int i) { lastRefRedef(def, bb, i, _) or - exists(SourceVariable v | ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) | + lastSsaRef(def, _, bb, i) and + ( // Can reach exit directly bb instanceof ExitBasicBlock or diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ModulusAnalysisSpecific.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ModulusAnalysisSpecific.qll index f71b0d0ffcd..030020ede63 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ModulusAnalysisSpecific.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/ModulusAnalysisSpecific.qll @@ -49,7 +49,7 @@ module Private { } int getId(PhiInputEdgeBlock bb) { - exists(CfgImpl::ControlFlowTree::Range t | CfgImpl::ControlFlowTree::idOf(t, result) | + exists(CfgImpl::ControlFlowTree::Range_ t | CfgImpl::ControlFlowTree::idOf(t, result) | t = bb.getFirstNode().getElement() or t = bb.(CS::ControlFlow::BasicBlocks::EntryBlock).getCallable() diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll index 70ff26c1c58..2f8e93a4b75 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll @@ -122,7 +122,7 @@ private module Impl { ) or exists(G::AbstractValue v0 | - G::Internal::impliesSteps(result, v, eqFlowCondAbs(def, e, delta, isEq, v0), v0) + G::Internal::impliesStep(result, v, eqFlowCondAbs(def, e, delta, isEq, v0), v0) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll b/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll index 1e0eaaf8017..f06fbca375d 100644 --- a/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll +++ b/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll @@ -233,71 +233,61 @@ private module Internal { } pragma[noinline] - private predicate hasOverrider(OverridableCallable oc, ValueOrRefType t) { - exists(oc.getAnOverrider(t)) + private predicate hasOverrider(OverridableCallable oc, Gvn::GvnType t) { + exists(oc.getAnOverrider(any(ValueOrRefType t0 | Gvn::getGlobalValueNumber(t0) = t))) } pragma[noinline] - private predicate hasCallable(OverridableCallable source, ValueOrRefType t, OverridableCallable c) { + private predicate hasCallable(OverridableCallable source, Gvn::GvnType t, OverridableCallable c) { c.getUnboundDeclaration() = source and - t.hasCallable(c) and + any(ValueOrRefType t0 | Gvn::getGlobalValueNumber(t0) = t).hasCallable(c) and hasOverrider(c, t) and - hasQualifierTypeOverridden0(t, _) and - hasQualifierTypeOverridden1(source, _) - } - - pragma[noinline] - private Unification::ConstrainedTypeParameter getAConstrainedTypeParameterQualifierType( - DispatchMethodOrAccessorCall call - ) { - result = getAPossibleType(call.getQualifier(), false) - } - - pragma[noinline] - private predicate constrainedTypeParameterQualifierTypeSubsumes( - ValueOrRefType t, Unification::ConstrainedTypeParameter tp - ) { - tp = getAConstrainedTypeParameterQualifierType(_) and - tp.subsumes(t) - } - - pragma[noinline] - private predicate hasQualifierTypeOverridden0(ValueOrRefType t, DispatchMethodOrAccessorCall call) { - hasOverrider(_, t) and - ( - exists(Type t0, Type t1 | - t0 = getAPossibleType(call.getQualifier(), false) and - t1 = [t0, t0.(Unification::UnconstrainedTypeParameter).getAnUltimatelySuppliedType()] - | - t = t1 - or - Unification::subsumes(t1, t) - ) - or - constrainedTypeParameterQualifierTypeSubsumes(t, - getAConstrainedTypeParameterQualifierType(call)) - ) - } - - pragma[noinline] - private predicate hasQualifierTypeOverridden1( - OverridableCallable c, DispatchMethodOrAccessorCall call - ) { - exists(OverridableCallable target | call.getAStaticTarget() = target | - c = target.getUnboundDeclaration() - or - c = target.getAnUltimateImplementor().getUnboundDeclaration() - ) + source = any(DispatchMethodOrAccessorCall call).getAStaticTargetExt() } abstract private class DispatchMethodOrAccessorCall extends DispatchCallImpl { + pragma[noinline] + OverridableCallable getAStaticTargetExt() { + exists(OverridableCallable target | this.getAStaticTarget() = target | + result = target.getUnboundDeclaration() + or + result = target.getAnUltimateImplementor().getUnboundDeclaration() + ) + } + pragma[nomagic] predicate hasQualifierTypeInherited(Type t) { t = getAPossibleType(this.getQualifier(), _) } + pragma[noinline] + private predicate hasSubsumedQualifierType(Gvn::GvnType t) { + hasOverrider(_, t) and + exists(Type t0 | + t0 = getAPossibleType(this.getQualifier(), false) and + not t0 instanceof TypeParameter + | + t = Gvn::getGlobalValueNumber(t0) + or + Gvn::subsumes(Gvn::getGlobalValueNumber(t0), t) + ) + } + + pragma[noinline] + private predicate hasConstrainedTypeParameterQualifierType( + Unification::ConstrainedTypeParameter tp + ) { + tp = getAPossibleType(this.getQualifier(), false) + } + + pragma[noinline] + private predicate hasUnconstrainedTypeParameterQualifierType() { + getAPossibleType(this.getQualifier(), false) instanceof + Unification::UnconstrainedTypeParameter + } + pragma[nomagic] - predicate hasQualifierTypeOverridden(ValueOrRefType t, OverridableCallable c) { - hasQualifierTypeOverridden0(t, this) and - hasCallable(any(OverridableCallable oc | hasQualifierTypeOverridden1(oc, this)), t, c) + predicate hasSubsumedQualifierTypeOverridden(Gvn::GvnType t, OverridableCallable c) { + this.hasSubsumedQualifierType(t) and + hasCallable(any(OverridableCallable oc | oc = this.getAStaticTargetExt()), t, c) } /** @@ -357,12 +347,33 @@ private module Internal { } pragma[nomagic] - private Callable getASubsumedStaticTarget0(Type t) { + private predicate contextArgHasConstrainedTypeParameterType( + DispatchCall ctx, Unification::ConstrainedTypeParameter tp + ) { + this.contextArgHasType(ctx, tp, false) + } + + pragma[nomagic] + private predicate contextArgHasUnconstrainedTypeParameterType(DispatchCall ctx) { + this.contextArgHasType(ctx, any(Unification::UnconstrainedTypeParameter t), false) + } + + pragma[nomagic] + private predicate contextArgHasNonTypeParameterType(DispatchCall ctx, Gvn::GvnType t) { + exists(Type t0 | + this.contextArgHasType(ctx, t0, false) and + not t0 instanceof TypeParameter and + t = Gvn::getGlobalValueNumber(t0) + ) + } + + pragma[nomagic] + private Callable getASubsumedStaticTarget0(Gvn::GvnType t) { exists(Callable staticTarget, Type declType | staticTarget = this.getAStaticTarget() and declType = staticTarget.getDeclaringType() and result = staticTarget.getUnboundDeclaration() and - Unification::subsumes(declType, t) + Gvn::subsumes(Gvn::getGlobalValueNumber(declType), t) ) } @@ -375,7 +386,9 @@ private module Internal { private Callable getASubsumedStaticTarget() { result = this.getAStaticTarget() or - result.getUnboundDeclaration() = this.getASubsumedStaticTarget0(result.getDeclaringType()) + result.getUnboundDeclaration() = + this.getASubsumedStaticTarget0(pragma[only_bind_out](Gvn::getGlobalValueNumber(result + .getDeclaringType()))) } /** @@ -426,6 +439,12 @@ private module Internal { ) } + pragma[noinline] + NonConstructedOverridableCallable getAViableOverrider0() { + getAPossibleType(this.getQualifier(), false) instanceof TypeParameter and + result.getAConstructingCallableOrSelf() = this.getAStaticTargetExt() + } + /** * Gets a callable that is defined in a subtype of the qualifier type of this * call, and which overrides a static target of this call. @@ -468,9 +487,22 @@ private module Internal { */ private RuntimeCallable getAViableOverrider() { exists(ValueOrRefType t, NonConstructedOverridableCallable c | - this.hasQualifierTypeOverridden(t, c.getAConstructingCallableOrSelf()) and + this.hasSubsumedQualifierTypeOverridden(Gvn::getGlobalValueNumber(t), + c.getAConstructingCallableOrSelf()) and result = c.getAnOverrider(t) ) + or + exists(NonConstructedOverridableCallable c | + c = this.getAViableOverrider0() and + result = c.getAnOverrider(_) + | + this.hasUnconstrainedTypeParameterQualifierType() + or + exists(Unification::ConstrainedTypeParameter tp | + this.hasConstrainedTypeParameterQualifierType(tp) and + tp.subsumes(result.getDeclaringType()) + ) + ) } override RuntimeCallable getADynamicTarget() { @@ -510,36 +542,40 @@ private module Internal { } pragma[nomagic] - private RuntimeCallable getAViableOverriderInCallContext0( - NonConstructedOverridableCallable c, ValueOrRefType t - ) { - result = this.getAViableOverrider() and - this.contextArgHasType(_, _, false) and - result = c.getAnOverrider(t) + private RuntimeCallable getAViableOverriderInCallContext0(Gvn::GvnType t) { + exists(NonConstructedOverridableCallable c | + result = this.getAViableOverrider() and + this.contextArgHasType(_, _, false) and + result = c.getAnOverrider(any(Type t0 | t = Gvn::getGlobalValueNumber(t0))) and + this.getAStaticTarget() = c.getAConstructingCallableOrSelf() + ) } pragma[nomagic] - private RuntimeCallable getAViableOverriderInCallContext1( - NonConstructedOverridableCallable c, DispatchCall ctx - ) { - exists(ValueOrRefType t | - result = this.getAViableOverriderInCallContext0(c, t) and - exists(Type t0, Type t1 | - this.contextArgHasType(ctx, t0, false) and - t1 = [t0, t0.(Unification::UnconstrainedTypeParameter).getAnUltimatelySuppliedType()] - | - t = t1 - or - Unification::subsumes(t1, t) - ) + private predicate contextArgHasSubsumedType(DispatchCall ctx, Gvn::GvnType t) { + hasOverrider(_, t) and + exists(Gvn::GvnType t0 | this.contextArgHasNonTypeParameterType(ctx, t0) | + t = t0 + or + Gvn::subsumes(t0, t) ) } pragma[nomagic] private RuntimeCallable getAViableOverriderInCallContext(DispatchCall ctx) { - exists(NonConstructedOverridableCallable c | - result = this.getAViableOverriderInCallContext1(c, ctx) and - this.getAStaticTarget() = c.getAConstructingCallableOrSelf() + exists(Gvn::GvnType t | + result = this.getAViableOverriderInCallContext0(t) and + this.contextArgHasSubsumedType(ctx, t) + ) + or + result = this.getAViableOverrider() and + ( + this.contextArgHasUnconstrainedTypeParameterType(ctx) + or + exists(Unification::ConstrainedTypeParameter tp | + this.contextArgHasConstrainedTypeParameterType(ctx, tp) and + tp.subsumes(result.getDeclaringType()) + ) ) } @@ -636,31 +672,41 @@ private module Internal { ) } - private predicate stepExpr0(Expr succ, Expr pred) { - Steps::stepOpen(pred, succ) - or - exists(Assignable a | - a instanceof Field or - a instanceof Property - | - succ.(AssignableRead) = a.getAnAccess() and - pred = a.getAnAssignedValue() and - a = any(Modifiable m | not m.isEffectivelyPublic()) - ) - } - - private predicate stepExpr(Expr succ, Expr pred) { - stepExpr0(succ, pred) and + private predicate stepExpr(Expr pred, Expr succ) { + Steps::stepOpen(pred, succ) and // Do not step through down casts not downCast(succ) and // Only step when we may learn more about the actual type typeMayBeImprecise(succ.getType()) } - private predicate stepTC(Expr succ, Expr pred) = fastTC(stepExpr/2)(succ, pred) + private class AnalyzableFieldOrProperty extends Assignable, Modifiable { + AnalyzableFieldOrProperty() { + ( + this instanceof Field or + this instanceof Property + ) and + not this.isEffectivelyPublic() and + exists(this.getAnAssignedValue()) + } + + AssignableRead getARead() { result = this.getAnAccess() } + } private class Source extends Expr { - Source() { not stepExpr(this, _) } + Source() { + not stepExpr(_, this) and + not this = any(AnalyzableFieldOrProperty a).getARead() + } + + Type getType(boolean isExact) { + result = this.getType() and + if + this instanceof ObjectCreation or + this instanceof BaseAccess + then isExact = true + else isExact = false + } } private class Sink extends Expr { @@ -680,24 +726,38 @@ private module Internal { this = any(DispatchCallImpl c).getArgument(_) } - Source getASource() { stepTC(this, result) } + pragma[nomagic] + Expr getAPred() { stepExpr*(result, this) } + + pragma[nomagic] + AnalyzableFieldOrProperty getAPredRead() { this.getAPred() = result.getARead() } } - /** Holds if the expression `e` has an exact type. */ - private predicate hasExactType(Expr e) { - e instanceof ObjectCreation or - e instanceof BaseAccess + /** Gets a source type for sink expression `e`, using simple data flow. */ + Type getASourceType(Sink sink, boolean isExact) { + result = sink.getAPred().(Source).getType(isExact) + or + result = sink.getAPredRead().(RelevantFieldOrProperty).getASourceType(isExact) } - /** Gets a source type for expression `e`, using simple data flow. */ - Type getASourceType(Sink e, boolean isExact) { - exists(Source s | - s = e.getASource() or - s = e - | - result = s.getType() and - if hasExactType(s) then isExact = true else isExact = false - ) + private class RelevantFieldOrProperty extends AnalyzableFieldOrProperty { + RelevantFieldOrProperty() { + this = any(Sink s).getAPredRead() + or + this = any(RelevantFieldOrProperty a).getAPredRead() + } + + pragma[nomagic] + Expr getAPred() { stepExpr*(result, this.getAnAssignedValue()) } + + pragma[nomagic] + AnalyzableFieldOrProperty getAPredRead() { this.getAPred() = result.getARead() } + + Type getASourceType(boolean isExact) { + result = this.getAPred().(Source).getType(isExact) + or + result = this.getAPredRead().(RelevantFieldOrProperty).getASourceType(isExact) + } } } diff --git a/csharp/ql/src/semmle/code/csharp/exprs/Expr.qll b/csharp/ql/src/semmle/code/csharp/exprs/Expr.qll index 6006e165470..0988bb84340 100644 --- a/csharp/ql/src/semmle/code/csharp/exprs/Expr.qll +++ b/csharp/ql/src/semmle/code/csharp/exprs/Expr.qll @@ -20,7 +20,7 @@ import semmle.code.csharp.Location import semmle.code.csharp.Stmt import semmle.code.csharp.Type private import dotnet -private import semmle.code.csharp.Enclosing::Internal +private import semmle.code.csharp.ExprOrStmtParent private import semmle.code.csharp.frameworks.System private import semmle.code.csharp.TypeRef @@ -55,7 +55,7 @@ class Expr extends DotNet::Expr, ControlFlowElement, @expr { final Stmt getEnclosingStmt() { enclosingStmt(this, result) } /** Gets the enclosing callable of this expression, if any. */ - override Callable getEnclosingCallable() { exprEnclosingCallable(this, result) } + override Callable getEnclosingCallable() { enclosingCallable(this, result) } /** * Holds if this expression is generated by the compiler and does not appear diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/Dapper.qll b/csharp/ql/src/semmle/code/csharp/frameworks/Dapper.qll new file mode 100644 index 00000000000..3d25f4389fd --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/frameworks/Dapper.qll @@ -0,0 +1,42 @@ +/** + * Classes for modeling Dapper. + */ + +import csharp +private import semmle.code.csharp.frameworks.system.Data + +/** Definitions relating to the `Dapper` package. */ +module Dapper { + /** The namespace `Dapper`. */ + class DapperNamespace extends Namespace { + DapperNamespace() { this.hasQualifiedName("Dapper") } + } + + /** A class in `Dapper`. */ + class DapperClass extends Class { + DapperClass() { this.getParent() instanceof DapperNamespace } + } + + /** A struct in `Dapper`. */ + class DapperStruct extends Struct { + DapperStruct() { this.getParent() instanceof DapperNamespace } + } + + /** The `Dapper.SqlMapper` class. */ + class SqlMapperClass extends DapperClass { + SqlMapperClass() { this.hasName("SqlMapper") } + + /** Gets a DB query method. */ + ExtensionMethod getAQueryMethod() { + result = this.getAMethod() and + result.getName().regexpMatch("Query.*|Execute.*") and + result.getExtendedType() instanceof SystemDataIDbConnectionInterface and + result.isPublic() + } + } + + /** The `Dapper.CommandDefinition` struct. */ + class CommandDefinitionStruct extends DapperStruct { + CommandDefinitionStruct() { this.hasName("CommandDefinition") } + } +} diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/Sql.qll b/csharp/ql/src/semmle/code/csharp/frameworks/Sql.qll index 5774b22a631..1a727030bea 100644 --- a/csharp/ql/src/semmle/code/csharp/frameworks/Sql.qll +++ b/csharp/ql/src/semmle/code/csharp/frameworks/Sql.qll @@ -5,6 +5,8 @@ private import semmle.code.csharp.frameworks.system.Data private import semmle.code.csharp.frameworks.system.data.SqlClient private import semmle.code.csharp.frameworks.EntityFramework private import semmle.code.csharp.frameworks.NHibernate +private import semmle.code.csharp.frameworks.Dapper +private import semmle.code.csharp.dataflow.DataFlow4 /** An expression containing a SQL command. */ abstract class SqlExpr extends Expr { @@ -83,3 +85,37 @@ class MicrosoftSqlHelperMethodCallSqlExpr extends SqlExpr, MethodCall { ) } } + +/** A `Dapper.SqlMapper` method that is taking a SQL string argument. */ +class DapperSqlMethodCallSqlExpr extends SqlExpr, MethodCall { + DapperSqlMethodCallSqlExpr() { + this.getTarget() = any(Dapper::SqlMapperClass c).getAQueryMethod() + } + + override Expr getSql() { result = this.getArgumentForName("sql") } +} + +/** A `Dapper.CommandDefinition` creation that is taking a SQL string argument and is passed to a `Dapper.SqlMapper` method. */ +class DapperCommandDefinitionMethodCallSqlExpr extends SqlExpr, ObjectCreation { + DapperCommandDefinitionMethodCallSqlExpr() { + this.getObjectType() instanceof Dapper::CommandDefinitionStruct and + exists(Conf c | c.hasFlow(DataFlow::exprNode(this), _)) + } + + override Expr getSql() { result = this.getArgumentForName("commandText") } +} + +private class Conf extends DataFlow4::Configuration { + Conf() { this = "DapperCommandDefinitionFlowConfig" } + + override predicate isSource(DataFlow::Node node) { + node.asExpr().(ObjectCreation).getObjectType() instanceof Dapper::CommandDefinitionStruct + } + + override predicate isSink(DataFlow::Node node) { + exists(MethodCall mc | + mc.getTarget() = any(Dapper::SqlMapperClass c).getAQueryMethod() and + node.asExpr() = mc.getArgumentForName("command") + ) + } +} diff --git a/csharp/ql/src/semmle/code/csharp/security/dataflow/HardcodedCredentials.qll b/csharp/ql/src/semmle/code/csharp/security/dataflow/HardcodedCredentials.qll index 4538f09e2c0..0ffabe60588 100644 --- a/csharp/ql/src/semmle/code/csharp/security/dataflow/HardcodedCredentials.qll +++ b/csharp/ql/src/semmle/code/csharp/security/dataflow/HardcodedCredentials.qll @@ -111,13 +111,24 @@ module HardcodedCredentials { } /** - * Gets a regular expression for matching names of locations (variables, parameters, keys) that - * indicate the value being held is a credential. + * An assignable whose name indicates that the value being held is a credential. */ - private string getACredentialRegex() { - result = "(?i).*pass(wd|word|code|phrase)(?!.*question).*" or - result = "(?i).*(puid|username|userid).*" or - result = "(?i).*(cert)(?!.*(format|name)).*" + private class CredentialVar extends Assignable { + pragma[noinline] + CredentialVar() { + exists(string name | name = this.getName() | + name.regexpMatch("(?i).*pass(wd|word|code|phrase)(?!.*question).*") + or + name.regexpMatch("(?i).*(puid|username|userid).*") + or + name.regexpMatch("(?i).*(cert)(?!.*(format|name)).*") + ) + } + } + + private class CredentialVariableAccess extends VariableAccess { + pragma[noinline] + CredentialVariableAccess() { this.getTarget() instanceof CredentialVar } } /** @@ -128,11 +139,11 @@ module HardcodedCredentials { ) { // An argument to a library call that looks like a credential // "...flows to the [Username] parameter in [call to method CreateUser]" - exists(Call call | + exists(Call call, CredentialVar param | supplementaryElement = call and description = "the $@ parameter in $@" and - sink = call.getArgumentForName(sinkName) and - sinkName.regexpMatch(getACredentialRegex()) and + sink = call.getArgumentForParameter(param) and + sinkName = param.getName() and call.getTarget().fromLibrary() ) or @@ -144,22 +155,20 @@ module HardcodedCredentials { description = "the $@ in $@" and sink = call.getArgument(0) and sinkName = "setter call argument" and - p.getName().regexpMatch(getACredentialRegex()) and + p instanceof CredentialVar and p.fromLibrary() ) or // Sink compared to password variable // "...flows to [] which is compared against [access of UserName]" - exists(ComparisonTest ct, VariableAccess credentialAccess, string varName | + exists(ComparisonTest ct, CredentialVariableAccess credentialAccess | sinkName = sink.toString() and supplementaryElement = credentialAccess and description = "$@ which is compared against $@" and ct.getAnArgument() = credentialAccess and ct.getAnArgument() = sink and ct.getComparisonKind().isEquality() and - not sink = credentialAccess and - varName = credentialAccess.getTarget().getName() and - varName.regexpMatch(getACredentialRegex()) + not sink = credentialAccess ) } diff --git a/csharp/ql/src/semmle/code/csharp/security/dataflow/flowsources/Local.qll b/csharp/ql/src/semmle/code/csharp/security/dataflow/flowsources/Local.qll index e867c9be3dd..4c7f70921d9 100644 --- a/csharp/ql/src/semmle/code/csharp/security/dataflow/flowsources/Local.qll +++ b/csharp/ql/src/semmle/code/csharp/security/dataflow/flowsources/Local.qll @@ -20,3 +20,15 @@ class TextFieldSource extends LocalUserInputSource { override string getSourceType() { result = "TextBox text" } } + +/** A call to any `System.Console.Read*` method. */ +class SystemConsoleReadSource extends LocalUserInputSource { + SystemConsoleReadSource() { + this.asExpr() = + any(MethodCall call | + call.getTarget().hasQualifiedName("System.Console", ["ReadLine", "Read", "ReadKey"]) + ) + } + + override string getSourceType() { result = "System.Console input" } +} diff --git a/csharp/ql/src/semmle/code/dotnet/Element.qll b/csharp/ql/src/semmle/code/dotnet/Element.qll index 956a5f2c052..c38b09ce270 100644 --- a/csharp/ql/src/semmle/code/dotnet/Element.qll +++ b/csharp/ql/src/semmle/code/dotnet/Element.qll @@ -28,6 +28,9 @@ class Element extends @dotnet_element { /** Holds if this element is from source code. */ predicate fromSource() { this.getFile().fromSource() } + /** Holds if this element is from an assembly. */ + predicate fromLibrary() { this.getFile().fromLibrary() } + /** * Gets the "language" of this program element, as defined by the extension of the filename. * For example, C# has language "cs", and Visual Basic has language "vb". diff --git a/csharp/ql/src/semmlecode.csharp.dbscheme b/csharp/ql/src/semmlecode.csharp.dbscheme index 9258e9b38d8..770f844243d 100644 --- a/csharp/ql/src/semmlecode.csharp.dbscheme +++ b/csharp/ql/src/semmlecode.csharp.dbscheme @@ -529,7 +529,7 @@ function_pointer_return_type( int return_type_id: @type_or_ref ref); extend( - unique int sub: @type ref, + int sub: @type ref, int super: @type_or_ref ref); anonymous_types( diff --git a/csharp/ql/test/experimental/ir/ir/raw_ir.expected b/csharp/ql/test/experimental/ir/ir/raw_ir.expected index bfbcc0066a3..5d3a1dfe6bf 100644 --- a/csharp/ql/test/experimental/ir/ir/raw_ir.expected +++ b/csharp/ql/test/experimental/ir/ir/raw_ir.expected @@ -285,29 +285,37 @@ collections.cs: constructor_init.cs: # 5| System.Void BaseClass..ctor() # 5| Block 0 -# 5| v5_1(Void) = EnterFunction : -# 5| mu5_2() = AliasedDefinition : -# 5| r5_3(glval) = InitializeThis : -# 6| v6_1(Void) = NoOp : -# 5| v5_4(Void) = ReturnVoid : -# 5| v5_5(Void) = AliasedUse : ~m? -# 5| v5_6(Void) = ExitFunction : +# 5| v5_1(Void) = EnterFunction : +# 5| mu5_2() = AliasedDefinition : +# 5| r5_3(glval) = InitializeThis : +# 5| r5_4(glval) = Convert[BaseClass : Object] : r5_3 +# 5| r5_5() = FunctionAddress[Object] : +# 5| v5_6(Void) = Call[Object] : func:r5_5, this:r5_4 +# 5| mu5_7() = ^CallSideEffect : ~m? +# 6| v6_1(Void) = NoOp : +# 5| v5_8(Void) = ReturnVoid : +# 5| v5_9(Void) = AliasedUse : ~m? +# 5| v5_10(Void) = ExitFunction : # 9| System.Void BaseClass..ctor(System.Int32) # 9| Block 0 -# 9| v9_1(Void) = EnterFunction : -# 9| mu9_2() = AliasedDefinition : -# 9| r9_3(glval) = InitializeThis : -# 9| r9_4(glval) = VariableAddress[i] : -# 9| mu9_5(Int32) = InitializeParameter[i] : &:r9_4 -# 11| r11_1(glval) = VariableAddress[i] : -# 11| r11_2(Int32) = Load[i] : &:r11_1, ~m? -# 11| r11_3(BaseClass) = CopyValue : r9_3 -# 11| r11_4(glval) = FieldAddress[num] : r11_3 -# 11| mu11_5(Int32) = Store[?] : &:r11_4, r11_2 -# 9| v9_6(Void) = ReturnVoid : -# 9| v9_7(Void) = AliasedUse : ~m? -# 9| v9_8(Void) = ExitFunction : +# 9| v9_1(Void) = EnterFunction : +# 9| mu9_2() = AliasedDefinition : +# 9| r9_3(glval) = InitializeThis : +# 9| r9_4(glval) = VariableAddress[i] : +# 9| mu9_5(Int32) = InitializeParameter[i] : &:r9_4 +# 9| r9_6(glval) = Convert[BaseClass : Object] : r9_3 +# 9| r9_7() = FunctionAddress[Object] : +# 9| v9_8(Void) = Call[Object] : func:r9_7, this:r9_6 +# 9| mu9_9() = ^CallSideEffect : ~m? +# 11| r11_1(glval) = VariableAddress[i] : +# 11| r11_2(Int32) = Load[i] : &:r11_1, ~m? +# 11| r11_3(BaseClass) = CopyValue : r9_3 +# 11| r11_4(glval) = FieldAddress[num] : r11_3 +# 11| mu11_5(Int32) = Store[?] : &:r11_4, r11_2 +# 9| v9_10(Void) = ReturnVoid : +# 9| v9_11(Void) = AliasedUse : ~m? +# 9| v9_12(Void) = ExitFunction : # 17| System.Void DerivedClass..ctor() # 17| Block 0 @@ -469,20 +477,24 @@ delegates.cs: events.cs: # 8| System.Void Events..ctor() # 8| Block 0 -# 8| v8_1(Void) = EnterFunction : -# 8| mu8_2() = AliasedDefinition : -# 8| r8_3(glval) = InitializeThis : -# 10| r10_1(MyDel) = NewObj : -# 10| r10_2() = FunctionAddress[MyDel] : -# 10| r10_3(glval) = FunctionAddress[Fun] : -# 10| v10_4(Void) = Call[MyDel] : func:r10_2, this:r10_1, 0:r10_3 -# 10| mu10_5() = ^CallSideEffect : ~m? -# 10| r10_6(Events) = CopyValue : r8_3 -# 10| r10_7(glval) = FieldAddress[Inst] : r10_6 -# 10| mu10_8(MyDel) = Store[?] : &:r10_7, r10_1 -# 8| v8_4(Void) = ReturnVoid : -# 8| v8_5(Void) = AliasedUse : ~m? -# 8| v8_6(Void) = ExitFunction : +# 8| v8_1(Void) = EnterFunction : +# 8| mu8_2() = AliasedDefinition : +# 8| r8_3(glval) = InitializeThis : +# 8| r8_4(glval) = Convert[Events : Object] : r8_3 +# 8| r8_5() = FunctionAddress[Object] : +# 8| v8_6(Void) = Call[Object] : func:r8_5, this:r8_4 +# 8| mu8_7() = ^CallSideEffect : ~m? +# 10| r10_1(MyDel) = NewObj : +# 10| r10_2() = FunctionAddress[MyDel] : +# 10| r10_3(glval) = FunctionAddress[Fun] : +# 10| v10_4(Void) = Call[MyDel] : func:r10_2, this:r10_1, 0:r10_3 +# 10| mu10_5() = ^CallSideEffect : ~m? +# 10| r10_6(Events) = CopyValue : r8_3 +# 10| r10_7(glval) = FieldAddress[Inst] : r10_6 +# 10| mu10_8(MyDel) = Store[?] : &:r10_7, r10_1 +# 8| v8_8(Void) = ReturnVoid : +# 8| v8_9(Void) = AliasedUse : ~m? +# 8| v8_10(Void) = ExitFunction : # 13| System.Void Events.AddEvent() # 13| Block 0 @@ -611,38 +623,38 @@ foreach.cs: #-----| Goto -> Block 1 # 7| Block 1 -# 7| r7_8(glval) = VariableAddress[#temp7:9] : -# 7| r7_9(Boolean) = Load[#temp7:9] : &:r7_8, ~m? -# 7| r7_10() = FunctionAddress[MoveNext] : -# 7| r7_11(Boolean) = Call[MoveNext] : func:r7_10, this:r7_9 -# 7| mu7_12() = ^CallSideEffect : ~m? -# 7| v7_13(Void) = ConditionalBranch : r7_11 +# 7| r7_8(glval) = VariableAddress[#temp7:9] : +# 7| r7_9(IEnumerator) = Load[#temp7:9] : &:r7_8, ~m? +# 7| r7_10() = FunctionAddress[MoveNext] : +# 7| r7_11(Boolean) = Call[MoveNext] : func:r7_10, this:r7_9 +# 7| mu7_12() = ^CallSideEffect : ~m? +# 7| v7_13(Void) = ConditionalBranch : r7_11 #-----| False -> Block 3 #-----| True -> Block 2 # 7| Block 2 -# 7| r7_14(glval) = VariableAddress[items] : -# 7| r7_15(glval) = VariableAddress[#temp7:9] : -# 7| r7_16(Boolean) = Load[#temp7:9] : &:r7_15, ~m? -# 7| r7_17() = FunctionAddress[get_Current] : -# 7| r7_18(Int32) = Call[get_Current] : func:r7_17, this:r7_16 -# 7| mu7_19() = ^CallSideEffect : ~m? -# 7| mu7_20(Int32) = Store[items] : &:r7_14, r7_18 -# 9| r9_1(glval) = VariableAddress[x] : -# 9| r9_2(glval) = VariableAddress[items] : -# 9| r9_3(Int32) = Load[items] : &:r9_2, ~m? -# 9| mu9_4(Int32) = Store[x] : &:r9_1, r9_3 +# 7| r7_14(glval) = VariableAddress[items] : +# 7| r7_15(glval) = VariableAddress[#temp7:9] : +# 7| r7_16(IEnumerator) = Load[#temp7:9] : &:r7_15, ~m? +# 7| r7_17() = FunctionAddress[get_Current] : +# 7| r7_18(Int32) = Call[get_Current] : func:r7_17, this:r7_16 +# 7| mu7_19() = ^CallSideEffect : ~m? +# 7| mu7_20(Int32) = Store[items] : &:r7_14, r7_18 +# 9| r9_1(glval) = VariableAddress[x] : +# 9| r9_2(glval) = VariableAddress[items] : +# 9| r9_3(Int32) = Load[items] : &:r9_2, ~m? +# 9| mu9_4(Int32) = Store[x] : &:r9_1, r9_3 #-----| Goto (back edge) -> Block 1 # 7| Block 3 -# 7| r7_21(glval) = VariableAddress[#temp7:9] : -# 7| r7_22(Boolean) = Load[#temp7:9] : &:r7_21, ~m? -# 7| r7_23() = FunctionAddress[Dispose] : -# 7| v7_24(Void) = Call[Dispose] : func:r7_23, this:r7_22 -# 7| mu7_25() = ^CallSideEffect : ~m? -# 4| v4_3(Void) = ReturnVoid : -# 4| v4_4(Void) = AliasedUse : ~m? -# 4| v4_5(Void) = ExitFunction : +# 7| r7_21(glval) = VariableAddress[#temp7:9] : +# 7| r7_22(IEnumerator) = Load[#temp7:9] : &:r7_21, ~m? +# 7| r7_23() = FunctionAddress[Dispose] : +# 7| v7_24(Void) = Call[Dispose] : func:r7_23, this:r7_22 +# 7| mu7_25() = ^CallSideEffect : ~m? +# 4| v4_3(Void) = ReturnVoid : +# 4| v4_4(Void) = AliasedUse : ~m? +# 4| v4_5(Void) = ExitFunction : func_with_param_call.cs: # 5| System.Int32 test_call_with_param.f(System.Int32,System.Int32) @@ -1237,29 +1249,37 @@ lock.cs: obj_creation.cs: # 7| System.Void ObjCreation.MyClass..ctor() # 7| Block 0 -# 7| v7_1(Void) = EnterFunction : -# 7| mu7_2() = AliasedDefinition : -# 7| r7_3(glval) = InitializeThis : -# 8| v8_1(Void) = NoOp : -# 7| v7_4(Void) = ReturnVoid : -# 7| v7_5(Void) = AliasedUse : ~m? -# 7| v7_6(Void) = ExitFunction : +# 7| v7_1(Void) = EnterFunction : +# 7| mu7_2() = AliasedDefinition : +# 7| r7_3(glval) = InitializeThis : +# 7| r7_4(glval) = Convert[MyClass : Object] : r7_3 +# 7| r7_5() = FunctionAddress[Object] : +# 7| v7_6(Void) = Call[Object] : func:r7_5, this:r7_4 +# 7| mu7_7() = ^CallSideEffect : ~m? +# 8| v8_1(Void) = NoOp : +# 7| v7_8(Void) = ReturnVoid : +# 7| v7_9(Void) = AliasedUse : ~m? +# 7| v7_10(Void) = ExitFunction : # 11| System.Void ObjCreation.MyClass..ctor(System.Int32) # 11| Block 0 -# 11| v11_1(Void) = EnterFunction : -# 11| mu11_2() = AliasedDefinition : -# 11| r11_3(glval) = InitializeThis : -# 11| r11_4(glval) = VariableAddress[_x] : -# 11| mu11_5(Int32) = InitializeParameter[_x] : &:r11_4 -# 13| r13_1(glval) = VariableAddress[_x] : -# 13| r13_2(Int32) = Load[_x] : &:r13_1, ~m? -# 13| r13_3(MyClass) = CopyValue : r11_3 -# 13| r13_4(glval) = FieldAddress[x] : r13_3 -# 13| mu13_5(Int32) = Store[?] : &:r13_4, r13_2 -# 11| v11_6(Void) = ReturnVoid : -# 11| v11_7(Void) = AliasedUse : ~m? -# 11| v11_8(Void) = ExitFunction : +# 11| v11_1(Void) = EnterFunction : +# 11| mu11_2() = AliasedDefinition : +# 11| r11_3(glval) = InitializeThis : +# 11| r11_4(glval) = VariableAddress[_x] : +# 11| mu11_5(Int32) = InitializeParameter[_x] : &:r11_4 +# 11| r11_6(glval) = Convert[MyClass : Object] : r11_3 +# 11| r11_7() = FunctionAddress[Object] : +# 11| v11_8(Void) = Call[Object] : func:r11_7, this:r11_6 +# 11| mu11_9() = ^CallSideEffect : ~m? +# 13| r13_1(glval) = VariableAddress[_x] : +# 13| r13_2(Int32) = Load[_x] : &:r13_1, ~m? +# 13| r13_3(MyClass) = CopyValue : r11_3 +# 13| r13_4(glval) = FieldAddress[x] : r13_3 +# 13| mu13_5(Int32) = Store[?] : &:r13_4, r13_2 +# 11| v11_10(Void) = ReturnVoid : +# 11| v11_11(Void) = AliasedUse : ~m? +# 11| v11_12(Void) = ExitFunction : # 17| System.Void ObjCreation.SomeFun(ObjCreation.MyClass) # 17| Block 0 @@ -1884,13 +1904,17 @@ stmts.cs: using.cs: # 7| System.Void UsingStmt.MyDisposable..ctor() # 7| Block 0 -# 7| v7_1(Void) = EnterFunction : -# 7| mu7_2() = AliasedDefinition : -# 7| r7_3(glval) = InitializeThis : -# 7| v7_4(Void) = NoOp : -# 7| v7_5(Void) = ReturnVoid : -# 7| v7_6(Void) = AliasedUse : ~m? -# 7| v7_7(Void) = ExitFunction : +# 7| v7_1(Void) = EnterFunction : +# 7| mu7_2() = AliasedDefinition : +# 7| r7_3(glval) = InitializeThis : +# 7| r7_4(glval) = Convert[MyDisposable : Object] : r7_3 +# 7| r7_5() = FunctionAddress[Object] : +# 7| v7_6(Void) = Call[Object] : func:r7_5, this:r7_4 +# 7| mu7_7() = ^CallSideEffect : ~m? +# 7| v7_8(Void) = NoOp : +# 7| v7_9(Void) = ReturnVoid : +# 7| v7_10(Void) = AliasedUse : ~m? +# 7| v7_11(Void) = ExitFunction : # 8| System.Void UsingStmt.MyDisposable.DoSomething() # 8| Block 0 diff --git a/csharp/ql/test/experimental/ir/ir/raw_ir_consistency.expected b/csharp/ql/test/experimental/ir/ir/raw_ir_consistency.expected index b2f9c0da160..7231134d5e2 100644 --- a/csharp/ql/test/experimental/ir/ir/raw_ir_consistency.expected +++ b/csharp/ql/test/experimental/ir/ir/raw_ir_consistency.expected @@ -26,9 +26,6 @@ fieldAddressOnNonPointer | inoutref.cs:19:13:19:17 | FieldAddress: access to field fld | FieldAddress instruction 'FieldAddress: access to field fld' has an object address operand that is not an address, in function '$@'. | inoutref.cs:16:17:16:17 | System.Void InOutRef.F(System.Int32,MyStruct,MyStruct,MyClass,MyClass) | System.Void InOutRef.F(System.Int32,MyStruct,MyStruct,MyClass,MyClass) | | pointers.cs:35:17:35:24 | FieldAddress: access to field fld | FieldAddress instruction 'FieldAddress: access to field fld' has an object address operand that is not an address, in function '$@'. | pointers.cs:25:17:25:20 | System.Void Pointers.Main() | System.Void Pointers.Main() | thisArgumentIsNonPointer -| foreach.cs:7:9:10:9 | Call: foreach (... ... in ...) ... | Call instruction 'Call: foreach (... ... in ...) ...' has a `this` argument operand that is not an address, in function '$@'. | foreach.cs:4:24:4:27 | System.Void ForEach.Main() | System.Void ForEach.Main() | -| foreach.cs:7:9:10:9 | Call: foreach (... ... in ...) ... | Call instruction 'Call: foreach (... ... in ...) ...' has a `this` argument operand that is not an address, in function '$@'. | foreach.cs:4:24:4:27 | System.Void ForEach.Main() | System.Void ForEach.Main() | -| foreach.cs:7:9:10:9 | Call: foreach (... ... in ...) ... | Call instruction 'Call: foreach (... ... in ...) ...' has a `this` argument operand that is not an address, in function '$@'. | foreach.cs:4:24:4:27 | System.Void ForEach.Main() | System.Void ForEach.Main() | | inoutref.cs:32:22:32:35 | Call: object creation of type MyStruct | Call instruction 'Call: object creation of type MyStruct' has a `this` argument operand that is not an address, in function '$@'. | inoutref.cs:29:17:29:20 | System.Void InOutRef.Main() | System.Void InOutRef.Main() | | pointers.cs:27:22:27:35 | Call: object creation of type MyStruct | Call instruction 'Call: object creation of type MyStruct' has a `this` argument operand that is not an address, in function '$@'. | pointers.cs:25:17:25:20 | System.Void Pointers.Main() | System.Void Pointers.Main() | missingCanonicalLanguageType diff --git a/csharp/ql/test/experimental/ir/ir/unaliased_ssa_consistency.expected b/csharp/ql/test/experimental/ir/ir/unaliased_ssa_consistency.expected index b2f9c0da160..7231134d5e2 100644 --- a/csharp/ql/test/experimental/ir/ir/unaliased_ssa_consistency.expected +++ b/csharp/ql/test/experimental/ir/ir/unaliased_ssa_consistency.expected @@ -26,9 +26,6 @@ fieldAddressOnNonPointer | inoutref.cs:19:13:19:17 | FieldAddress: access to field fld | FieldAddress instruction 'FieldAddress: access to field fld' has an object address operand that is not an address, in function '$@'. | inoutref.cs:16:17:16:17 | System.Void InOutRef.F(System.Int32,MyStruct,MyStruct,MyClass,MyClass) | System.Void InOutRef.F(System.Int32,MyStruct,MyStruct,MyClass,MyClass) | | pointers.cs:35:17:35:24 | FieldAddress: access to field fld | FieldAddress instruction 'FieldAddress: access to field fld' has an object address operand that is not an address, in function '$@'. | pointers.cs:25:17:25:20 | System.Void Pointers.Main() | System.Void Pointers.Main() | thisArgumentIsNonPointer -| foreach.cs:7:9:10:9 | Call: foreach (... ... in ...) ... | Call instruction 'Call: foreach (... ... in ...) ...' has a `this` argument operand that is not an address, in function '$@'. | foreach.cs:4:24:4:27 | System.Void ForEach.Main() | System.Void ForEach.Main() | -| foreach.cs:7:9:10:9 | Call: foreach (... ... in ...) ... | Call instruction 'Call: foreach (... ... in ...) ...' has a `this` argument operand that is not an address, in function '$@'. | foreach.cs:4:24:4:27 | System.Void ForEach.Main() | System.Void ForEach.Main() | -| foreach.cs:7:9:10:9 | Call: foreach (... ... in ...) ... | Call instruction 'Call: foreach (... ... in ...) ...' has a `this` argument operand that is not an address, in function '$@'. | foreach.cs:4:24:4:27 | System.Void ForEach.Main() | System.Void ForEach.Main() | | inoutref.cs:32:22:32:35 | Call: object creation of type MyStruct | Call instruction 'Call: object creation of type MyStruct' has a `this` argument operand that is not an address, in function '$@'. | inoutref.cs:29:17:29:20 | System.Void InOutRef.Main() | System.Void InOutRef.Main() | | pointers.cs:27:22:27:35 | Call: object creation of type MyStruct | Call instruction 'Call: object creation of type MyStruct' has a `this` argument operand that is not an address, in function '$@'. | pointers.cs:25:17:25:20 | System.Void Pointers.Main() | System.Void Pointers.Main() | missingCanonicalLanguageType diff --git a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected index 3936ebd12ae..45b1b481612 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected @@ -674,14 +674,14 @@ | Foreach.cs:36:10:36:11 | exit M6 (normal) | Foreach.cs:36:10:36:11 | exit M6 | 2 | | Foreach.cs:38:9:39:11 | foreach (... ... in ...) ... | Foreach.cs:38:9:39:11 | foreach (... ... in ...) ... | 1 | | Foreach.cs:38:26:38:26 | String x | Foreach.cs:39:11:39:11 | ; | 4 | -| Initializers.cs:8:5:8:16 | enter Initializers | Initializers.cs:8:5:8:16 | exit Initializers | 15 | -| Initializers.cs:10:5:10:16 | enter Initializers | Initializers.cs:10:5:10:16 | exit Initializers | 15 | +| Initializers.cs:8:5:8:16 | enter Initializers | Initializers.cs:8:5:8:16 | exit Initializers | 16 | +| Initializers.cs:10:5:10:16 | enter Initializers | Initializers.cs:10:5:10:16 | exit Initializers | 16 | | Initializers.cs:12:10:12:10 | enter M | Initializers.cs:12:10:12:10 | exit M | 22 | | Initializers.cs:18:20:18:20 | 1 | Initializers.cs:18:16:18:20 | ... = ... | 2 | | Initializers.cs:20:11:20:23 | enter NoConstructor | Initializers.cs:20:11:20:23 | exit NoConstructor | 9 | | Initializers.cs:31:9:31:11 | enter Sub | Initializers.cs:31:9:31:11 | exit Sub | 12 | | Initializers.cs:33:9:33:11 | enter Sub | Initializers.cs:33:9:33:11 | exit Sub | 9 | -| Initializers.cs:35:9:35:11 | enter Sub | Initializers.cs:35:9:35:11 | exit Sub | 19 | +| Initializers.cs:35:9:35:11 | enter Sub | Initializers.cs:35:9:35:11 | exit Sub | 14 | | Initializers.cs:51:10:51:13 | enter Test | Initializers.cs:51:10:51:13 | exit Test | 105 | | LoopUnrolling.cs:7:10:7:11 | enter M1 | LoopUnrolling.cs:9:13:9:28 | ... == ... | 7 | | LoopUnrolling.cs:7:10:7:11 | exit M1 (normal) | LoopUnrolling.cs:7:10:7:11 | exit M1 | 2 | @@ -752,7 +752,6 @@ | MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationB.cs:5:16:5:16 | exit M | 1 | | MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationA.cs:8:16:8:16 | exit M (abnormal) | 3 | | MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationB.cs:5:16:5:16 | exit M (abnormal) | 3 | -| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:13:16:13:20 | ... = ... | 3 | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | exit get_Item (normal) | 2 | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationB.cs:12:31:12:40 | exit get_Item (normal) | 2 | | MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | enter get_Item | 1 | @@ -776,18 +775,17 @@ | MultiImplementationA.cs:16:17:16:18 | exit M1 (normal) | MultiImplementationB.cs:14:17:14:18 | exit M1 | 2 | | MultiImplementationA.cs:17:5:19:5 | {...} | MultiImplementationA.cs:18:9:18:22 | M2(...) | 2 | | MultiImplementationA.cs:18:9:18:22 | enter M2 | MultiImplementationA.cs:18:9:18:22 | exit M2 | 4 | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationA.cs:20:12:20:13 | exit C2 (normal) | 14 | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationB.cs:18:12:18:13 | exit C2 (normal) | 14 | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | enter C2 | 1 | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | enter C2 | 1 | | MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | 1 | | MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | 1 | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:12:20:13 | exit C2 (normal) | 6 | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationB.cs:18:12:18:13 | exit C2 (normal) | 6 | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | enter C2 | 1 | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | enter C2 | 1 | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | exit C2 | 2 | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | exit C2 | 2 | -| MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | 2 | -| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationA.cs:21:27:21:29 | {...} | 1 | +| MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:27:21:29 | {...} | 3 | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | 1 | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | 1 | | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | 1 | @@ -800,7 +798,6 @@ | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | 1 | | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion (normal) | 2 | | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion (normal) | 2 | -| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:24:32:24:34 | ... = ... | 4 | | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | exit get_P3 | 5 | | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | exit get_P3 | 5 | | MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | enter M1 | 1 | @@ -834,7 +831,6 @@ | MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationB.cs:5:16:5:16 | exit M | 1 | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationA.cs:8:16:8:16 | exit M (normal) | 2 | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:16:5:16 | exit M (normal) | 2 | -| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:11:16:11:20 | ... = ... | 3 | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | enter get_Item | 1 | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationB.cs:12:31:12:40 | enter get_Item | 1 | | MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | exit get_Item | 1 | @@ -858,18 +854,17 @@ | MultiImplementationB.cs:14:17:14:18 | exit M1 (normal) | MultiImplementationB.cs:14:17:14:18 | exit M1 | 2 | | MultiImplementationB.cs:15:5:17:5 | {...} | MultiImplementationB.cs:16:9:16:31 | M2(...) | 2 | | MultiImplementationB.cs:16:9:16:31 | enter M2 | MultiImplementationB.cs:16:9:16:31 | exit M2 | 5 | +| MultiImplementationB.cs:18:12:18:13 | call to constructor Object | MultiImplementationA.cs:20:12:20:13 | exit C2 (abnormal) | 12 | +| MultiImplementationB.cs:18:12:18:13 | call to constructor Object | MultiImplementationB.cs:18:12:18:13 | exit C2 (abnormal) | 12 | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | enter C2 | 1 | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | enter C2 | 1 | | MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | 1 | | MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | 1 | -| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationA.cs:20:12:20:13 | exit C2 (abnormal) | 4 | -| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:18:12:18:13 | exit C2 (abnormal) | 4 | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | enter C2 | 1 | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | enter C2 | 1 | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | exit C2 | 2 | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | exit C2 | 2 | -| MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | 2 | -| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationB.cs:19:27:19:29 | {...} | 1 | +| MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:27:19:29 | {...} | 3 | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | 1 | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | 1 | | MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | 1 | @@ -882,7 +877,6 @@ | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | 1 | | MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion (abnormal) | 3 | | MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion (abnormal) | 3 | -| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:22:32:22:34 | ... = ... | 4 | | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | exit get_P3 | 5 | | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | exit get_P3 | 5 | | MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | enter M1 | 1 | @@ -928,6 +922,8 @@ | NullCoalescing.cs:15:31:15:31 | 0 | NullCoalescing.cs:16:17:16:18 | "" | 5 | | NullCoalescing.cs:16:17:16:25 | ... ?? ... | NullCoalescing.cs:17:13:17:19 | (...) ... | 5 | | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:13:10:13:11 | exit M6 | 4 | +| PartialImplementationA.cs:3:12:3:18 | enter Partial | PartialImplementationA.cs:3:12:3:18 | exit Partial | 12 | +| PartialImplementationB.cs:4:12:4:18 | enter Partial | PartialImplementationB.cs:4:12:4:18 | exit Partial | 12 | | Patterns.cs:5:10:5:11 | enter M1 | Patterns.cs:8:18:8:23 | Int32 i1 | 8 | | Patterns.cs:8:13:8:23 | [false] ... is ... | Patterns.cs:8:13:8:23 | [false] ... is ... | 1 | | Patterns.cs:8:13:8:23 | [true] ... is ... | Patterns.cs:8:13:8:23 | [true] ... is ... | 1 | @@ -1231,7 +1227,7 @@ | cflow.cs:127:48:127:49 | "" | cflow.cs:127:48:127:49 | "" | 1 | | cflow.cs:127:53:127:57 | this access | cflow.cs:127:53:127:57 | access to field Field | 2 | | cflow.cs:127:62:127:64 | enter set_Prop | cflow.cs:127:62:127:64 | exit set_Prop | 8 | -| cflow.cs:129:5:129:15 | enter ControlFlow | cflow.cs:129:5:129:15 | exit ControlFlow | 8 | +| cflow.cs:129:5:129:15 | enter ControlFlow | cflow.cs:129:5:129:15 | exit ControlFlow | 9 | | cflow.cs:134:5:134:15 | enter ControlFlow | cflow.cs:134:5:134:15 | exit ControlFlow | 9 | | cflow.cs:136:12:136:22 | enter ControlFlow | cflow.cs:136:12:136:22 | exit ControlFlow | 8 | | cflow.cs:138:40:138:40 | enter + | cflow.cs:138:40:138:40 | exit + | 9 | @@ -1330,7 +1326,7 @@ | cflow.cs:284:5:284:18 | enter ControlFlowSub | cflow.cs:284:5:284:18 | exit ControlFlowSub | 5 | | cflow.cs:286:5:286:18 | enter ControlFlowSub | cflow.cs:286:5:286:18 | exit ControlFlowSub | 7 | | cflow.cs:291:12:291:12 | enter M | cflow.cs:291:12:291:12 | exit M | 6 | -| cflow.cs:296:5:296:25 | enter NegationInConstructor | cflow.cs:296:5:296:25 | exit NegationInConstructor | 4 | +| cflow.cs:296:5:296:25 | enter NegationInConstructor | cflow.cs:296:5:296:25 | exit NegationInConstructor | 5 | | cflow.cs:298:10:298:10 | enter M | cflow.cs:300:46:300:50 | ... > ... | 7 | | cflow.cs:300:44:300:51 | [false] !... | cflow.cs:300:44:300:51 | [false] !... | 1 | | cflow.cs:300:44:300:51 | [true] !... | cflow.cs:300:44:300:51 | [true] !... | 1 | diff --git a/csharp/ql/test/library-tests/controlflow/graph/Common.qll b/csharp/ql/test/library-tests/controlflow/graph/Common.qll index 5945d2003b2..f6f9b26f1cd 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Common.qll +++ b/csharp/ql/test/library-tests/controlflow/graph/Common.qll @@ -11,9 +11,15 @@ class SourceControlFlowElement extends ControlFlowElement { } class SourceControlFlowNode extends ControlFlow::Node { - SourceControlFlowNode() { not this.getLocation().getFile() instanceof StubFile } + SourceControlFlowNode() { + not this.getLocation().getFile() instanceof StubFile and + not this.getLocation().getFile().fromLibrary() + } } class SourceBasicBlock extends ControlFlow::BasicBlock { - SourceBasicBlock() { not this.getLocation().getFile() instanceof StubFile } + SourceBasicBlock() { + not this.getLocation().getFile() instanceof StubFile and + not this.getLocation().getFile().fromLibrary() + } } diff --git a/csharp/ql/test/library-tests/controlflow/graph/Consistency.expected b/csharp/ql/test/library-tests/controlflow/graph/Consistency.expected index 8e868a0678a..bccab8e85a4 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Consistency.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/Consistency.expected @@ -5,23 +5,3 @@ breakInvariant3 breakInvariant4 breakInvariant5 multipleSuccessors -| MultiImplementationA.cs:13:16:13:20 | ... = ... | successor | MultiImplementationA.cs:13:16:13:16 | this access | -| MultiImplementationA.cs:13:16:13:20 | ... = ... | successor | MultiImplementationA.cs:24:16:24:16 | this access | -| MultiImplementationA.cs:13:16:13:20 | ... = ... | successor | MultiImplementationB.cs:11:16:11:16 | this access | -| MultiImplementationA.cs:13:16:13:20 | ... = ... | successor | MultiImplementationB.cs:22:16:22:16 | this access | -| MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | successor | MultiImplementationA.cs:21:27:21:29 | {...} | -| MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | successor | MultiImplementationB.cs:19:27:19:29 | {...} | -| MultiImplementationA.cs:24:32:24:34 | ... = ... | successor | MultiImplementationA.cs:20:22:20:31 | {...} | -| MultiImplementationA.cs:24:32:24:34 | ... = ... | successor | MultiImplementationA.cs:24:16:24:16 | this access | -| MultiImplementationA.cs:24:32:24:34 | ... = ... | successor | MultiImplementationB.cs:18:22:18:36 | {...} | -| MultiImplementationA.cs:24:32:24:34 | ... = ... | successor | MultiImplementationB.cs:22:16:22:16 | this access | -| MultiImplementationB.cs:11:16:11:20 | ... = ... | successor | MultiImplementationA.cs:13:16:13:16 | this access | -| MultiImplementationB.cs:11:16:11:20 | ... = ... | successor | MultiImplementationA.cs:24:16:24:16 | this access | -| MultiImplementationB.cs:11:16:11:20 | ... = ... | successor | MultiImplementationB.cs:11:16:11:16 | this access | -| MultiImplementationB.cs:11:16:11:20 | ... = ... | successor | MultiImplementationB.cs:22:16:22:16 | this access | -| MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | successor | MultiImplementationA.cs:21:27:21:29 | {...} | -| MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | successor | MultiImplementationB.cs:19:27:19:29 | {...} | -| MultiImplementationB.cs:22:32:22:34 | ... = ... | successor | MultiImplementationA.cs:20:22:20:31 | {...} | -| MultiImplementationB.cs:22:32:22:34 | ... = ... | successor | MultiImplementationA.cs:24:16:24:16 | this access | -| MultiImplementationB.cs:22:32:22:34 | ... = ... | successor | MultiImplementationB.cs:18:22:18:36 | {...} | -| MultiImplementationB.cs:22:32:22:34 | ... = ... | successor | MultiImplementationB.cs:22:16:22:16 | this access | diff --git a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected index 2583984542d..de10c0acf6f 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected @@ -2475,10 +2475,12 @@ dominance | Initializers.cs:6:27:6:31 | ... + ... | Initializers.cs:6:9:6:9 | access to property G | | Initializers.cs:6:31:6:31 | 2 | Initializers.cs:6:27:6:31 | ... + ... | | Initializers.cs:6:31:6:31 | 2 | Initializers.cs:6:27:6:31 | ... + ... | -| Initializers.cs:8:5:8:16 | enter Initializers | Initializers.cs:5:9:5:9 | this access | +| Initializers.cs:8:5:8:16 | call to constructor Object | Initializers.cs:5:9:5:9 | this access | +| Initializers.cs:8:5:8:16 | enter Initializers | Initializers.cs:8:5:8:16 | call to constructor Object | | Initializers.cs:8:5:8:16 | exit Initializers (normal) | Initializers.cs:8:5:8:16 | exit Initializers | | Initializers.cs:8:20:8:22 | {...} | Initializers.cs:8:5:8:16 | exit Initializers (normal) | -| Initializers.cs:10:5:10:16 | enter Initializers | Initializers.cs:5:9:5:9 | this access | +| Initializers.cs:10:5:10:16 | call to constructor Object | Initializers.cs:5:9:5:9 | this access | +| Initializers.cs:10:5:10:16 | enter Initializers | Initializers.cs:10:5:10:16 | call to constructor Object | | Initializers.cs:10:5:10:16 | exit Initializers (normal) | Initializers.cs:10:5:10:16 | exit Initializers | | Initializers.cs:10:28:10:30 | {...} | Initializers.cs:10:5:10:16 | exit Initializers (normal) | | Initializers.cs:12:10:12:10 | enter M | Initializers.cs:13:5:16:5 | {...} | @@ -2506,16 +2508,10 @@ dominance | Initializers.cs:20:11:20:23 | enter NoConstructor | Initializers.cs:22:23:22:23 | this access | | Initializers.cs:20:11:20:23 | exit NoConstructor (normal) | Initializers.cs:20:11:20:23 | exit NoConstructor | | Initializers.cs:22:23:22:23 | this access | Initializers.cs:22:27:22:27 | 0 | -| Initializers.cs:22:23:22:23 | this access | Initializers.cs:22:27:22:27 | 0 | -| Initializers.cs:22:23:22:27 | ... = ... | Initializers.cs:23:23:23:23 | this access | | Initializers.cs:22:23:22:27 | ... = ... | Initializers.cs:23:23:23:23 | this access | | Initializers.cs:22:27:22:27 | 0 | Initializers.cs:22:23:22:27 | ... = ... | -| Initializers.cs:22:27:22:27 | 0 | Initializers.cs:22:23:22:27 | ... = ... | -| Initializers.cs:23:23:23:23 | this access | Initializers.cs:23:27:23:27 | 1 | | Initializers.cs:23:23:23:23 | this access | Initializers.cs:23:27:23:27 | 1 | | Initializers.cs:23:23:23:27 | ... = ... | Initializers.cs:20:11:20:23 | exit NoConstructor (normal) | -| Initializers.cs:23:23:23:27 | ... = ... | Initializers.cs:28:13:28:13 | this access | -| Initializers.cs:23:27:23:27 | 1 | Initializers.cs:23:23:23:27 | ... = ... | | Initializers.cs:23:27:23:27 | 1 | Initializers.cs:23:23:23:27 | ... = ... | | Initializers.cs:28:13:28:13 | this access | Initializers.cs:28:17:28:17 | 2 | | Initializers.cs:28:13:28:13 | this access | Initializers.cs:28:17:28:17 | 2 | @@ -2539,7 +2535,8 @@ dominance | Initializers.cs:33:31:33:35 | ... = ... | Initializers.cs:33:9:33:11 | exit Sub (normal) | | Initializers.cs:33:31:33:36 | ...; | Initializers.cs:33:31:33:31 | this access | | Initializers.cs:33:35:33:35 | access to parameter i | Initializers.cs:33:31:33:35 | ... = ... | -| Initializers.cs:35:9:35:11 | enter Sub | Initializers.cs:22:23:22:23 | this access | +| Initializers.cs:35:9:35:11 | call to constructor NoConstructor | Initializers.cs:28:13:28:13 | this access | +| Initializers.cs:35:9:35:11 | enter Sub | Initializers.cs:35:9:35:11 | call to constructor NoConstructor | | Initializers.cs:35:9:35:11 | exit Sub (normal) | Initializers.cs:35:9:35:11 | exit Sub | | Initializers.cs:35:27:35:40 | {...} | Initializers.cs:35:29:35:38 | ...; | | Initializers.cs:35:29:35:29 | this access | Initializers.cs:35:33:35:33 | access to parameter i | @@ -2865,6 +2862,7 @@ dominance | MultiImplementationA.cs:8:23:8:32 | throw ... | MultiImplementationB.cs:5:16:5:16 | exit M (abnormal) | | MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationA.cs:8:23:8:32 | throw ... | | MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:13:20:13:20 | 0 | +| MultiImplementationA.cs:13:16:13:20 | ... = ... | MultiImplementationA.cs:24:16:24:16 | this access | | MultiImplementationA.cs:13:20:13:20 | 0 | MultiImplementationA.cs:13:16:13:20 | ... = ... | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | exit get_Item (normal) | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationB.cs:12:31:12:40 | exit get_Item (normal) | @@ -2888,8 +2886,9 @@ dominance | MultiImplementationA.cs:18:9:18:22 | enter M2 | MultiImplementationA.cs:18:21:18:21 | 0 | | MultiImplementationA.cs:18:9:18:22 | exit M2 (normal) | MultiImplementationA.cs:18:9:18:22 | exit M2 | | MultiImplementationA.cs:18:21:18:21 | 0 | MultiImplementationA.cs:18:9:18:22 | exit M2 (normal) | -| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:13:16:13:16 | this access | -| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationA.cs:13:16:13:16 | this access | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | | MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:24:20:29 | ...; | | MultiImplementationA.cs:20:24:20:24 | this access | MultiImplementationA.cs:20:28:20:28 | access to parameter i | | MultiImplementationA.cs:20:24:20:28 | ... = ... | MultiImplementationA.cs:20:12:20:13 | exit C2 (normal) | @@ -2900,6 +2899,7 @@ dominance | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:24:19:24 | 1 | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | exit C2 | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | exit C2 | +| MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | MultiImplementationA.cs:21:27:21:29 | {...} | | MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationB.cs:20:11:20:25 | {...} | @@ -2911,6 +2911,7 @@ dominance | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion (normal) | | MultiImplementationA.cs:24:16:24:16 | access to property P | MultiImplementationA.cs:24:32:24:34 | ... = ... | | MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:24:34:24:34 | 0 | +| MultiImplementationA.cs:24:32:24:34 | ... = ... | MultiImplementationA.cs:20:22:20:31 | {...} | | MultiImplementationA.cs:24:34:24:34 | 0 | MultiImplementationA.cs:24:16:24:16 | access to property P | | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationA.cs:30:34:30:37 | null | | MultiImplementationA.cs:30:21:30:23 | exit get_P3 (abnormal) | MultiImplementationA.cs:30:21:30:23 | exit get_P3 | @@ -2948,6 +2949,7 @@ dominance | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationA.cs:8:16:8:16 | exit M (normal) | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:16:5:16 | exit M (normal) | | MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:11:20:11:20 | 1 | +| MultiImplementationB.cs:11:16:11:20 | ... = ... | MultiImplementationB.cs:22:16:22:16 | this access | | MultiImplementationB.cs:11:20:11:20 | 1 | MultiImplementationB.cs:11:16:11:20 | ... = ... | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | access to parameter i | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationB.cs:12:37:12:40 | null | @@ -2973,8 +2975,9 @@ dominance | MultiImplementationB.cs:16:9:16:31 | exit M2 (abnormal) | MultiImplementationB.cs:16:9:16:31 | exit M2 | | MultiImplementationB.cs:16:21:16:30 | throw ... | MultiImplementationB.cs:16:9:16:31 | exit M2 (abnormal) | | MultiImplementationB.cs:16:27:16:30 | null | MultiImplementationB.cs:16:21:16:30 | throw ... | -| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:13:16:13:16 | this access | -| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationB.cs:18:12:18:13 | call to constructor Object | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | | MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:18:30:18:33 | null | | MultiImplementationB.cs:18:24:18:34 | throw ...; | MultiImplementationA.cs:20:12:20:13 | exit C2 (abnormal) | | MultiImplementationB.cs:18:24:18:34 | throw ...; | MultiImplementationB.cs:18:12:18:13 | exit C2 (abnormal) | @@ -2983,6 +2986,7 @@ dominance | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:24:19:24 | 1 | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | exit C2 | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | exit C2 | +| MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | MultiImplementationB.cs:19:27:19:29 | {...} | | MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationB.cs:20:11:20:25 | {...} | @@ -2997,6 +3001,7 @@ dominance | MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationB.cs:21:50:21:59 | throw ... | | MultiImplementationB.cs:22:16:22:16 | access to property P | MultiImplementationB.cs:22:32:22:34 | ... = ... | | MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:22:34:22:34 | 1 | +| MultiImplementationB.cs:22:32:22:34 | ... = ... | MultiImplementationB.cs:18:22:18:36 | {...} | | MultiImplementationB.cs:22:34:22:34 | 1 | MultiImplementationB.cs:22:16:22:16 | access to property P | | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationA.cs:30:34:30:37 | null | | MultiImplementationB.cs:27:21:27:23 | exit get_P3 (abnormal) | MultiImplementationA.cs:30:21:30:23 | exit get_P3 | @@ -3061,6 +3066,28 @@ dominance | NullCoalescing.cs:17:13:17:19 | (...) ... | NullCoalescing.cs:17:13:17:24 | ... ?? ... | | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:17:9:17:24 | ... = ... | | NullCoalescing.cs:17:19:17:19 | access to parameter i | NullCoalescing.cs:17:13:17:19 | (...) ... | +| PartialImplementationA.cs:3:12:3:18 | call to constructor Object | PartialImplementationB.cs:3:16:3:16 | this access | +| PartialImplementationA.cs:3:12:3:18 | enter Partial | PartialImplementationA.cs:3:12:3:18 | call to constructor Object | +| PartialImplementationA.cs:3:12:3:18 | exit Partial (normal) | PartialImplementationA.cs:3:12:3:18 | exit Partial | +| PartialImplementationA.cs:3:27:3:29 | {...} | PartialImplementationA.cs:3:12:3:18 | exit Partial (normal) | +| PartialImplementationB.cs:3:16:3:16 | this access | PartialImplementationB.cs:3:20:3:20 | 0 | +| PartialImplementationB.cs:3:16:3:16 | this access | PartialImplementationB.cs:3:20:3:20 | 0 | +| PartialImplementationB.cs:3:16:3:20 | ... = ... | PartialImplementationB.cs:5:16:5:16 | this access | +| PartialImplementationB.cs:3:16:3:20 | ... = ... | PartialImplementationB.cs:5:16:5:16 | this access | +| PartialImplementationB.cs:3:20:3:20 | 0 | PartialImplementationB.cs:3:16:3:20 | ... = ... | +| PartialImplementationB.cs:3:20:3:20 | 0 | PartialImplementationB.cs:3:16:3:20 | ... = ... | +| PartialImplementationB.cs:4:12:4:18 | call to constructor Object | PartialImplementationB.cs:3:16:3:16 | this access | +| PartialImplementationB.cs:4:12:4:18 | enter Partial | PartialImplementationB.cs:4:12:4:18 | call to constructor Object | +| PartialImplementationB.cs:4:12:4:18 | exit Partial (normal) | PartialImplementationB.cs:4:12:4:18 | exit Partial | +| PartialImplementationB.cs:4:22:4:24 | {...} | PartialImplementationB.cs:4:12:4:18 | exit Partial (normal) | +| PartialImplementationB.cs:5:16:5:16 | access to property P | PartialImplementationB.cs:5:32:5:34 | ... = ... | +| PartialImplementationB.cs:5:16:5:16 | access to property P | PartialImplementationB.cs:5:32:5:34 | ... = ... | +| PartialImplementationB.cs:5:16:5:16 | this access | PartialImplementationB.cs:5:34:5:34 | 0 | +| PartialImplementationB.cs:5:16:5:16 | this access | PartialImplementationB.cs:5:34:5:34 | 0 | +| PartialImplementationB.cs:5:32:5:34 | ... = ... | PartialImplementationA.cs:3:27:3:29 | {...} | +| PartialImplementationB.cs:5:32:5:34 | ... = ... | PartialImplementationB.cs:4:22:4:24 | {...} | +| PartialImplementationB.cs:5:34:5:34 | 0 | PartialImplementationB.cs:5:16:5:16 | access to property P | +| PartialImplementationB.cs:5:34:5:34 | 0 | PartialImplementationB.cs:5:16:5:16 | access to property P | | Patterns.cs:5:10:5:11 | enter M1 | Patterns.cs:6:5:43:5 | {...} | | Patterns.cs:5:10:5:11 | exit M1 (normal) | Patterns.cs:5:10:5:11 | exit M1 | | Patterns.cs:6:5:43:5 | {...} | Patterns.cs:7:9:7:24 | ... ...; | @@ -3917,7 +3944,8 @@ dominance | cflow.cs:127:68:127:80 | ... = ... | cflow.cs:127:62:127:64 | exit set_Prop (normal) | | cflow.cs:127:68:127:81 | ...; | cflow.cs:127:68:127:72 | this access | | cflow.cs:127:76:127:80 | access to parameter value | cflow.cs:127:68:127:80 | ... = ... | -| cflow.cs:129:5:129:15 | enter ControlFlow | cflow.cs:130:5:132:5 | {...} | +| cflow.cs:129:5:129:15 | call to constructor Object | cflow.cs:130:5:132:5 | {...} | +| cflow.cs:129:5:129:15 | enter ControlFlow | cflow.cs:129:5:129:15 | call to constructor Object | | cflow.cs:129:5:129:15 | exit ControlFlow (normal) | cflow.cs:129:5:129:15 | exit ControlFlow | | cflow.cs:130:5:132:5 | {...} | cflow.cs:131:9:131:18 | ...; | | cflow.cs:131:9:131:13 | this access | cflow.cs:131:17:131:17 | access to parameter s | @@ -4278,7 +4306,8 @@ dominance | cflow.cs:291:38:291:38 | access to parameter f | cflow.cs:291:40:291:40 | 0 | | cflow.cs:291:38:291:41 | delegate call | cflow.cs:291:12:291:12 | exit M (normal) | | cflow.cs:291:40:291:40 | 0 | cflow.cs:291:38:291:41 | delegate call | -| cflow.cs:296:5:296:25 | enter NegationInConstructor | cflow.cs:296:52:296:54 | {...} | +| cflow.cs:296:5:296:25 | call to constructor Object | cflow.cs:296:52:296:54 | {...} | +| cflow.cs:296:5:296:25 | enter NegationInConstructor | cflow.cs:296:5:296:25 | call to constructor Object | | cflow.cs:296:5:296:25 | exit NegationInConstructor (normal) | cflow.cs:296:5:296:25 | exit NegationInConstructor | | cflow.cs:296:52:296:54 | {...} | cflow.cs:296:5:296:25 | exit NegationInConstructor (normal) | | cflow.cs:298:10:298:10 | enter M | cflow.cs:299:5:301:5 | {...} | @@ -6550,8 +6579,8 @@ postDominance | Foreach.cs:38:33:38:33 | Int32 y | Foreach.cs:38:26:38:26 | String x | | Foreach.cs:38:39:38:42 | access to parameter args | Foreach.cs:37:5:40:5 | {...} | | Foreach.cs:39:11:39:11 | ; | Foreach.cs:38:18:38:34 | (..., ...) | -| Initializers.cs:5:9:5:9 | this access | Initializers.cs:8:5:8:16 | enter Initializers | -| Initializers.cs:5:9:5:9 | this access | Initializers.cs:10:5:10:16 | enter Initializers | +| Initializers.cs:5:9:5:9 | this access | Initializers.cs:8:5:8:16 | call to constructor Object | +| Initializers.cs:5:9:5:9 | this access | Initializers.cs:10:5:10:16 | call to constructor Object | | Initializers.cs:5:9:5:17 | ... = ... | Initializers.cs:5:13:5:17 | ... + ... | | Initializers.cs:5:9:5:17 | ... = ... | Initializers.cs:5:13:5:17 | ... + ... | | Initializers.cs:5:13:5:13 | access to field H | Initializers.cs:5:9:5:9 | this access | @@ -6572,9 +6601,11 @@ postDominance | Initializers.cs:6:27:6:31 | ... + ... | Initializers.cs:6:31:6:31 | 2 | | Initializers.cs:6:31:6:31 | 2 | Initializers.cs:6:27:6:27 | access to field H | | Initializers.cs:6:31:6:31 | 2 | Initializers.cs:6:27:6:27 | access to field H | +| Initializers.cs:8:5:8:16 | call to constructor Object | Initializers.cs:8:5:8:16 | enter Initializers | | Initializers.cs:8:5:8:16 | exit Initializers | Initializers.cs:8:5:8:16 | exit Initializers (normal) | | Initializers.cs:8:5:8:16 | exit Initializers (normal) | Initializers.cs:8:20:8:22 | {...} | | Initializers.cs:8:20:8:22 | {...} | Initializers.cs:6:25:6:31 | ... = ... | +| Initializers.cs:10:5:10:16 | call to constructor Object | Initializers.cs:10:5:10:16 | enter Initializers | | Initializers.cs:10:5:10:16 | exit Initializers | Initializers.cs:10:5:10:16 | exit Initializers (normal) | | Initializers.cs:10:5:10:16 | exit Initializers (normal) | Initializers.cs:10:28:10:30 | {...} | | Initializers.cs:10:28:10:30 | {...} | Initializers.cs:6:25:6:31 | ... = ... | @@ -6603,19 +6634,13 @@ postDominance | Initializers.cs:20:11:20:23 | exit NoConstructor | Initializers.cs:20:11:20:23 | exit NoConstructor (normal) | | Initializers.cs:20:11:20:23 | exit NoConstructor (normal) | Initializers.cs:23:23:23:27 | ... = ... | | Initializers.cs:22:23:22:23 | this access | Initializers.cs:20:11:20:23 | enter NoConstructor | -| Initializers.cs:22:23:22:23 | this access | Initializers.cs:35:9:35:11 | enter Sub | -| Initializers.cs:22:23:22:27 | ... = ... | Initializers.cs:22:27:22:27 | 0 | | Initializers.cs:22:23:22:27 | ... = ... | Initializers.cs:22:27:22:27 | 0 | | Initializers.cs:22:27:22:27 | 0 | Initializers.cs:22:23:22:23 | this access | -| Initializers.cs:22:27:22:27 | 0 | Initializers.cs:22:23:22:23 | this access | -| Initializers.cs:23:23:23:23 | this access | Initializers.cs:22:23:22:27 | ... = ... | | Initializers.cs:23:23:23:23 | this access | Initializers.cs:22:23:22:27 | ... = ... | | Initializers.cs:23:23:23:27 | ... = ... | Initializers.cs:23:27:23:27 | 1 | -| Initializers.cs:23:23:23:27 | ... = ... | Initializers.cs:23:27:23:27 | 1 | | Initializers.cs:23:27:23:27 | 1 | Initializers.cs:23:23:23:23 | this access | -| Initializers.cs:23:27:23:27 | 1 | Initializers.cs:23:23:23:23 | this access | -| Initializers.cs:28:13:28:13 | this access | Initializers.cs:23:23:23:27 | ... = ... | | Initializers.cs:28:13:28:13 | this access | Initializers.cs:31:17:31:20 | call to constructor NoConstructor | +| Initializers.cs:28:13:28:13 | this access | Initializers.cs:35:9:35:11 | call to constructor NoConstructor | | Initializers.cs:28:13:28:17 | ... = ... | Initializers.cs:28:17:28:17 | 2 | | Initializers.cs:28:13:28:17 | ... = ... | Initializers.cs:28:17:28:17 | 2 | | Initializers.cs:28:17:28:17 | 2 | Initializers.cs:28:13:28:13 | this access | @@ -6636,6 +6661,7 @@ postDominance | Initializers.cs:33:31:33:35 | ... = ... | Initializers.cs:33:35:33:35 | access to parameter i | | Initializers.cs:33:31:33:36 | ...; | Initializers.cs:33:29:33:38 | {...} | | Initializers.cs:33:35:33:35 | access to parameter i | Initializers.cs:33:31:33:31 | this access | +| Initializers.cs:35:9:35:11 | call to constructor NoConstructor | Initializers.cs:35:9:35:11 | enter Sub | | Initializers.cs:35:9:35:11 | exit Sub | Initializers.cs:35:9:35:11 | exit Sub (normal) | | Initializers.cs:35:9:35:11 | exit Sub (normal) | Initializers.cs:35:29:35:37 | ... = ... | | Initializers.cs:35:27:35:40 | {...} | Initializers.cs:28:13:28:17 | ... = ... | @@ -6951,6 +6977,7 @@ postDominance | MultiImplementationA.cs:8:16:8:16 | exit M (abnormal) | MultiImplementationA.cs:8:23:8:32 | throw ... | | MultiImplementationA.cs:8:16:8:16 | exit M (normal) | MultiImplementationB.cs:5:23:5:23 | 2 | | MultiImplementationA.cs:8:23:8:32 | throw ... | MultiImplementationA.cs:8:29:8:32 | null | +| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | | MultiImplementationA.cs:13:16:13:20 | ... = ... | MultiImplementationA.cs:13:20:13:20 | 0 | | MultiImplementationA.cs:13:20:13:20 | 0 | MultiImplementationA.cs:13:16:13:16 | this access | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | enter get_Item | @@ -6975,10 +7002,11 @@ postDominance | MultiImplementationA.cs:18:9:18:22 | exit M2 | MultiImplementationA.cs:18:9:18:22 | exit M2 (normal) | | MultiImplementationA.cs:18:9:18:22 | exit M2 (normal) | MultiImplementationA.cs:18:21:18:21 | 0 | | MultiImplementationA.cs:18:21:18:21 | 0 | MultiImplementationA.cs:18:9:18:22 | enter M2 | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationA.cs:20:12:20:13 | enter C2 | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationB.cs:18:12:18:13 | enter C2 | | MultiImplementationA.cs:20:12:20:13 | exit C2 (abnormal) | MultiImplementationB.cs:18:24:18:34 | throw ...; | | MultiImplementationA.cs:20:12:20:13 | exit C2 (normal) | MultiImplementationA.cs:20:24:20:28 | ... = ... | | MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:24:32:24:34 | ... = ... | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationB.cs:22:32:22:34 | ... = ... | | MultiImplementationA.cs:20:24:20:24 | this access | MultiImplementationA.cs:20:24:20:29 | ...; | | MultiImplementationA.cs:20:24:20:28 | ... = ... | MultiImplementationA.cs:20:28:20:28 | access to parameter i | | MultiImplementationA.cs:20:24:20:29 | ...; | MultiImplementationA.cs:20:22:20:31 | {...} | @@ -6988,6 +7016,7 @@ postDominance | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationA.cs:21:27:21:29 | {...} | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationB.cs:19:27:19:29 | {...} | | MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | MultiImplementationA.cs:21:24:21:24 | 0 | +| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | | MultiImplementationA.cs:22:6:22:7 | exit ~C2 (abnormal) | MultiImplementationB.cs:20:13:20:23 | throw ...; | | MultiImplementationA.cs:22:6:22:7 | exit ~C2 (normal) | MultiImplementationA.cs:22:11:22:13 | {...} | | MultiImplementationA.cs:22:11:22:13 | {...} | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | @@ -6997,6 +7026,7 @@ postDominance | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | | MultiImplementationA.cs:24:16:24:16 | access to property P | MultiImplementationA.cs:24:34:24:34 | 0 | +| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:13:16:13:20 | ... = ... | | MultiImplementationA.cs:24:32:24:34 | ... = ... | MultiImplementationA.cs:24:16:24:16 | access to property P | | MultiImplementationA.cs:24:34:24:34 | 0 | MultiImplementationA.cs:24:16:24:16 | this access | | MultiImplementationA.cs:30:21:30:23 | exit get_P3 | MultiImplementationA.cs:30:21:30:23 | exit get_P3 (abnormal) | @@ -7032,6 +7062,7 @@ postDominance | MultiImplementationB.cs:5:16:5:16 | exit M (normal) | MultiImplementationB.cs:5:23:5:23 | 2 | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationA.cs:8:16:8:16 | enter M | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:16:5:16 | enter M | +| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | | MultiImplementationB.cs:11:16:11:20 | ... = ... | MultiImplementationB.cs:11:20:11:20 | 1 | | MultiImplementationB.cs:11:20:11:20 | 1 | MultiImplementationB.cs:11:16:11:16 | this access | | MultiImplementationB.cs:12:31:12:40 | exit get_Item (abnormal) | MultiImplementationB.cs:12:31:12:40 | throw ... | @@ -7056,6 +7087,7 @@ postDominance | MultiImplementationB.cs:16:27:16:30 | null | MultiImplementationB.cs:16:9:16:31 | enter M2 | | MultiImplementationB.cs:18:12:18:13 | exit C2 (abnormal) | MultiImplementationB.cs:18:24:18:34 | throw ...; | | MultiImplementationB.cs:18:12:18:13 | exit C2 (normal) | MultiImplementationA.cs:20:24:20:28 | ... = ... | +| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:22:32:22:34 | ... = ... | | MultiImplementationB.cs:18:24:18:34 | throw ...; | MultiImplementationB.cs:18:30:18:33 | null | | MultiImplementationB.cs:18:30:18:33 | null | MultiImplementationB.cs:18:22:18:36 | {...} | | MultiImplementationB.cs:19:12:19:13 | exit C2 | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | @@ -7063,6 +7095,7 @@ postDominance | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationA.cs:21:27:21:29 | {...} | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationB.cs:19:27:19:29 | {...} | | MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | MultiImplementationB.cs:19:24:19:24 | 1 | +| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | | MultiImplementationB.cs:20:6:20:7 | exit ~C2 (abnormal) | MultiImplementationB.cs:20:13:20:23 | throw ...; | | MultiImplementationB.cs:20:6:20:7 | exit ~C2 (normal) | MultiImplementationA.cs:22:11:22:13 | {...} | | MultiImplementationB.cs:20:13:20:23 | throw ...; | MultiImplementationB.cs:20:19:20:22 | null | @@ -7071,6 +7104,7 @@ postDominance | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion (normal) | MultiImplementationA.cs:23:50:23:53 | null | | MultiImplementationB.cs:21:50:21:59 | throw ... | MultiImplementationB.cs:21:56:21:59 | null | | MultiImplementationB.cs:22:16:22:16 | access to property P | MultiImplementationB.cs:22:34:22:34 | 1 | +| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:11:16:11:20 | ... = ... | | MultiImplementationB.cs:22:32:22:34 | ... = ... | MultiImplementationB.cs:22:16:22:16 | access to property P | | MultiImplementationB.cs:22:34:22:34 | 1 | MultiImplementationB.cs:22:16:22:16 | this access | | MultiImplementationB.cs:27:21:27:23 | exit get_P3 | MultiImplementationA.cs:30:21:30:23 | exit get_P3 (abnormal) | @@ -7134,6 +7168,28 @@ postDominance | NullCoalescing.cs:17:13:17:19 | (...) ... | NullCoalescing.cs:17:19:17:19 | access to parameter i | | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:17:13:17:19 | (...) ... | | NullCoalescing.cs:17:19:17:19 | access to parameter i | NullCoalescing.cs:17:9:17:25 | ...; | +| PartialImplementationA.cs:3:12:3:18 | call to constructor Object | PartialImplementationA.cs:3:12:3:18 | enter Partial | +| PartialImplementationA.cs:3:12:3:18 | exit Partial | PartialImplementationA.cs:3:12:3:18 | exit Partial (normal) | +| PartialImplementationA.cs:3:12:3:18 | exit Partial (normal) | PartialImplementationA.cs:3:27:3:29 | {...} | +| PartialImplementationA.cs:3:27:3:29 | {...} | PartialImplementationB.cs:5:32:5:34 | ... = ... | +| PartialImplementationB.cs:3:16:3:16 | this access | PartialImplementationA.cs:3:12:3:18 | call to constructor Object | +| PartialImplementationB.cs:3:16:3:16 | this access | PartialImplementationB.cs:4:12:4:18 | call to constructor Object | +| PartialImplementationB.cs:3:16:3:20 | ... = ... | PartialImplementationB.cs:3:20:3:20 | 0 | +| PartialImplementationB.cs:3:16:3:20 | ... = ... | PartialImplementationB.cs:3:20:3:20 | 0 | +| PartialImplementationB.cs:3:20:3:20 | 0 | PartialImplementationB.cs:3:16:3:16 | this access | +| PartialImplementationB.cs:3:20:3:20 | 0 | PartialImplementationB.cs:3:16:3:16 | this access | +| PartialImplementationB.cs:4:12:4:18 | call to constructor Object | PartialImplementationB.cs:4:12:4:18 | enter Partial | +| PartialImplementationB.cs:4:12:4:18 | exit Partial | PartialImplementationB.cs:4:12:4:18 | exit Partial (normal) | +| PartialImplementationB.cs:4:12:4:18 | exit Partial (normal) | PartialImplementationB.cs:4:22:4:24 | {...} | +| PartialImplementationB.cs:4:22:4:24 | {...} | PartialImplementationB.cs:5:32:5:34 | ... = ... | +| PartialImplementationB.cs:5:16:5:16 | access to property P | PartialImplementationB.cs:5:34:5:34 | 0 | +| PartialImplementationB.cs:5:16:5:16 | access to property P | PartialImplementationB.cs:5:34:5:34 | 0 | +| PartialImplementationB.cs:5:16:5:16 | this access | PartialImplementationB.cs:3:16:3:20 | ... = ... | +| PartialImplementationB.cs:5:16:5:16 | this access | PartialImplementationB.cs:3:16:3:20 | ... = ... | +| PartialImplementationB.cs:5:32:5:34 | ... = ... | PartialImplementationB.cs:5:16:5:16 | access to property P | +| PartialImplementationB.cs:5:32:5:34 | ... = ... | PartialImplementationB.cs:5:16:5:16 | access to property P | +| PartialImplementationB.cs:5:34:5:34 | 0 | PartialImplementationB.cs:5:16:5:16 | this access | +| PartialImplementationB.cs:5:34:5:34 | 0 | PartialImplementationB.cs:5:16:5:16 | this access | | Patterns.cs:5:10:5:11 | exit M1 | Patterns.cs:5:10:5:11 | exit M1 (normal) | | Patterns.cs:5:10:5:11 | exit M1 (normal) | Patterns.cs:40:17:40:17 | access to local variable o | | Patterns.cs:6:5:43:5 | {...} | Patterns.cs:5:10:5:11 | enter M1 | @@ -7948,9 +8004,10 @@ postDominance | cflow.cs:127:68:127:80 | ... = ... | cflow.cs:127:76:127:80 | access to parameter value | | cflow.cs:127:68:127:81 | ...; | cflow.cs:127:66:127:83 | {...} | | cflow.cs:127:76:127:80 | access to parameter value | cflow.cs:127:68:127:72 | this access | +| cflow.cs:129:5:129:15 | call to constructor Object | cflow.cs:129:5:129:15 | enter ControlFlow | | cflow.cs:129:5:129:15 | exit ControlFlow | cflow.cs:129:5:129:15 | exit ControlFlow (normal) | | cflow.cs:129:5:129:15 | exit ControlFlow (normal) | cflow.cs:131:9:131:17 | ... = ... | -| cflow.cs:130:5:132:5 | {...} | cflow.cs:129:5:129:15 | enter ControlFlow | +| cflow.cs:130:5:132:5 | {...} | cflow.cs:129:5:129:15 | call to constructor Object | | cflow.cs:131:9:131:13 | this access | cflow.cs:131:9:131:18 | ...; | | cflow.cs:131:9:131:17 | ... = ... | cflow.cs:131:17:131:17 | access to parameter s | | cflow.cs:131:9:131:18 | ...; | cflow.cs:130:5:132:5 | {...} | @@ -8301,9 +8358,10 @@ postDominance | cflow.cs:291:38:291:38 | access to parameter f | cflow.cs:291:12:291:12 | enter M | | cflow.cs:291:38:291:41 | delegate call | cflow.cs:291:40:291:40 | 0 | | cflow.cs:291:40:291:40 | 0 | cflow.cs:291:38:291:38 | access to parameter f | +| cflow.cs:296:5:296:25 | call to constructor Object | cflow.cs:296:5:296:25 | enter NegationInConstructor | | cflow.cs:296:5:296:25 | exit NegationInConstructor | cflow.cs:296:5:296:25 | exit NegationInConstructor (normal) | | cflow.cs:296:5:296:25 | exit NegationInConstructor (normal) | cflow.cs:296:52:296:54 | {...} | -| cflow.cs:296:52:296:54 | {...} | cflow.cs:296:5:296:25 | enter NegationInConstructor | +| cflow.cs:296:52:296:54 | {...} | cflow.cs:296:5:296:25 | call to constructor Object | | cflow.cs:298:10:298:10 | exit M | cflow.cs:298:10:298:10 | exit M (normal) | | cflow.cs:298:10:298:10 | exit M (normal) | cflow.cs:300:9:300:72 | object creation of type NegationInConstructor | | cflow.cs:299:5:301:5 | {...} | cflow.cs:298:10:298:10 | enter M | @@ -11366,7 +11424,6 @@ blockDominance | MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationA.cs:8:16:8:16 | exit M | | MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationB.cs:5:16:5:16 | exit M | | MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationA.cs:8:29:8:32 | null | -| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:13:16:13:16 | this access | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | access to parameter i | | MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | access to parameter i | | MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | enter get_Item | @@ -11404,31 +11461,24 @@ blockDominance | MultiImplementationA.cs:16:17:16:18 | exit M1 (normal) | MultiImplementationB.cs:14:17:14:18 | exit M1 (normal) | | MultiImplementationA.cs:17:5:19:5 | {...} | MultiImplementationA.cs:17:5:19:5 | {...} | | MultiImplementationA.cs:18:9:18:22 | enter M2 | MultiImplementationA.cs:18:9:18:22 | enter M2 | -| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:13:16:13:16 | this access | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | enter C2 | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | -| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:22:20:31 | {...} | -| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:24:16:24:16 | this access | -| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | enter C2 | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | -| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:22:18:36 | {...} | -| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:22:16:22:16 | this access | | MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | | MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:22:20:31 | {...} | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | enter C2 | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:24:21:24 | 0 | -| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:27:21:29 | {...} | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | enter C2 | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:24:19:24 | 1 | -| MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:27:19:29 | {...} | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | | MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:24:21:24 | 0 | -| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationA.cs:21:27:21:29 | {...} | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | @@ -11447,7 +11497,6 @@ blockDominance | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationA.cs:23:50:23:53 | null | -| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:24:16:24:16 | this access | | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | | MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | enter M1 | @@ -11496,7 +11545,6 @@ blockDominance | MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationA.cs:8:16:8:16 | exit M | | MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationB.cs:5:16:5:16 | exit M | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:23:5:23 | 2 | -| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:11:16:11:16 | this access | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | access to parameter i | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | enter get_Item | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | exit get_Item | @@ -11534,31 +11582,24 @@ blockDominance | MultiImplementationB.cs:14:17:14:18 | exit M1 (normal) | MultiImplementationB.cs:14:17:14:18 | exit M1 (normal) | | MultiImplementationB.cs:15:5:17:5 | {...} | MultiImplementationB.cs:15:5:17:5 | {...} | | MultiImplementationB.cs:16:9:16:31 | enter M2 | MultiImplementationB.cs:16:9:16:31 | enter M2 | -| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:13:16:13:16 | this access | +| MultiImplementationB.cs:18:12:18:13 | call to constructor Object | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | enter C2 | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | -| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:22:20:31 | {...} | -| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:24:16:24:16 | this access | -| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | enter C2 | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | -| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:22:18:36 | {...} | -| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:22:16:22:16 | this access | | MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | | MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | -| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:18:22:18:36 | {...} | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | enter C2 | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:24:21:24 | 0 | -| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:27:21:29 | {...} | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | enter C2 | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:24:19:24 | 1 | -| MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:27:19:29 | {...} | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | | MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:24:19:24 | 1 | -| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationB.cs:19:27:19:29 | {...} | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | @@ -11577,7 +11618,6 @@ blockDominance | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | | MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationB.cs:21:56:21:59 | null | -| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:22:16:22:16 | this access | | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | | MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | enter M1 | @@ -11673,6 +11713,8 @@ blockDominance | NullCoalescing.cs:16:17:16:25 | ... ?? ... | NullCoalescing.cs:16:17:16:25 | ... ?? ... | | NullCoalescing.cs:16:17:16:25 | ... ?? ... | NullCoalescing.cs:17:13:17:24 | ... ?? ... | | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:17:13:17:24 | ... ?? ... | +| PartialImplementationA.cs:3:12:3:18 | enter Partial | PartialImplementationA.cs:3:12:3:18 | enter Partial | +| PartialImplementationB.cs:4:12:4:18 | enter Partial | PartialImplementationB.cs:4:12:4:18 | enter Partial | | Patterns.cs:5:10:5:11 | enter M1 | Patterns.cs:5:10:5:11 | enter M1 | | Patterns.cs:5:10:5:11 | enter M1 | Patterns.cs:8:13:8:23 | [false] ... is ... | | Patterns.cs:5:10:5:11 | enter M1 | Patterns.cs:8:13:8:23 | [true] ... is ... | @@ -14949,7 +14991,6 @@ postBlockDominance | MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationA.cs:8:16:8:16 | exit M | | MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationB.cs:5:16:5:16 | exit M | | MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationA.cs:8:29:8:32 | null | -| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:13:16:13:16 | this access | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | access to parameter i | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | enter get_Item | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationB.cs:12:31:12:40 | enter get_Item | @@ -14983,29 +15024,22 @@ postBlockDominance | MultiImplementationA.cs:16:17:16:18 | exit M1 (normal) | MultiImplementationB.cs:15:5:17:5 | {...} | | MultiImplementationA.cs:17:5:19:5 | {...} | MultiImplementationA.cs:17:5:19:5 | {...} | | MultiImplementationA.cs:18:9:18:22 | enter M2 | MultiImplementationA.cs:18:9:18:22 | enter M2 | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationA.cs:20:12:20:13 | enter C2 | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationB.cs:18:12:18:13 | enter C2 | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | enter C2 | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | enter C2 | | MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | | MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:13:16:13:16 | this access | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:12:20:13 | enter C2 | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:22:20:31 | {...} | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:24:16:24:16 | this access | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationB.cs:11:16:11:16 | this access | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationB.cs:18:12:18:13 | enter C2 | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationB.cs:22:16:22:16 | this access | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | enter C2 | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | enter C2 | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | enter C2 | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationA.cs:21:24:21:24 | 0 | -| MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationA.cs:21:27:21:29 | {...} | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | enter C2 | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationB.cs:19:24:19:24 | 1 | -| MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationB.cs:19:27:19:29 | {...} | | MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:24:21:24 | 0 | -| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationA.cs:21:27:21:29 | {...} | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | @@ -15020,7 +15054,6 @@ postBlockDominance | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationA.cs:23:28:23:35 | enter implicit conversion | | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationA.cs:23:50:23:53 | null | | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationB.cs:21:28:21:35 | enter implicit conversion | -| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:24:16:24:16 | this access | | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | | MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | enter M1 | @@ -15057,7 +15090,6 @@ postBlockDominance | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationA.cs:8:16:8:16 | enter M | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:16:5:16 | enter M | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:23:5:23 | 2 | -| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:11:16:11:16 | this access | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | enter get_Item | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationB.cs:12:31:12:40 | enter get_Item | | MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | exit get_Item | @@ -15087,23 +15119,20 @@ postBlockDominance | MultiImplementationB.cs:14:17:14:18 | exit M1 (normal) | MultiImplementationB.cs:15:5:17:5 | {...} | | MultiImplementationB.cs:15:5:17:5 | {...} | MultiImplementationB.cs:15:5:17:5 | {...} | | MultiImplementationB.cs:16:9:16:31 | enter M2 | MultiImplementationB.cs:16:9:16:31 | enter M2 | +| MultiImplementationB.cs:18:12:18:13 | call to constructor Object | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | enter C2 | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | enter C2 | | MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | exit C2 | | MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | exit C2 | -| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:18:22:18:36 | {...} | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | enter C2 | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | enter C2 | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | enter C2 | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationA.cs:21:24:21:24 | 0 | -| MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationA.cs:21:27:21:29 | {...} | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | enter C2 | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationB.cs:19:24:19:24 | 1 | -| MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationB.cs:19:27:19:29 | {...} | | MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:24:19:24 | 1 | -| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationB.cs:19:27:19:29 | {...} | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | | MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | @@ -15114,7 +15143,6 @@ postBlockDominance | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | | MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationB.cs:21:56:21:59 | null | -| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:22:16:22:16 | this access | | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | | MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | enter M1 | @@ -15210,6 +15238,8 @@ postBlockDominance | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:15:31:15:31 | 0 | | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:16:17:16:25 | ... ?? ... | | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:17:13:17:24 | ... ?? ... | +| PartialImplementationA.cs:3:12:3:18 | enter Partial | PartialImplementationA.cs:3:12:3:18 | enter Partial | +| PartialImplementationB.cs:4:12:4:18 | enter Partial | PartialImplementationB.cs:4:12:4:18 | enter Partial | | Patterns.cs:5:10:5:11 | enter M1 | Patterns.cs:5:10:5:11 | enter M1 | | Patterns.cs:8:13:8:23 | [false] ... is ... | Patterns.cs:8:13:8:23 | [false] ... is ... | | Patterns.cs:8:13:8:23 | [true] ... is ... | Patterns.cs:8:13:8:23 | [true] ... is ... | diff --git a/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected b/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected index 6df7f49dc63..ab190057a83 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected @@ -2675,10 +2675,12 @@ nodeEnclosing | Initializers.cs:6:27:6:31 | ... + ... | Initializers.cs:10:5:10:16 | Initializers | | Initializers.cs:6:31:6:31 | 2 | Initializers.cs:8:5:8:16 | Initializers | | Initializers.cs:6:31:6:31 | 2 | Initializers.cs:10:5:10:16 | Initializers | +| Initializers.cs:8:5:8:16 | call to constructor Object | Initializers.cs:8:5:8:16 | Initializers | | Initializers.cs:8:5:8:16 | enter Initializers | Initializers.cs:8:5:8:16 | Initializers | | Initializers.cs:8:5:8:16 | exit Initializers | Initializers.cs:8:5:8:16 | Initializers | | Initializers.cs:8:5:8:16 | exit Initializers (normal) | Initializers.cs:8:5:8:16 | Initializers | | Initializers.cs:8:20:8:22 | {...} | Initializers.cs:8:5:8:16 | Initializers | +| Initializers.cs:10:5:10:16 | call to constructor Object | Initializers.cs:10:5:10:16 | Initializers | | Initializers.cs:10:5:10:16 | enter Initializers | Initializers.cs:10:5:10:16 | Initializers | | Initializers.cs:10:5:10:16 | exit Initializers | Initializers.cs:10:5:10:16 | Initializers | | Initializers.cs:10:5:10:16 | exit Initializers (normal) | Initializers.cs:10:5:10:16 | Initializers | @@ -2709,17 +2711,11 @@ nodeEnclosing | Initializers.cs:20:11:20:23 | exit NoConstructor | Initializers.cs:20:11:20:23 | NoConstructor | | Initializers.cs:20:11:20:23 | exit NoConstructor (normal) | Initializers.cs:20:11:20:23 | NoConstructor | | Initializers.cs:22:23:22:23 | this access | Initializers.cs:20:11:20:23 | NoConstructor | -| Initializers.cs:22:23:22:23 | this access | Initializers.cs:35:9:35:11 | Sub | | Initializers.cs:22:23:22:27 | ... = ... | Initializers.cs:20:11:20:23 | NoConstructor | -| Initializers.cs:22:23:22:27 | ... = ... | Initializers.cs:35:9:35:11 | Sub | | Initializers.cs:22:27:22:27 | 0 | Initializers.cs:20:11:20:23 | NoConstructor | -| Initializers.cs:22:27:22:27 | 0 | Initializers.cs:35:9:35:11 | Sub | | Initializers.cs:23:23:23:23 | this access | Initializers.cs:20:11:20:23 | NoConstructor | -| Initializers.cs:23:23:23:23 | this access | Initializers.cs:35:9:35:11 | Sub | | Initializers.cs:23:23:23:27 | ... = ... | Initializers.cs:20:11:20:23 | NoConstructor | -| Initializers.cs:23:23:23:27 | ... = ... | Initializers.cs:35:9:35:11 | Sub | | Initializers.cs:23:27:23:27 | 1 | Initializers.cs:20:11:20:23 | NoConstructor | -| Initializers.cs:23:27:23:27 | 1 | Initializers.cs:35:9:35:11 | Sub | | Initializers.cs:28:13:28:13 | this access | Initializers.cs:31:9:31:11 | Sub | | Initializers.cs:28:13:28:13 | this access | Initializers.cs:35:9:35:11 | Sub | | Initializers.cs:28:13:28:17 | ... = ... | Initializers.cs:31:9:31:11 | Sub | @@ -2744,6 +2740,7 @@ nodeEnclosing | Initializers.cs:33:31:33:35 | ... = ... | Initializers.cs:33:9:33:11 | Sub | | Initializers.cs:33:31:33:36 | ...; | Initializers.cs:33:9:33:11 | Sub | | Initializers.cs:33:35:33:35 | access to parameter i | Initializers.cs:33:9:33:11 | Sub | +| Initializers.cs:35:9:35:11 | call to constructor NoConstructor | Initializers.cs:35:9:35:11 | Sub | | Initializers.cs:35:9:35:11 | enter Sub | Initializers.cs:35:9:35:11 | Sub | | Initializers.cs:35:9:35:11 | exit Sub | Initializers.cs:35:9:35:11 | Sub | | Initializers.cs:35:9:35:11 | exit Sub (normal) | Initializers.cs:35:9:35:11 | Sub | @@ -3167,6 +3164,8 @@ nodeEnclosing | MultiImplementationA.cs:18:9:18:22 | exit M2 | MultiImplementationA.cs:18:9:18:22 | M2 | | MultiImplementationA.cs:18:9:18:22 | exit M2 (normal) | MultiImplementationA.cs:18:9:18:22 | M2 | | MultiImplementationA.cs:18:21:18:21 | 0 | MultiImplementationA.cs:18:9:18:22 | M2 | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | C2 | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | C2 | @@ -3354,6 +3353,8 @@ nodeEnclosing | MultiImplementationB.cs:16:9:16:31 | exit M2 (abnormal) | MultiImplementationB.cs:16:9:16:31 | M2 | | MultiImplementationB.cs:16:21:16:30 | throw ... | MultiImplementationB.cs:16:9:16:31 | M2 | | MultiImplementationB.cs:16:27:16:30 | null | MultiImplementationB.cs:16:9:16:31 | M2 | +| MultiImplementationB.cs:18:12:18:13 | call to constructor Object | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationB.cs:18:12:18:13 | call to constructor Object | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | C2 | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | C2 | @@ -3497,6 +3498,30 @@ nodeEnclosing | NullCoalescing.cs:17:13:17:19 | (...) ... | NullCoalescing.cs:13:10:13:11 | M6 | | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:13:10:13:11 | M6 | | NullCoalescing.cs:17:19:17:19 | access to parameter i | NullCoalescing.cs:13:10:13:11 | M6 | +| PartialImplementationA.cs:3:12:3:18 | call to constructor Object | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationA.cs:3:12:3:18 | enter Partial | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationA.cs:3:12:3:18 | exit Partial | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationA.cs:3:12:3:18 | exit Partial (normal) | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationA.cs:3:27:3:29 | {...} | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationB.cs:3:16:3:16 | this access | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationB.cs:3:16:3:16 | this access | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:3:16:3:20 | ... = ... | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationB.cs:3:16:3:20 | ... = ... | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:3:20:3:20 | 0 | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationB.cs:3:20:3:20 | 0 | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:4:12:4:18 | call to constructor Object | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:4:12:4:18 | enter Partial | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:4:12:4:18 | exit Partial | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:4:12:4:18 | exit Partial (normal) | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:4:22:4:24 | {...} | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:5:16:5:16 | access to property P | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationB.cs:5:16:5:16 | access to property P | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:5:16:5:16 | this access | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationB.cs:5:16:5:16 | this access | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:5:32:5:34 | ... = ... | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationB.cs:5:32:5:34 | ... = ... | PartialImplementationB.cs:4:12:4:18 | Partial | +| PartialImplementationB.cs:5:34:5:34 | 0 | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationB.cs:5:34:5:34 | 0 | PartialImplementationB.cs:4:12:4:18 | Partial | | Patterns.cs:5:10:5:11 | enter M1 | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:5:10:5:11 | exit M1 | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:5:10:5:11 | exit M1 (normal) | Patterns.cs:5:10:5:11 | M1 | @@ -4436,6 +4461,7 @@ nodeEnclosing | cflow.cs:127:68:127:80 | ... = ... | cflow.cs:127:62:127:64 | set_Prop | | cflow.cs:127:68:127:81 | ...; | cflow.cs:127:62:127:64 | set_Prop | | cflow.cs:127:76:127:80 | access to parameter value | cflow.cs:127:62:127:64 | set_Prop | +| cflow.cs:129:5:129:15 | call to constructor Object | cflow.cs:129:5:129:15 | ControlFlow | | cflow.cs:129:5:129:15 | enter ControlFlow | cflow.cs:129:5:129:15 | ControlFlow | | cflow.cs:129:5:129:15 | exit ControlFlow | cflow.cs:129:5:129:15 | ControlFlow | | cflow.cs:129:5:129:15 | exit ControlFlow (normal) | cflow.cs:129:5:129:15 | ControlFlow | @@ -4826,6 +4852,7 @@ nodeEnclosing | cflow.cs:291:38:291:38 | access to parameter f | cflow.cs:291:12:291:12 | M | | cflow.cs:291:38:291:41 | delegate call | cflow.cs:291:12:291:12 | M | | cflow.cs:291:40:291:40 | 0 | cflow.cs:291:12:291:12 | M | +| cflow.cs:296:5:296:25 | call to constructor Object | cflow.cs:296:5:296:25 | NegationInConstructor | | cflow.cs:296:5:296:25 | enter NegationInConstructor | cflow.cs:296:5:296:25 | NegationInConstructor | | cflow.cs:296:5:296:25 | exit NegationInConstructor | cflow.cs:296:5:296:25 | NegationInConstructor | | cflow.cs:296:5:296:25 | exit NegationInConstructor (normal) | cflow.cs:296:5:296:25 | NegationInConstructor | @@ -5601,8 +5628,6 @@ blockEnclosing | MultiImplementationA.cs:8:16:8:16 | exit M | MultiImplementationB.cs:5:16:5:16 | M | | MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationA.cs:8:16:8:16 | M | | MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationB.cs:5:16:5:16 | M | -| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:20:12:20:13 | C2 | -| MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | get_Item | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationB.cs:12:31:12:40 | get_Item | | MultiImplementationA.cs:14:31:14:31 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | get_Item | @@ -5628,20 +5653,18 @@ blockEnclosing | MultiImplementationA.cs:17:5:19:5 | {...} | MultiImplementationA.cs:16:17:16:18 | M1 | | MultiImplementationA.cs:17:5:19:5 | {...} | MultiImplementationB.cs:14:17:14:18 | M1 | | MultiImplementationA.cs:18:9:18:22 | enter M2 | MultiImplementationA.cs:18:9:18:22 | M2 | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | C2 | | MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | C2 | | MultiImplementationA.cs:20:12:20:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | C2 | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:12:20:13 | C2 | -| MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | C2 | | MultiImplementationA.cs:21:12:21:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | C2 | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | C2 | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | C2 | | MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:12:21:13 | C2 | | MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationB.cs:19:12:19:13 | C2 | -| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationA.cs:21:12:21:13 | C2 | -| MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationB.cs:19:12:19:13 | C2 | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | ~C2 | | MultiImplementationA.cs:22:6:22:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | ~C2 | | MultiImplementationA.cs:22:6:22:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | ~C2 | @@ -5654,8 +5677,6 @@ blockEnclosing | MultiImplementationA.cs:23:28:23:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | implicit conversion | | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationA.cs:23:28:23:35 | implicit conversion | | MultiImplementationA.cs:23:50:23:53 | null | MultiImplementationB.cs:21:28:21:35 | implicit conversion | -| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:20:12:20:13 | C2 | -| MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | get_P3 | | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | get_P3 | | MultiImplementationA.cs:36:9:36:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | M1 | @@ -5689,8 +5710,6 @@ blockEnclosing | MultiImplementationB.cs:5:16:5:16 | exit M | MultiImplementationB.cs:5:16:5:16 | M | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationA.cs:8:16:8:16 | M | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:16:5:16 | M | -| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationA.cs:20:12:20:13 | C2 | -| MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | get_Item | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationB.cs:12:31:12:40 | get_Item | | MultiImplementationB.cs:12:31:12:40 | exit get_Item | MultiImplementationA.cs:14:31:14:31 | get_Item | @@ -5716,20 +5735,18 @@ blockEnclosing | MultiImplementationB.cs:15:5:17:5 | {...} | MultiImplementationA.cs:16:17:16:18 | M1 | | MultiImplementationB.cs:15:5:17:5 | {...} | MultiImplementationB.cs:14:17:14:18 | M1 | | MultiImplementationB.cs:16:9:16:31 | enter M2 | MultiImplementationB.cs:16:9:16:31 | M2 | +| MultiImplementationB.cs:18:12:18:13 | call to constructor Object | MultiImplementationA.cs:20:12:20:13 | C2 | +| MultiImplementationB.cs:18:12:18:13 | call to constructor Object | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | C2 | | MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationA.cs:20:12:20:13 | C2 | | MultiImplementationB.cs:18:12:18:13 | exit C2 | MultiImplementationB.cs:18:12:18:13 | C2 | -| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationA.cs:20:12:20:13 | C2 | -| MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationA.cs:21:12:21:13 | C2 | | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:12:19:13 | C2 | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | C2 | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | C2 | | MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationA.cs:21:12:21:13 | C2 | | MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:12:19:13 | C2 | -| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationA.cs:21:12:21:13 | C2 | -| MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationB.cs:19:12:19:13 | C2 | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationA.cs:22:6:22:7 | ~C2 | | MultiImplementationB.cs:20:6:20:7 | enter ~C2 | MultiImplementationB.cs:20:6:20:7 | ~C2 | | MultiImplementationB.cs:20:6:20:7 | exit ~C2 | MultiImplementationA.cs:22:6:22:7 | ~C2 | @@ -5742,8 +5759,6 @@ blockEnclosing | MultiImplementationB.cs:21:28:21:35 | exit implicit conversion | MultiImplementationB.cs:21:28:21:35 | implicit conversion | | MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationA.cs:23:28:23:35 | implicit conversion | | MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationB.cs:21:28:21:35 | implicit conversion | -| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationA.cs:20:12:20:13 | C2 | -| MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:18:12:18:13 | C2 | | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationA.cs:30:21:30:23 | get_P3 | | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationB.cs:27:21:27:23 | get_P3 | | MultiImplementationB.cs:32:9:32:10 | enter M1 | MultiImplementationA.cs:36:9:36:10 | M1 | @@ -5789,6 +5804,8 @@ blockEnclosing | NullCoalescing.cs:15:31:15:31 | 0 | NullCoalescing.cs:13:10:13:11 | M6 | | NullCoalescing.cs:16:17:16:25 | ... ?? ... | NullCoalescing.cs:13:10:13:11 | M6 | | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:13:10:13:11 | M6 | +| PartialImplementationA.cs:3:12:3:18 | enter Partial | PartialImplementationA.cs:3:12:3:18 | Partial | +| PartialImplementationB.cs:4:12:4:18 | enter Partial | PartialImplementationB.cs:4:12:4:18 | Partial | | Patterns.cs:5:10:5:11 | enter M1 | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:8:13:8:23 | [false] ... is ... | Patterns.cs:5:10:5:11 | M1 | | Patterns.cs:8:13:8:23 | [true] ... is ... | Patterns.cs:5:10:5:11 | M1 | diff --git a/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected b/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected index 28fd165909c..4a8935d09b5 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected @@ -1687,7 +1687,9 @@ | Initializers.cs:6:27:6:27 | access to field H | Initializers.cs:6:27:6:27 | access to field H | | Initializers.cs:6:27:6:31 | ... + ... | Initializers.cs:6:27:6:27 | access to field H | | Initializers.cs:6:31:6:31 | 2 | Initializers.cs:6:31:6:31 | 2 | +| Initializers.cs:8:5:8:16 | call to constructor Object | Initializers.cs:8:5:8:16 | call to constructor Object | | Initializers.cs:8:20:8:22 | {...} | Initializers.cs:8:20:8:22 | {...} | +| Initializers.cs:10:5:10:16 | call to constructor Object | Initializers.cs:10:5:10:16 | call to constructor Object | | Initializers.cs:10:28:10:30 | {...} | Initializers.cs:10:28:10:30 | {...} | | Initializers.cs:13:5:16:5 | {...} | Initializers.cs:13:5:16:5 | {...} | | Initializers.cs:14:9:14:54 | ... ...; | Initializers.cs:14:9:14:54 | ... ...; | @@ -1735,6 +1737,7 @@ | Initializers.cs:33:31:33:35 | ... = ... | Initializers.cs:33:31:33:31 | this access | | Initializers.cs:33:31:33:36 | ...; | Initializers.cs:33:31:33:36 | ...; | | Initializers.cs:33:35:33:35 | access to parameter i | Initializers.cs:33:35:33:35 | access to parameter i | +| Initializers.cs:35:9:35:11 | call to constructor NoConstructor | Initializers.cs:35:9:35:11 | call to constructor NoConstructor | | Initializers.cs:35:27:35:40 | {...} | Initializers.cs:35:27:35:40 | {...} | | Initializers.cs:35:29:35:29 | access to field I | Initializers.cs:35:29:35:29 | this access | | Initializers.cs:35:29:35:29 | this access | Initializers.cs:35:29:35:29 | this access | @@ -2045,6 +2048,7 @@ | MultiImplementationA.cs:17:5:19:5 | {...} | MultiImplementationA.cs:17:5:19:5 | {...} | | MultiImplementationA.cs:18:9:18:22 | M2(...) | MultiImplementationA.cs:18:9:18:22 | M2(...) | | MultiImplementationA.cs:18:21:18:21 | 0 | MultiImplementationA.cs:18:21:18:21 | 0 | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | | MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:22:20:31 | {...} | | MultiImplementationA.cs:20:24:20:24 | access to field F | MultiImplementationA.cs:20:24:20:24 | this access | | MultiImplementationA.cs:20:24:20:24 | this access | MultiImplementationA.cs:20:24:20:24 | this access | @@ -2089,6 +2093,7 @@ | MultiImplementationB.cs:16:9:16:31 | M2(...) | MultiImplementationB.cs:16:9:16:31 | M2(...) | | MultiImplementationB.cs:16:21:16:30 | throw ... | MultiImplementationB.cs:16:27:16:30 | null | | MultiImplementationB.cs:16:27:16:30 | null | MultiImplementationB.cs:16:27:16:30 | null | +| MultiImplementationB.cs:18:12:18:13 | call to constructor Object | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | | MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:18:22:18:36 | {...} | | MultiImplementationB.cs:18:24:18:34 | throw ...; | MultiImplementationB.cs:18:30:18:33 | null | | MultiImplementationB.cs:18:30:18:33 | null | MultiImplementationB.cs:18:30:18:33 | null | @@ -2153,6 +2158,18 @@ | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:17:19:17:19 | access to parameter i | | NullCoalescing.cs:17:19:17:19 | access to parameter i | NullCoalescing.cs:17:19:17:19 | access to parameter i | | NullCoalescing.cs:17:24:17:24 | 1 | NullCoalescing.cs:17:24:17:24 | 1 | +| PartialImplementationA.cs:3:12:3:18 | call to constructor Object | PartialImplementationA.cs:3:12:3:18 | call to constructor Object | +| PartialImplementationA.cs:3:27:3:29 | {...} | PartialImplementationA.cs:3:27:3:29 | {...} | +| PartialImplementationB.cs:3:16:3:16 | access to field F | PartialImplementationB.cs:3:16:3:16 | this access | +| PartialImplementationB.cs:3:16:3:16 | this access | PartialImplementationB.cs:3:16:3:16 | this access | +| PartialImplementationB.cs:3:16:3:20 | ... = ... | PartialImplementationB.cs:3:16:3:16 | this access | +| PartialImplementationB.cs:3:20:3:20 | 0 | PartialImplementationB.cs:3:20:3:20 | 0 | +| PartialImplementationB.cs:4:12:4:18 | call to constructor Object | PartialImplementationB.cs:4:12:4:18 | call to constructor Object | +| PartialImplementationB.cs:4:22:4:24 | {...} | PartialImplementationB.cs:4:22:4:24 | {...} | +| PartialImplementationB.cs:5:16:5:16 | access to property P | PartialImplementationB.cs:5:16:5:16 | this access | +| PartialImplementationB.cs:5:16:5:16 | this access | PartialImplementationB.cs:5:16:5:16 | this access | +| PartialImplementationB.cs:5:32:5:34 | ... = ... | PartialImplementationB.cs:5:16:5:16 | this access | +| PartialImplementationB.cs:5:34:5:34 | 0 | PartialImplementationB.cs:5:34:5:34 | 0 | | Patterns.cs:6:5:43:5 | {...} | Patterns.cs:6:5:43:5 | {...} | | Patterns.cs:7:9:7:24 | ... ...; | Patterns.cs:7:9:7:24 | ... ...; | | Patterns.cs:7:16:7:23 | Object o = ... | Patterns.cs:7:20:7:23 | null | @@ -2933,6 +2950,7 @@ | cflow.cs:127:68:127:80 | ... = ... | cflow.cs:127:68:127:72 | this access | | cflow.cs:127:68:127:81 | ...; | cflow.cs:127:68:127:81 | ...; | | cflow.cs:127:76:127:80 | access to parameter value | cflow.cs:127:76:127:80 | access to parameter value | +| cflow.cs:129:5:129:15 | call to constructor Object | cflow.cs:129:5:129:15 | call to constructor Object | | cflow.cs:130:5:132:5 | {...} | cflow.cs:130:5:132:5 | {...} | | cflow.cs:131:9:131:13 | access to field Field | cflow.cs:131:9:131:13 | this access | | cflow.cs:131:9:131:13 | this access | cflow.cs:131:9:131:13 | this access | @@ -3267,6 +3285,7 @@ | cflow.cs:291:38:291:38 | access to parameter f | cflow.cs:291:38:291:38 | access to parameter f | | cflow.cs:291:38:291:41 | delegate call | cflow.cs:291:38:291:38 | access to parameter f | | cflow.cs:291:40:291:40 | 0 | cflow.cs:291:40:291:40 | 0 | +| cflow.cs:296:5:296:25 | call to constructor Object | cflow.cs:296:5:296:25 | call to constructor Object | | cflow.cs:296:52:296:54 | {...} | cflow.cs:296:52:296:54 | {...} | | cflow.cs:299:5:301:5 | {...} | cflow.cs:299:5:301:5 | {...} | | cflow.cs:300:9:300:72 | object creation of type NegationInConstructor | cflow.cs:300:38:300:38 | 0 | diff --git a/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected b/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected index df6d8eb416f..0207253e3f9 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected @@ -2325,7 +2325,9 @@ | Initializers.cs:6:27:6:27 | access to field H | Initializers.cs:6:27:6:27 | access to field H | normal | | Initializers.cs:6:27:6:31 | ... + ... | Initializers.cs:6:27:6:31 | ... + ... | normal | | Initializers.cs:6:31:6:31 | 2 | Initializers.cs:6:31:6:31 | 2 | normal | +| Initializers.cs:8:5:8:16 | call to constructor Object | Initializers.cs:8:5:8:16 | call to constructor Object | normal | | Initializers.cs:8:20:8:22 | {...} | Initializers.cs:8:20:8:22 | {...} | normal | +| Initializers.cs:10:5:10:16 | call to constructor Object | Initializers.cs:10:5:10:16 | call to constructor Object | normal | | Initializers.cs:10:28:10:30 | {...} | Initializers.cs:10:28:10:30 | {...} | normal | | Initializers.cs:13:5:16:5 | {...} | Initializers.cs:15:13:15:63 | Initializers[] iz = ... | normal | | Initializers.cs:14:9:14:54 | ... ...; | Initializers.cs:14:13:14:53 | Initializers i = ... | normal | @@ -2373,6 +2375,7 @@ | Initializers.cs:33:31:33:35 | ... = ... | Initializers.cs:33:31:33:35 | ... = ... | normal | | Initializers.cs:33:31:33:36 | ...; | Initializers.cs:33:31:33:35 | ... = ... | normal | | Initializers.cs:33:35:33:35 | access to parameter i | Initializers.cs:33:35:33:35 | access to parameter i | normal | +| Initializers.cs:35:9:35:11 | call to constructor NoConstructor | Initializers.cs:35:9:35:11 | call to constructor NoConstructor | normal | | Initializers.cs:35:27:35:40 | {...} | Initializers.cs:35:29:35:37 | ... = ... | normal | | Initializers.cs:35:29:35:29 | access to field I | Initializers.cs:35:29:35:29 | this access | normal | | Initializers.cs:35:29:35:29 | this access | Initializers.cs:35:29:35:29 | this access | normal | @@ -2697,6 +2700,7 @@ | MultiImplementationA.cs:17:5:19:5 | {...} | MultiImplementationA.cs:18:9:18:22 | M2(...) | normal | | MultiImplementationA.cs:18:9:18:22 | M2(...) | MultiImplementationA.cs:18:9:18:22 | M2(...) | normal | | MultiImplementationA.cs:18:21:18:21 | 0 | MultiImplementationA.cs:18:21:18:21 | 0 | normal | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | normal | | MultiImplementationA.cs:20:22:20:31 | {...} | MultiImplementationA.cs:20:24:20:28 | ... = ... | normal | | MultiImplementationA.cs:20:24:20:24 | access to field F | MultiImplementationA.cs:20:24:20:24 | this access | normal | | MultiImplementationA.cs:20:24:20:24 | this access | MultiImplementationA.cs:20:24:20:24 | this access | normal | @@ -2741,6 +2745,7 @@ | MultiImplementationB.cs:16:9:16:31 | M2(...) | MultiImplementationB.cs:16:9:16:31 | M2(...) | normal | | MultiImplementationB.cs:16:21:16:30 | throw ... | MultiImplementationB.cs:16:21:16:30 | throw ... | throw(NullReferenceException) | | MultiImplementationB.cs:16:27:16:30 | null | MultiImplementationB.cs:16:27:16:30 | null | normal | +| MultiImplementationB.cs:18:12:18:13 | call to constructor Object | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | normal | | MultiImplementationB.cs:18:22:18:36 | {...} | MultiImplementationB.cs:18:24:18:34 | throw ...; | throw(NullReferenceException) | | MultiImplementationB.cs:18:24:18:34 | throw ...; | MultiImplementationB.cs:18:24:18:34 | throw ...; | throw(NullReferenceException) | | MultiImplementationB.cs:18:30:18:33 | null | MultiImplementationB.cs:18:30:18:33 | null | normal | @@ -2821,6 +2826,18 @@ | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:17:13:17:24 | ... ?? ... | normal | | NullCoalescing.cs:17:19:17:19 | access to parameter i | NullCoalescing.cs:17:19:17:19 | access to parameter i | normal | | NullCoalescing.cs:17:24:17:24 | 1 | NullCoalescing.cs:17:24:17:24 | 1 | normal | +| PartialImplementationA.cs:3:12:3:18 | call to constructor Object | PartialImplementationA.cs:3:12:3:18 | call to constructor Object | normal | +| PartialImplementationA.cs:3:27:3:29 | {...} | PartialImplementationA.cs:3:27:3:29 | {...} | normal | +| PartialImplementationB.cs:3:16:3:16 | access to field F | PartialImplementationB.cs:3:16:3:16 | this access | normal | +| PartialImplementationB.cs:3:16:3:16 | this access | PartialImplementationB.cs:3:16:3:16 | this access | normal | +| PartialImplementationB.cs:3:16:3:20 | ... = ... | PartialImplementationB.cs:3:16:3:20 | ... = ... | normal | +| PartialImplementationB.cs:3:20:3:20 | 0 | PartialImplementationB.cs:3:20:3:20 | 0 | normal | +| PartialImplementationB.cs:4:12:4:18 | call to constructor Object | PartialImplementationB.cs:4:12:4:18 | call to constructor Object | normal | +| PartialImplementationB.cs:4:22:4:24 | {...} | PartialImplementationB.cs:4:22:4:24 | {...} | normal | +| PartialImplementationB.cs:5:16:5:16 | access to property P | PartialImplementationB.cs:5:16:5:16 | this access | normal | +| PartialImplementationB.cs:5:16:5:16 | this access | PartialImplementationB.cs:5:16:5:16 | this access | normal | +| PartialImplementationB.cs:5:32:5:34 | ... = ... | PartialImplementationB.cs:5:32:5:34 | ... = ... | normal | +| PartialImplementationB.cs:5:34:5:34 | 0 | PartialImplementationB.cs:5:34:5:34 | 0 | normal | | Patterns.cs:6:5:43:5 | {...} | Patterns.cs:40:17:40:17 | access to local variable o | normal | | Patterns.cs:7:9:7:24 | ... ...; | Patterns.cs:7:16:7:23 | Object o = ... | normal | | Patterns.cs:7:16:7:23 | Object o = ... | Patterns.cs:7:16:7:23 | Object o = ... | normal | @@ -3868,6 +3885,7 @@ | cflow.cs:127:68:127:80 | ... = ... | cflow.cs:127:68:127:80 | ... = ... | normal | | cflow.cs:127:68:127:81 | ...; | cflow.cs:127:68:127:80 | ... = ... | normal | | cflow.cs:127:76:127:80 | access to parameter value | cflow.cs:127:76:127:80 | access to parameter value | normal | +| cflow.cs:129:5:129:15 | call to constructor Object | cflow.cs:129:5:129:15 | call to constructor Object | normal | | cflow.cs:130:5:132:5 | {...} | cflow.cs:131:9:131:17 | ... = ... | normal | | cflow.cs:131:9:131:13 | access to field Field | cflow.cs:131:9:131:13 | this access | normal | | cflow.cs:131:9:131:13 | this access | cflow.cs:131:9:131:13 | this access | normal | @@ -4272,6 +4290,7 @@ | cflow.cs:291:38:291:38 | access to parameter f | cflow.cs:291:38:291:38 | access to parameter f | normal | | cflow.cs:291:38:291:41 | delegate call | cflow.cs:291:38:291:41 | delegate call | normal | | cflow.cs:291:40:291:40 | 0 | cflow.cs:291:40:291:40 | 0 | normal | +| cflow.cs:296:5:296:25 | call to constructor Object | cflow.cs:296:5:296:25 | call to constructor Object | normal | | cflow.cs:296:52:296:54 | {...} | cflow.cs:296:52:296:54 | {...} | normal | | cflow.cs:299:5:301:5 | {...} | cflow.cs:300:9:300:72 | object creation of type NegationInConstructor | normal | | cflow.cs:300:9:300:72 | object creation of type NegationInConstructor | cflow.cs:300:9:300:72 | object creation of type NegationInConstructor | normal | diff --git a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected index 4ee65369ce6..2441e36f62d 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected @@ -2792,10 +2792,12 @@ | Initializers.cs:6:27:6:31 | ... + ... | Initializers.cs:6:9:6:9 | access to property G | semmle.label | successor | | Initializers.cs:6:31:6:31 | 2 | Initializers.cs:6:27:6:31 | ... + ... | semmle.label | successor | | Initializers.cs:6:31:6:31 | 2 | Initializers.cs:6:27:6:31 | ... + ... | semmle.label | successor | -| Initializers.cs:8:5:8:16 | enter Initializers | Initializers.cs:5:9:5:9 | this access | semmle.label | successor | +| Initializers.cs:8:5:8:16 | call to constructor Object | Initializers.cs:5:9:5:9 | this access | semmle.label | successor | +| Initializers.cs:8:5:8:16 | enter Initializers | Initializers.cs:8:5:8:16 | call to constructor Object | semmle.label | successor | | Initializers.cs:8:5:8:16 | exit Initializers (normal) | Initializers.cs:8:5:8:16 | exit Initializers | semmle.label | successor | | Initializers.cs:8:20:8:22 | {...} | Initializers.cs:8:5:8:16 | exit Initializers (normal) | semmle.label | successor | -| Initializers.cs:10:5:10:16 | enter Initializers | Initializers.cs:5:9:5:9 | this access | semmle.label | successor | +| Initializers.cs:10:5:10:16 | call to constructor Object | Initializers.cs:5:9:5:9 | this access | semmle.label | successor | +| Initializers.cs:10:5:10:16 | enter Initializers | Initializers.cs:10:5:10:16 | call to constructor Object | semmle.label | successor | | Initializers.cs:10:5:10:16 | exit Initializers (normal) | Initializers.cs:10:5:10:16 | exit Initializers | semmle.label | successor | | Initializers.cs:10:28:10:30 | {...} | Initializers.cs:10:5:10:16 | exit Initializers (normal) | semmle.label | successor | | Initializers.cs:12:10:12:10 | enter M | Initializers.cs:13:5:16:5 | {...} | semmle.label | successor | @@ -2823,16 +2825,10 @@ | Initializers.cs:20:11:20:23 | enter NoConstructor | Initializers.cs:22:23:22:23 | this access | semmle.label | successor | | Initializers.cs:20:11:20:23 | exit NoConstructor (normal) | Initializers.cs:20:11:20:23 | exit NoConstructor | semmle.label | successor | | Initializers.cs:22:23:22:23 | this access | Initializers.cs:22:27:22:27 | 0 | semmle.label | successor | -| Initializers.cs:22:23:22:23 | this access | Initializers.cs:22:27:22:27 | 0 | semmle.label | successor | -| Initializers.cs:22:23:22:27 | ... = ... | Initializers.cs:23:23:23:23 | this access | semmle.label | successor | | Initializers.cs:22:23:22:27 | ... = ... | Initializers.cs:23:23:23:23 | this access | semmle.label | successor | | Initializers.cs:22:27:22:27 | 0 | Initializers.cs:22:23:22:27 | ... = ... | semmle.label | successor | -| Initializers.cs:22:27:22:27 | 0 | Initializers.cs:22:23:22:27 | ... = ... | semmle.label | successor | -| Initializers.cs:23:23:23:23 | this access | Initializers.cs:23:27:23:27 | 1 | semmle.label | successor | | Initializers.cs:23:23:23:23 | this access | Initializers.cs:23:27:23:27 | 1 | semmle.label | successor | | Initializers.cs:23:23:23:27 | ... = ... | Initializers.cs:20:11:20:23 | exit NoConstructor (normal) | semmle.label | successor | -| Initializers.cs:23:23:23:27 | ... = ... | Initializers.cs:28:13:28:13 | this access | semmle.label | successor | -| Initializers.cs:23:27:23:27 | 1 | Initializers.cs:23:23:23:27 | ... = ... | semmle.label | successor | | Initializers.cs:23:27:23:27 | 1 | Initializers.cs:23:23:23:27 | ... = ... | semmle.label | successor | | Initializers.cs:28:13:28:13 | this access | Initializers.cs:28:17:28:17 | 2 | semmle.label | successor | | Initializers.cs:28:13:28:13 | this access | Initializers.cs:28:17:28:17 | 2 | semmle.label | successor | @@ -2856,7 +2852,8 @@ | Initializers.cs:33:31:33:35 | ... = ... | Initializers.cs:33:9:33:11 | exit Sub (normal) | semmle.label | successor | | Initializers.cs:33:31:33:36 | ...; | Initializers.cs:33:31:33:31 | this access | semmle.label | successor | | Initializers.cs:33:35:33:35 | access to parameter i | Initializers.cs:33:31:33:35 | ... = ... | semmle.label | successor | -| Initializers.cs:35:9:35:11 | enter Sub | Initializers.cs:22:23:22:23 | this access | semmle.label | successor | +| Initializers.cs:35:9:35:11 | call to constructor NoConstructor | Initializers.cs:28:13:28:13 | this access | semmle.label | successor | +| Initializers.cs:35:9:35:11 | enter Sub | Initializers.cs:35:9:35:11 | call to constructor NoConstructor | semmle.label | successor | | Initializers.cs:35:9:35:11 | exit Sub (normal) | Initializers.cs:35:9:35:11 | exit Sub | semmle.label | successor | | Initializers.cs:35:27:35:40 | {...} | Initializers.cs:35:29:35:38 | ...; | semmle.label | successor | | Initializers.cs:35:29:35:29 | this access | Initializers.cs:35:33:35:33 | access to parameter i | semmle.label | successor | @@ -3214,10 +3211,7 @@ | MultiImplementationA.cs:8:23:8:32 | throw ... | MultiImplementationB.cs:5:16:5:16 | exit M (abnormal) | semmle.label | exception(NullReferenceException) | | MultiImplementationA.cs:8:29:8:32 | null | MultiImplementationA.cs:8:23:8:32 | throw ... | semmle.label | successor | | MultiImplementationA.cs:13:16:13:16 | this access | MultiImplementationA.cs:13:20:13:20 | 0 | semmle.label | successor | -| MultiImplementationA.cs:13:16:13:20 | ... = ... | MultiImplementationA.cs:13:16:13:16 | this access | semmle.label | successor | | MultiImplementationA.cs:13:16:13:20 | ... = ... | MultiImplementationA.cs:24:16:24:16 | this access | semmle.label | successor | -| MultiImplementationA.cs:13:16:13:20 | ... = ... | MultiImplementationB.cs:11:16:11:16 | this access | semmle.label | successor | -| MultiImplementationA.cs:13:16:13:20 | ... = ... | MultiImplementationB.cs:22:16:22:16 | this access | semmle.label | successor | | MultiImplementationA.cs:13:20:13:20 | 0 | MultiImplementationA.cs:13:16:13:20 | ... = ... | semmle.label | successor | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationA.cs:14:31:14:31 | exit get_Item (normal) | semmle.label | successor | | MultiImplementationA.cs:14:31:14:31 | access to parameter i | MultiImplementationB.cs:12:31:12:40 | exit get_Item (normal) | semmle.label | successor | @@ -3253,8 +3247,9 @@ | MultiImplementationA.cs:18:9:18:22 | enter M2 | MultiImplementationA.cs:18:21:18:21 | 0 | semmle.label | successor | | MultiImplementationA.cs:18:9:18:22 | exit M2 (normal) | MultiImplementationA.cs:18:9:18:22 | exit M2 | semmle.label | successor | | MultiImplementationA.cs:18:21:18:21 | 0 | MultiImplementationA.cs:18:9:18:22 | exit M2 (normal) | semmle.label | successor | -| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:13:16:13:16 | this access | semmle.label | successor | -| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:11:16:11:16 | this access | semmle.label | successor | +| MultiImplementationA.cs:20:12:20:13 | call to constructor Object | MultiImplementationA.cs:13:16:13:16 | this access | semmle.label | successor | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | semmle.label | successor | +| MultiImplementationA.cs:20:12:20:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | semmle.label | successor | | MultiImplementationA.cs:20:12:20:13 | exit C2 (abnormal) | MultiImplementationA.cs:20:12:20:13 | exit C2 | semmle.label | successor | | MultiImplementationA.cs:20:12:20:13 | exit C2 (abnormal) | MultiImplementationB.cs:18:12:18:13 | exit C2 | semmle.label | successor | | MultiImplementationA.cs:20:12:20:13 | exit C2 (normal) | MultiImplementationA.cs:20:12:20:13 | exit C2 | semmle.label | successor | @@ -3270,7 +3265,6 @@ | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | exit C2 | semmle.label | successor | | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | exit C2 | semmle.label | successor | | MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | MultiImplementationA.cs:21:27:21:29 | {...} | semmle.label | successor | -| MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | MultiImplementationB.cs:19:27:19:29 | {...} | semmle.label | successor | | MultiImplementationA.cs:21:24:21:24 | 0 | MultiImplementationA.cs:21:19:21:22 | call to constructor C2 | semmle.label | successor | | MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | semmle.label | successor | | MultiImplementationA.cs:21:27:21:29 | {...} | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | semmle.label | successor | @@ -3293,9 +3287,6 @@ | MultiImplementationA.cs:24:16:24:16 | access to property P | MultiImplementationA.cs:24:32:24:34 | ... = ... | semmle.label | successor | | MultiImplementationA.cs:24:16:24:16 | this access | MultiImplementationA.cs:24:34:24:34 | 0 | semmle.label | successor | | MultiImplementationA.cs:24:32:24:34 | ... = ... | MultiImplementationA.cs:20:22:20:31 | {...} | semmle.label | successor | -| MultiImplementationA.cs:24:32:24:34 | ... = ... | MultiImplementationA.cs:24:16:24:16 | this access | semmle.label | successor | -| MultiImplementationA.cs:24:32:24:34 | ... = ... | MultiImplementationB.cs:18:22:18:36 | {...} | semmle.label | successor | -| MultiImplementationA.cs:24:32:24:34 | ... = ... | MultiImplementationB.cs:22:16:22:16 | this access | semmle.label | successor | | MultiImplementationA.cs:24:34:24:34 | 0 | MultiImplementationA.cs:24:16:24:16 | access to property P | semmle.label | successor | | MultiImplementationA.cs:30:21:30:23 | enter get_P3 | MultiImplementationA.cs:30:34:30:37 | null | semmle.label | successor | | MultiImplementationA.cs:30:21:30:23 | exit get_P3 (abnormal) | MultiImplementationA.cs:30:21:30:23 | exit get_P3 | semmle.label | successor | @@ -3353,9 +3344,6 @@ | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationA.cs:8:16:8:16 | exit M (normal) | semmle.label | successor | | MultiImplementationB.cs:5:23:5:23 | 2 | MultiImplementationB.cs:5:16:5:16 | exit M (normal) | semmle.label | successor | | MultiImplementationB.cs:11:16:11:16 | this access | MultiImplementationB.cs:11:20:11:20 | 1 | semmle.label | successor | -| MultiImplementationB.cs:11:16:11:20 | ... = ... | MultiImplementationA.cs:13:16:13:16 | this access | semmle.label | successor | -| MultiImplementationB.cs:11:16:11:20 | ... = ... | MultiImplementationA.cs:24:16:24:16 | this access | semmle.label | successor | -| MultiImplementationB.cs:11:16:11:20 | ... = ... | MultiImplementationB.cs:11:16:11:16 | this access | semmle.label | successor | | MultiImplementationB.cs:11:16:11:20 | ... = ... | MultiImplementationB.cs:22:16:22:16 | this access | semmle.label | successor | | MultiImplementationB.cs:11:20:11:20 | 1 | MultiImplementationB.cs:11:16:11:20 | ... = ... | semmle.label | successor | | MultiImplementationB.cs:12:31:12:40 | enter get_Item | MultiImplementationA.cs:14:31:14:31 | access to parameter i | semmle.label | successor | @@ -3394,8 +3382,9 @@ | MultiImplementationB.cs:16:9:16:31 | exit M2 (abnormal) | MultiImplementationB.cs:16:9:16:31 | exit M2 | semmle.label | successor | | MultiImplementationB.cs:16:21:16:30 | throw ... | MultiImplementationB.cs:16:9:16:31 | exit M2 (abnormal) | semmle.label | exception(NullReferenceException) | | MultiImplementationB.cs:16:27:16:30 | null | MultiImplementationB.cs:16:21:16:30 | throw ... | semmle.label | successor | -| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:13:16:13:16 | this access | semmle.label | successor | -| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:11:16:11:16 | this access | semmle.label | successor | +| MultiImplementationB.cs:18:12:18:13 | call to constructor Object | MultiImplementationB.cs:11:16:11:16 | this access | semmle.label | successor | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | semmle.label | successor | +| MultiImplementationB.cs:18:12:18:13 | enter C2 | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | semmle.label | successor | | MultiImplementationB.cs:18:12:18:13 | exit C2 (abnormal) | MultiImplementationA.cs:20:12:20:13 | exit C2 | semmle.label | successor | | MultiImplementationB.cs:18:12:18:13 | exit C2 (abnormal) | MultiImplementationB.cs:18:12:18:13 | exit C2 | semmle.label | successor | | MultiImplementationB.cs:18:12:18:13 | exit C2 (normal) | MultiImplementationA.cs:20:12:20:13 | exit C2 | semmle.label | successor | @@ -3408,7 +3397,6 @@ | MultiImplementationB.cs:19:12:19:13 | enter C2 | MultiImplementationB.cs:19:24:19:24 | 1 | semmle.label | successor | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationA.cs:21:12:21:13 | exit C2 | semmle.label | successor | | MultiImplementationB.cs:19:12:19:13 | exit C2 (normal) | MultiImplementationB.cs:19:12:19:13 | exit C2 | semmle.label | successor | -| MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | MultiImplementationA.cs:21:27:21:29 | {...} | semmle.label | successor | | MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | MultiImplementationB.cs:19:27:19:29 | {...} | semmle.label | successor | | MultiImplementationB.cs:19:24:19:24 | 1 | MultiImplementationB.cs:19:19:19:22 | call to constructor C2 | semmle.label | successor | | MultiImplementationB.cs:19:27:19:29 | {...} | MultiImplementationA.cs:21:12:21:13 | exit C2 (normal) | semmle.label | successor | @@ -3434,10 +3422,7 @@ | MultiImplementationB.cs:21:56:21:59 | null | MultiImplementationB.cs:21:50:21:59 | throw ... | semmle.label | successor | | MultiImplementationB.cs:22:16:22:16 | access to property P | MultiImplementationB.cs:22:32:22:34 | ... = ... | semmle.label | successor | | MultiImplementationB.cs:22:16:22:16 | this access | MultiImplementationB.cs:22:34:22:34 | 1 | semmle.label | successor | -| MultiImplementationB.cs:22:32:22:34 | ... = ... | MultiImplementationA.cs:20:22:20:31 | {...} | semmle.label | successor | -| MultiImplementationB.cs:22:32:22:34 | ... = ... | MultiImplementationA.cs:24:16:24:16 | this access | semmle.label | successor | | MultiImplementationB.cs:22:32:22:34 | ... = ... | MultiImplementationB.cs:18:22:18:36 | {...} | semmle.label | successor | -| MultiImplementationB.cs:22:32:22:34 | ... = ... | MultiImplementationB.cs:22:16:22:16 | this access | semmle.label | successor | | MultiImplementationB.cs:22:34:22:34 | 1 | MultiImplementationB.cs:22:16:22:16 | access to property P | semmle.label | successor | | MultiImplementationB.cs:27:21:27:23 | enter get_P3 | MultiImplementationA.cs:30:34:30:37 | null | semmle.label | successor | | MultiImplementationB.cs:27:21:27:23 | exit get_P3 (abnormal) | MultiImplementationA.cs:30:21:30:23 | exit get_P3 | semmle.label | successor | @@ -3523,6 +3508,28 @@ | NullCoalescing.cs:17:13:17:19 | (...) ... | NullCoalescing.cs:17:13:17:24 | ... ?? ... | semmle.label | non-null | | NullCoalescing.cs:17:13:17:24 | ... ?? ... | NullCoalescing.cs:17:9:17:24 | ... = ... | semmle.label | successor | | NullCoalescing.cs:17:19:17:19 | access to parameter i | NullCoalescing.cs:17:13:17:19 | (...) ... | semmle.label | successor | +| PartialImplementationA.cs:3:12:3:18 | call to constructor Object | PartialImplementationB.cs:3:16:3:16 | this access | semmle.label | successor | +| PartialImplementationA.cs:3:12:3:18 | enter Partial | PartialImplementationA.cs:3:12:3:18 | call to constructor Object | semmle.label | successor | +| PartialImplementationA.cs:3:12:3:18 | exit Partial (normal) | PartialImplementationA.cs:3:12:3:18 | exit Partial | semmle.label | successor | +| PartialImplementationA.cs:3:27:3:29 | {...} | PartialImplementationA.cs:3:12:3:18 | exit Partial (normal) | semmle.label | successor | +| PartialImplementationB.cs:3:16:3:16 | this access | PartialImplementationB.cs:3:20:3:20 | 0 | semmle.label | successor | +| PartialImplementationB.cs:3:16:3:16 | this access | PartialImplementationB.cs:3:20:3:20 | 0 | semmle.label | successor | +| PartialImplementationB.cs:3:16:3:20 | ... = ... | PartialImplementationB.cs:5:16:5:16 | this access | semmle.label | successor | +| PartialImplementationB.cs:3:16:3:20 | ... = ... | PartialImplementationB.cs:5:16:5:16 | this access | semmle.label | successor | +| PartialImplementationB.cs:3:20:3:20 | 0 | PartialImplementationB.cs:3:16:3:20 | ... = ... | semmle.label | successor | +| PartialImplementationB.cs:3:20:3:20 | 0 | PartialImplementationB.cs:3:16:3:20 | ... = ... | semmle.label | successor | +| PartialImplementationB.cs:4:12:4:18 | call to constructor Object | PartialImplementationB.cs:3:16:3:16 | this access | semmle.label | successor | +| PartialImplementationB.cs:4:12:4:18 | enter Partial | PartialImplementationB.cs:4:12:4:18 | call to constructor Object | semmle.label | successor | +| PartialImplementationB.cs:4:12:4:18 | exit Partial (normal) | PartialImplementationB.cs:4:12:4:18 | exit Partial | semmle.label | successor | +| PartialImplementationB.cs:4:22:4:24 | {...} | PartialImplementationB.cs:4:12:4:18 | exit Partial (normal) | semmle.label | successor | +| PartialImplementationB.cs:5:16:5:16 | access to property P | PartialImplementationB.cs:5:32:5:34 | ... = ... | semmle.label | successor | +| PartialImplementationB.cs:5:16:5:16 | access to property P | PartialImplementationB.cs:5:32:5:34 | ... = ... | semmle.label | successor | +| PartialImplementationB.cs:5:16:5:16 | this access | PartialImplementationB.cs:5:34:5:34 | 0 | semmle.label | successor | +| PartialImplementationB.cs:5:16:5:16 | this access | PartialImplementationB.cs:5:34:5:34 | 0 | semmle.label | successor | +| PartialImplementationB.cs:5:32:5:34 | ... = ... | PartialImplementationA.cs:3:27:3:29 | {...} | semmle.label | successor | +| PartialImplementationB.cs:5:32:5:34 | ... = ... | PartialImplementationB.cs:4:22:4:24 | {...} | semmle.label | successor | +| PartialImplementationB.cs:5:34:5:34 | 0 | PartialImplementationB.cs:5:16:5:16 | access to property P | semmle.label | successor | +| PartialImplementationB.cs:5:34:5:34 | 0 | PartialImplementationB.cs:5:16:5:16 | access to property P | semmle.label | successor | | Patterns.cs:5:10:5:11 | enter M1 | Patterns.cs:6:5:43:5 | {...} | semmle.label | successor | | Patterns.cs:5:10:5:11 | exit M1 (normal) | Patterns.cs:5:10:5:11 | exit M1 | semmle.label | successor | | Patterns.cs:6:5:43:5 | {...} | Patterns.cs:7:9:7:24 | ... ...; | semmle.label | successor | @@ -4501,7 +4508,8 @@ | cflow.cs:127:68:127:80 | ... = ... | cflow.cs:127:62:127:64 | exit set_Prop (normal) | semmle.label | successor | | cflow.cs:127:68:127:81 | ...; | cflow.cs:127:68:127:72 | this access | semmle.label | successor | | cflow.cs:127:76:127:80 | access to parameter value | cflow.cs:127:68:127:80 | ... = ... | semmle.label | successor | -| cflow.cs:129:5:129:15 | enter ControlFlow | cflow.cs:130:5:132:5 | {...} | semmle.label | successor | +| cflow.cs:129:5:129:15 | call to constructor Object | cflow.cs:130:5:132:5 | {...} | semmle.label | successor | +| cflow.cs:129:5:129:15 | enter ControlFlow | cflow.cs:129:5:129:15 | call to constructor Object | semmle.label | successor | | cflow.cs:129:5:129:15 | exit ControlFlow (normal) | cflow.cs:129:5:129:15 | exit ControlFlow | semmle.label | successor | | cflow.cs:130:5:132:5 | {...} | cflow.cs:131:9:131:18 | ...; | semmle.label | successor | | cflow.cs:131:9:131:13 | this access | cflow.cs:131:17:131:17 | access to parameter s | semmle.label | successor | @@ -4894,7 +4902,8 @@ | cflow.cs:291:38:291:38 | access to parameter f | cflow.cs:291:40:291:40 | 0 | semmle.label | successor | | cflow.cs:291:38:291:41 | delegate call | cflow.cs:291:12:291:12 | exit M (normal) | semmle.label | successor | | cflow.cs:291:40:291:40 | 0 | cflow.cs:291:38:291:41 | delegate call | semmle.label | successor | -| cflow.cs:296:5:296:25 | enter NegationInConstructor | cflow.cs:296:52:296:54 | {...} | semmle.label | successor | +| cflow.cs:296:5:296:25 | call to constructor Object | cflow.cs:296:52:296:54 | {...} | semmle.label | successor | +| cflow.cs:296:5:296:25 | enter NegationInConstructor | cflow.cs:296:5:296:25 | call to constructor Object | semmle.label | successor | | cflow.cs:296:5:296:25 | exit NegationInConstructor (normal) | cflow.cs:296:5:296:25 | exit NegationInConstructor | semmle.label | successor | | cflow.cs:296:52:296:54 | {...} | cflow.cs:296:5:296:25 | exit NegationInConstructor (normal) | semmle.label | successor | | cflow.cs:298:10:298:10 | enter M | cflow.cs:299:5:301:5 | {...} | semmle.label | successor | diff --git a/csharp/ql/test/library-tests/controlflow/graph/Nodes.expected b/csharp/ql/test/library-tests/controlflow/graph/Nodes.expected index 9085e248d03..8b158822831 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Nodes.expected +++ b/csharp/ql/test/library-tests/controlflow/graph/Nodes.expected @@ -1137,13 +1137,13 @@ entryPoint | Foreach.cs:24:10:24:11 | M4 | Foreach.cs:25:5:28:5 | {...} | | Foreach.cs:30:10:30:11 | M5 | Foreach.cs:31:5:34:5 | {...} | | Foreach.cs:36:10:36:11 | M6 | Foreach.cs:37:5:40:5 | {...} | -| Initializers.cs:8:5:8:16 | Initializers | Initializers.cs:5:9:5:9 | this access | -| Initializers.cs:10:5:10:16 | Initializers | Initializers.cs:5:9:5:9 | this access | +| Initializers.cs:8:5:8:16 | Initializers | Initializers.cs:8:5:8:16 | call to constructor Object | +| Initializers.cs:10:5:10:16 | Initializers | Initializers.cs:10:5:10:16 | call to constructor Object | | Initializers.cs:12:10:12:10 | M | Initializers.cs:13:5:16:5 | {...} | | Initializers.cs:20:11:20:23 | NoConstructor | Initializers.cs:22:23:22:23 | this access | | Initializers.cs:31:9:31:11 | Sub | Initializers.cs:31:17:31:20 | call to constructor NoConstructor | | Initializers.cs:33:9:33:11 | Sub | Initializers.cs:33:22:33:25 | call to constructor Sub | -| Initializers.cs:35:9:35:11 | Sub | Initializers.cs:22:23:22:23 | this access | +| Initializers.cs:35:9:35:11 | Sub | Initializers.cs:35:9:35:11 | call to constructor NoConstructor | | Initializers.cs:51:10:51:13 | Test | Initializers.cs:52:5:66:5 | {...} | | LoopUnrolling.cs:7:10:7:11 | M1 | LoopUnrolling.cs:8:5:13:5 | {...} | | LoopUnrolling.cs:15:10:15:11 | M2 | LoopUnrolling.cs:16:5:20:5 | {...} | @@ -1173,8 +1173,8 @@ entryPoint | MultiImplementationA.cs:16:17:16:18 | M1 | MultiImplementationA.cs:17:5:19:5 | {...} | | MultiImplementationA.cs:16:17:16:18 | M1 | MultiImplementationB.cs:15:5:17:5 | {...} | | MultiImplementationA.cs:18:9:18:22 | M2 | MultiImplementationA.cs:18:21:18:21 | 0 | -| MultiImplementationA.cs:20:12:20:13 | C2 | MultiImplementationA.cs:13:16:13:16 | this access | -| MultiImplementationA.cs:20:12:20:13 | C2 | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationA.cs:20:12:20:13 | C2 | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | +| MultiImplementationA.cs:20:12:20:13 | C2 | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | | MultiImplementationA.cs:21:12:21:13 | C2 | MultiImplementationA.cs:21:24:21:24 | 0 | | MultiImplementationA.cs:21:12:21:13 | C2 | MultiImplementationB.cs:19:24:19:24 | 1 | | MultiImplementationA.cs:22:6:22:7 | ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | @@ -1202,8 +1202,8 @@ entryPoint | MultiImplementationB.cs:14:17:14:18 | M1 | MultiImplementationA.cs:17:5:19:5 | {...} | | MultiImplementationB.cs:14:17:14:18 | M1 | MultiImplementationB.cs:15:5:17:5 | {...} | | MultiImplementationB.cs:16:9:16:31 | M2 | MultiImplementationB.cs:16:27:16:30 | null | -| MultiImplementationB.cs:18:12:18:13 | C2 | MultiImplementationA.cs:13:16:13:16 | this access | -| MultiImplementationB.cs:18:12:18:13 | C2 | MultiImplementationB.cs:11:16:11:16 | this access | +| MultiImplementationB.cs:18:12:18:13 | C2 | MultiImplementationA.cs:20:12:20:13 | call to constructor Object | +| MultiImplementationB.cs:18:12:18:13 | C2 | MultiImplementationB.cs:18:12:18:13 | call to constructor Object | | MultiImplementationB.cs:19:12:19:13 | C2 | MultiImplementationA.cs:21:24:21:24 | 0 | | MultiImplementationB.cs:19:12:19:13 | C2 | MultiImplementationB.cs:19:24:19:24 | 1 | | MultiImplementationB.cs:20:6:20:7 | ~C2 | MultiImplementationA.cs:22:11:22:13 | {...} | @@ -1219,6 +1219,8 @@ entryPoint | NullCoalescing.cs:9:12:9:13 | M4 | NullCoalescing.cs:9:37:9:37 | access to parameter b | | NullCoalescing.cs:11:9:11:10 | M5 | NullCoalescing.cs:11:44:11:45 | access to parameter b1 | | NullCoalescing.cs:13:10:13:11 | M6 | NullCoalescing.cs:14:5:18:5 | {...} | +| PartialImplementationA.cs:3:12:3:18 | Partial | PartialImplementationA.cs:3:12:3:18 | call to constructor Object | +| PartialImplementationB.cs:4:12:4:18 | Partial | PartialImplementationB.cs:4:12:4:18 | call to constructor Object | | Patterns.cs:5:10:5:11 | M1 | Patterns.cs:6:5:43:5 | {...} | | Patterns.cs:47:24:47:25 | M2 | Patterns.cs:48:9:48:9 | access to parameter c | | Patterns.cs:50:24:50:25 | M3 | Patterns.cs:51:9:51:9 | access to parameter c | @@ -1265,7 +1267,7 @@ entryPoint | cflow.cs:119:20:119:21 | M5 | cflow.cs:120:5:124:5 | {...} | | cflow.cs:127:19:127:21 | get_Prop | cflow.cs:127:23:127:60 | {...} | | cflow.cs:127:62:127:64 | set_Prop | cflow.cs:127:66:127:83 | {...} | -| cflow.cs:129:5:129:15 | ControlFlow | cflow.cs:130:5:132:5 | {...} | +| cflow.cs:129:5:129:15 | ControlFlow | cflow.cs:129:5:129:15 | call to constructor Object | | cflow.cs:134:5:134:15 | ControlFlow | cflow.cs:134:31:134:31 | access to parameter i | | cflow.cs:136:12:136:22 | ControlFlow | cflow.cs:136:33:136:33 | 0 | | cflow.cs:138:40:138:40 | + | cflow.cs:139:5:142:5 | {...} | @@ -1285,5 +1287,5 @@ entryPoint | cflow.cs:284:5:284:18 | ControlFlowSub | cflow.cs:284:32:284:35 | call to constructor ControlFlowSub | | cflow.cs:286:5:286:18 | ControlFlowSub | cflow.cs:286:34:286:34 | access to parameter i | | cflow.cs:291:12:291:12 | M | cflow.cs:291:38:291:38 | access to parameter f | -| cflow.cs:296:5:296:25 | NegationInConstructor | cflow.cs:296:52:296:54 | {...} | +| cflow.cs:296:5:296:25 | NegationInConstructor | cflow.cs:296:5:296:25 | call to constructor Object | | cflow.cs:298:10:298:10 | M | cflow.cs:299:5:301:5 | {...} | diff --git a/csharp/ql/test/library-tests/controlflow/graph/PartialImplementationA.cs b/csharp/ql/test/library-tests/controlflow/graph/PartialImplementationA.cs new file mode 100644 index 00000000000..e3061f38a6e --- /dev/null +++ b/csharp/ql/test/library-tests/controlflow/graph/PartialImplementationA.cs @@ -0,0 +1,4 @@ +partial class Partial +{ + public Partial(int i) { } +} diff --git a/csharp/ql/test/library-tests/controlflow/graph/PartialImplementationB.cs b/csharp/ql/test/library-tests/controlflow/graph/PartialImplementationB.cs new file mode 100644 index 00000000000..c5120b44cdd --- /dev/null +++ b/csharp/ql/test/library-tests/controlflow/graph/PartialImplementationB.cs @@ -0,0 +1,6 @@ +partial class Partial +{ + public int F = 0; + public Partial() { } + public int P { get; set; } = 0; +} diff --git a/csharp/ql/test/library-tests/controlflow/guards-large/GuardedExpr.expected b/csharp/ql/test/library-tests/controlflow/guards-large/GuardedExpr.expected new file mode 100644 index 00000000000..b390b5be639 --- /dev/null +++ b/csharp/ql/test/library-tests/controlflow/guards-large/GuardedExpr.expected @@ -0,0 +1,797 @@ +| GuardsStressTest.cs:8:26:8:27 | access to field ch | +| GuardsStressTest.cs:9:4:9:5 | access to field ch | +| GuardsStressTest.cs:9:17:9:18 | access to field ch | +| GuardsStressTest.cs:10:4:10:5 | access to field ch | +| GuardsStressTest.cs:11:4:11:5 | access to field ch | +| GuardsStressTest.cs:11:17:11:18 | access to field ch | +| GuardsStressTest.cs:12:4:12:5 | access to field ch | +| GuardsStressTest.cs:13:4:13:5 | access to field ch | +| GuardsStressTest.cs:14:4:14:5 | access to field ch | +| GuardsStressTest.cs:15:4:15:5 | access to field ch | +| GuardsStressTest.cs:15:17:15:18 | access to field ch | +| GuardsStressTest.cs:16:4:16:5 | access to field ch | +| GuardsStressTest.cs:16:17:16:18 | access to field ch | +| GuardsStressTest.cs:17:4:17:5 | access to field ch | +| GuardsStressTest.cs:17:17:17:18 | access to field ch | +| GuardsStressTest.cs:18:4:18:5 | access to field ch | +| GuardsStressTest.cs:18:17:18:18 | access to field ch | +| GuardsStressTest.cs:19:4:19:5 | access to field ch | +| GuardsStressTest.cs:19:17:19:18 | access to field ch | +| GuardsStressTest.cs:20:4:20:5 | access to field ch | +| GuardsStressTest.cs:21:4:21:5 | access to field ch | +| GuardsStressTest.cs:22:4:22:5 | access to field ch | +| GuardsStressTest.cs:22:17:22:18 | access to field ch | +| GuardsStressTest.cs:23:4:23:5 | access to field ch | +| GuardsStressTest.cs:23:17:23:18 | access to field ch | +| GuardsStressTest.cs:24:4:24:5 | access to field ch | +| GuardsStressTest.cs:24:17:24:18 | access to field ch | +| GuardsStressTest.cs:25:4:25:5 | access to field ch | +| GuardsStressTest.cs:26:4:26:5 | access to field ch | +| GuardsStressTest.cs:26:17:26:18 | access to field ch | +| GuardsStressTest.cs:27:4:27:5 | access to field ch | +| GuardsStressTest.cs:28:4:28:5 | access to field ch | +| GuardsStressTest.cs:28:17:28:18 | access to field ch | +| GuardsStressTest.cs:29:4:29:5 | access to field ch | +| GuardsStressTest.cs:29:17:29:18 | access to field ch | +| GuardsStressTest.cs:30:4:30:5 | access to field ch | +| GuardsStressTest.cs:30:18:30:19 | access to field ch | +| GuardsStressTest.cs:31:4:31:5 | access to field ch | +| GuardsStressTest.cs:31:18:31:19 | access to field ch | +| GuardsStressTest.cs:32:4:32:5 | access to field ch | +| GuardsStressTest.cs:32:18:32:19 | access to field ch | +| GuardsStressTest.cs:33:4:33:5 | access to field ch | +| GuardsStressTest.cs:33:18:33:19 | access to field ch | +| GuardsStressTest.cs:34:4:34:5 | access to field ch | +| GuardsStressTest.cs:35:4:35:5 | access to field ch | +| GuardsStressTest.cs:35:18:35:19 | access to field ch | +| GuardsStressTest.cs:36:4:36:5 | access to field ch | +| GuardsStressTest.cs:36:18:36:19 | access to field ch | +| GuardsStressTest.cs:37:4:37:5 | access to field ch | +| GuardsStressTest.cs:38:4:38:5 | access to field ch | +| GuardsStressTest.cs:38:18:38:19 | access to field ch | +| GuardsStressTest.cs:39:4:39:5 | access to field ch | +| GuardsStressTest.cs:39:18:39:19 | access to field ch | +| GuardsStressTest.cs:40:4:40:5 | access to field ch | +| GuardsStressTest.cs:41:4:41:5 | access to field ch | +| GuardsStressTest.cs:41:18:41:19 | access to field ch | +| GuardsStressTest.cs:42:4:42:5 | access to field ch | +| GuardsStressTest.cs:42:18:42:19 | access to field ch | +| GuardsStressTest.cs:43:4:43:5 | access to field ch | +| GuardsStressTest.cs:43:18:43:19 | access to field ch | +| GuardsStressTest.cs:44:4:44:5 | access to field ch | +| GuardsStressTest.cs:44:18:44:19 | access to field ch | +| GuardsStressTest.cs:45:4:45:5 | access to field ch | +| GuardsStressTest.cs:45:18:45:19 | access to field ch | +| GuardsStressTest.cs:46:4:46:5 | access to field ch | +| GuardsStressTest.cs:46:18:46:19 | access to field ch | +| GuardsStressTest.cs:47:4:47:5 | access to field ch | +| GuardsStressTest.cs:47:18:47:19 | access to field ch | +| GuardsStressTest.cs:48:4:48:5 | access to field ch | +| GuardsStressTest.cs:48:18:48:19 | access to field ch | +| GuardsStressTest.cs:49:4:49:5 | access to field ch | +| GuardsStressTest.cs:50:4:50:5 | access to field ch | +| GuardsStressTest.cs:50:18:50:19 | access to field ch | +| GuardsStressTest.cs:51:4:51:5 | access to field ch | +| GuardsStressTest.cs:51:18:51:19 | access to field ch | +| GuardsStressTest.cs:52:4:52:5 | access to field ch | +| GuardsStressTest.cs:52:18:52:19 | access to field ch | +| GuardsStressTest.cs:53:4:53:5 | access to field ch | +| GuardsStressTest.cs:54:4:54:5 | access to field ch | +| GuardsStressTest.cs:54:18:54:19 | access to field ch | +| GuardsStressTest.cs:55:4:55:5 | access to field ch | +| GuardsStressTest.cs:55:18:55:19 | access to field ch | +| GuardsStressTest.cs:56:4:56:5 | access to field ch | +| GuardsStressTest.cs:57:4:57:5 | access to field ch | +| GuardsStressTest.cs:57:18:57:19 | access to field ch | +| GuardsStressTest.cs:58:4:58:5 | access to field ch | +| GuardsStressTest.cs:58:18:58:19 | access to field ch | +| GuardsStressTest.cs:59:4:59:5 | access to field ch | +| GuardsStressTest.cs:59:18:59:19 | access to field ch | +| GuardsStressTest.cs:60:4:60:5 | access to field ch | +| GuardsStressTest.cs:60:18:60:19 | access to field ch | +| GuardsStressTest.cs:61:4:61:5 | access to field ch | +| GuardsStressTest.cs:61:18:61:19 | access to field ch | +| GuardsStressTest.cs:62:4:62:5 | access to field ch | +| GuardsStressTest.cs:62:18:62:19 | access to field ch | +| GuardsStressTest.cs:63:4:63:5 | access to field ch | +| GuardsStressTest.cs:63:18:63:19 | access to field ch | +| GuardsStressTest.cs:64:4:64:5 | access to field ch | +| GuardsStressTest.cs:64:18:64:19 | access to field ch | +| GuardsStressTest.cs:65:4:65:5 | access to field ch | +| GuardsStressTest.cs:65:18:65:19 | access to field ch | +| GuardsStressTest.cs:66:4:66:5 | access to field ch | +| GuardsStressTest.cs:66:18:66:19 | access to field ch | +| GuardsStressTest.cs:67:4:67:5 | access to field ch | +| GuardsStressTest.cs:67:18:67:19 | access to field ch | +| GuardsStressTest.cs:68:4:68:5 | access to field ch | +| GuardsStressTest.cs:69:4:69:5 | access to field ch | +| GuardsStressTest.cs:69:18:69:19 | access to field ch | +| GuardsStressTest.cs:70:4:70:5 | access to field ch | +| GuardsStressTest.cs:70:18:70:19 | access to field ch | +| GuardsStressTest.cs:71:4:71:5 | access to field ch | +| GuardsStressTest.cs:71:18:71:19 | access to field ch | +| GuardsStressTest.cs:72:4:72:5 | access to field ch | +| GuardsStressTest.cs:72:18:72:19 | access to field ch | +| GuardsStressTest.cs:73:4:73:5 | access to field ch | +| GuardsStressTest.cs:74:4:74:5 | access to field ch | +| GuardsStressTest.cs:74:18:74:19 | access to field ch | +| GuardsStressTest.cs:75:4:75:5 | access to field ch | +| GuardsStressTest.cs:75:18:75:19 | access to field ch | +| GuardsStressTest.cs:76:4:76:5 | access to field ch | +| GuardsStressTest.cs:76:18:76:19 | access to field ch | +| GuardsStressTest.cs:77:4:77:5 | access to field ch | +| GuardsStressTest.cs:77:18:77:19 | access to field ch | +| GuardsStressTest.cs:78:4:78:5 | access to field ch | +| GuardsStressTest.cs:78:18:78:19 | access to field ch | +| GuardsStressTest.cs:79:4:79:5 | access to field ch | +| GuardsStressTest.cs:79:18:79:19 | access to field ch | +| GuardsStressTest.cs:80:4:80:5 | access to field ch | +| GuardsStressTest.cs:80:18:80:19 | access to field ch | +| GuardsStressTest.cs:81:4:81:5 | access to field ch | +| GuardsStressTest.cs:81:18:81:19 | access to field ch | +| GuardsStressTest.cs:82:4:82:5 | access to field ch | +| GuardsStressTest.cs:82:18:82:19 | access to field ch | +| GuardsStressTest.cs:83:4:83:5 | access to field ch | +| GuardsStressTest.cs:83:18:83:19 | access to field ch | +| GuardsStressTest.cs:84:4:84:5 | access to field ch | +| GuardsStressTest.cs:84:18:84:19 | access to field ch | +| GuardsStressTest.cs:85:4:85:5 | access to field ch | +| GuardsStressTest.cs:86:4:86:5 | access to field ch | +| GuardsStressTest.cs:86:18:86:19 | access to field ch | +| GuardsStressTest.cs:87:4:87:5 | access to field ch | +| GuardsStressTest.cs:87:18:87:19 | access to field ch | +| GuardsStressTest.cs:88:4:88:5 | access to field ch | +| GuardsStressTest.cs:88:18:88:19 | access to field ch | +| GuardsStressTest.cs:89:4:89:5 | access to field ch | +| GuardsStressTest.cs:90:4:90:5 | access to field ch | +| GuardsStressTest.cs:90:18:90:19 | access to field ch | +| GuardsStressTest.cs:91:4:91:5 | access to field ch | +| GuardsStressTest.cs:92:4:92:5 | access to field ch | +| GuardsStressTest.cs:92:18:92:19 | access to field ch | +| GuardsStressTest.cs:93:4:93:5 | access to field ch | +| GuardsStressTest.cs:93:18:93:19 | access to field ch | +| GuardsStressTest.cs:94:4:94:5 | access to field ch | +| GuardsStressTest.cs:94:18:94:19 | access to field ch | +| GuardsStressTest.cs:95:4:95:5 | access to field ch | +| GuardsStressTest.cs:95:18:95:19 | access to field ch | +| GuardsStressTest.cs:96:4:96:5 | access to field ch | +| GuardsStressTest.cs:96:18:96:19 | access to field ch | +| GuardsStressTest.cs:97:4:97:5 | access to field ch | +| GuardsStressTest.cs:97:18:97:19 | access to field ch | +| GuardsStressTest.cs:98:4:98:5 | access to field ch | +| GuardsStressTest.cs:98:18:98:19 | access to field ch | +| GuardsStressTest.cs:99:4:99:5 | access to field ch | +| GuardsStressTest.cs:99:18:99:19 | access to field ch | +| GuardsStressTest.cs:100:4:100:5 | access to field ch | +| GuardsStressTest.cs:100:18:100:19 | access to field ch | +| GuardsStressTest.cs:101:4:101:5 | access to field ch | +| GuardsStressTest.cs:101:18:101:19 | access to field ch | +| GuardsStressTest.cs:102:4:102:5 | access to field ch | +| GuardsStressTest.cs:102:18:102:19 | access to field ch | +| GuardsStressTest.cs:103:4:103:5 | access to field ch | +| GuardsStressTest.cs:104:4:104:5 | access to field ch | +| GuardsStressTest.cs:104:18:104:19 | access to field ch | +| GuardsStressTest.cs:105:4:105:5 | access to field ch | +| GuardsStressTest.cs:105:18:105:19 | access to field ch | +| GuardsStressTest.cs:106:4:106:5 | access to field ch | +| GuardsStressTest.cs:106:18:106:19 | access to field ch | +| GuardsStressTest.cs:107:4:107:5 | access to field ch | +| GuardsStressTest.cs:107:18:107:19 | access to field ch | +| GuardsStressTest.cs:108:4:108:5 | access to field ch | +| GuardsStressTest.cs:108:18:108:19 | access to field ch | +| GuardsStressTest.cs:109:4:109:5 | access to field ch | +| GuardsStressTest.cs:109:18:109:19 | access to field ch | +| GuardsStressTest.cs:110:4:110:5 | access to field ch | +| GuardsStressTest.cs:110:18:110:19 | access to field ch | +| GuardsStressTest.cs:111:4:111:5 | access to field ch | +| GuardsStressTest.cs:111:18:111:19 | access to field ch | +| GuardsStressTest.cs:112:4:112:5 | access to field ch | +| GuardsStressTest.cs:112:18:112:19 | access to field ch | +| GuardsStressTest.cs:113:4:113:5 | access to field ch | +| GuardsStressTest.cs:113:18:113:19 | access to field ch | +| GuardsStressTest.cs:114:4:114:5 | access to field ch | +| GuardsStressTest.cs:114:18:114:19 | access to field ch | +| GuardsStressTest.cs:115:4:115:5 | access to field ch | +| GuardsStressTest.cs:115:18:115:19 | access to field ch | +| GuardsStressTest.cs:116:4:116:5 | access to field ch | +| GuardsStressTest.cs:116:18:116:19 | access to field ch | +| GuardsStressTest.cs:117:4:117:5 | access to field ch | +| GuardsStressTest.cs:117:18:117:19 | access to field ch | +| GuardsStressTest.cs:118:4:118:5 | access to field ch | +| GuardsStressTest.cs:118:18:118:19 | access to field ch | +| GuardsStressTest.cs:119:4:119:5 | access to field ch | +| GuardsStressTest.cs:119:18:119:19 | access to field ch | +| GuardsStressTest.cs:120:4:120:5 | access to field ch | +| GuardsStressTest.cs:121:4:121:5 | access to field ch | +| GuardsStressTest.cs:121:18:121:19 | access to field ch | +| GuardsStressTest.cs:122:4:122:5 | access to field ch | +| GuardsStressTest.cs:122:18:122:19 | access to field ch | +| GuardsStressTest.cs:123:4:123:5 | access to field ch | +| GuardsStressTest.cs:123:18:123:19 | access to field ch | +| GuardsStressTest.cs:124:4:124:5 | access to field ch | +| GuardsStressTest.cs:124:18:124:19 | access to field ch | +| GuardsStressTest.cs:125:4:125:5 | access to field ch | +| GuardsStressTest.cs:125:18:125:19 | access to field ch | +| GuardsStressTest.cs:126:4:126:5 | access to field ch | +| GuardsStressTest.cs:127:4:127:5 | access to field ch | +| GuardsStressTest.cs:127:18:127:19 | access to field ch | +| GuardsStressTest.cs:128:4:128:5 | access to field ch | +| GuardsStressTest.cs:128:18:128:19 | access to field ch | +| GuardsStressTest.cs:129:4:129:5 | access to field ch | +| GuardsStressTest.cs:129:18:129:19 | access to field ch | +| GuardsStressTest.cs:130:4:130:5 | access to field ch | +| GuardsStressTest.cs:130:18:130:19 | access to field ch | +| GuardsStressTest.cs:131:4:131:5 | access to field ch | +| GuardsStressTest.cs:131:18:131:19 | access to field ch | +| GuardsStressTest.cs:132:4:132:5 | access to field ch | +| GuardsStressTest.cs:132:18:132:19 | access to field ch | +| GuardsStressTest.cs:133:4:133:5 | access to field ch | +| GuardsStressTest.cs:133:18:133:19 | access to field ch | +| GuardsStressTest.cs:134:4:134:5 | access to field ch | +| GuardsStressTest.cs:135:4:135:5 | access to field ch | +| GuardsStressTest.cs:136:4:136:5 | access to field ch | +| GuardsStressTest.cs:136:18:136:19 | access to field ch | +| GuardsStressTest.cs:137:4:137:5 | access to field ch | +| GuardsStressTest.cs:137:18:137:19 | access to field ch | +| GuardsStressTest.cs:138:4:138:5 | access to field ch | +| GuardsStressTest.cs:138:18:138:19 | access to field ch | +| GuardsStressTest.cs:139:4:139:5 | access to field ch | +| GuardsStressTest.cs:139:18:139:19 | access to field ch | +| GuardsStressTest.cs:140:4:140:5 | access to field ch | +| GuardsStressTest.cs:140:18:140:19 | access to field ch | +| GuardsStressTest.cs:141:4:141:5 | access to field ch | +| GuardsStressTest.cs:141:18:141:19 | access to field ch | +| GuardsStressTest.cs:142:4:142:5 | access to field ch | +| GuardsStressTest.cs:142:18:142:19 | access to field ch | +| GuardsStressTest.cs:143:4:143:5 | access to field ch | +| GuardsStressTest.cs:143:18:143:19 | access to field ch | +| GuardsStressTest.cs:144:4:144:5 | access to field ch | +| GuardsStressTest.cs:144:18:144:19 | access to field ch | +| GuardsStressTest.cs:145:4:145:5 | access to field ch | +| GuardsStressTest.cs:145:18:145:19 | access to field ch | +| GuardsStressTest.cs:146:4:146:5 | access to field ch | +| GuardsStressTest.cs:146:18:146:19 | access to field ch | +| GuardsStressTest.cs:147:4:147:5 | access to field ch | +| GuardsStressTest.cs:147:18:147:19 | access to field ch | +| GuardsStressTest.cs:148:4:148:5 | access to field ch | +| GuardsStressTest.cs:148:18:148:19 | access to field ch | +| GuardsStressTest.cs:149:4:149:5 | access to field ch | +| GuardsStressTest.cs:149:18:149:19 | access to field ch | +| GuardsStressTest.cs:150:4:150:5 | access to field ch | +| GuardsStressTest.cs:150:18:150:19 | access to field ch | +| GuardsStressTest.cs:151:4:151:5 | access to field ch | +| GuardsStressTest.cs:151:18:151:19 | access to field ch | +| GuardsStressTest.cs:152:4:152:5 | access to field ch | +| GuardsStressTest.cs:152:18:152:19 | access to field ch | +| GuardsStressTest.cs:153:4:153:5 | access to field ch | +| GuardsStressTest.cs:153:18:153:19 | access to field ch | +| GuardsStressTest.cs:154:4:154:5 | access to field ch | +| GuardsStressTest.cs:154:18:154:19 | access to field ch | +| GuardsStressTest.cs:155:4:155:5 | access to field ch | +| GuardsStressTest.cs:155:18:155:19 | access to field ch | +| GuardsStressTest.cs:156:4:156:5 | access to field ch | +| GuardsStressTest.cs:156:18:156:19 | access to field ch | +| GuardsStressTest.cs:157:4:157:5 | access to field ch | +| GuardsStressTest.cs:157:18:157:19 | access to field ch | +| GuardsStressTest.cs:158:4:158:5 | access to field ch | +| GuardsStressTest.cs:158:18:158:19 | access to field ch | +| GuardsStressTest.cs:159:4:159:5 | access to field ch | +| GuardsStressTest.cs:159:18:159:19 | access to field ch | +| GuardsStressTest.cs:160:4:160:5 | access to field ch | +| GuardsStressTest.cs:161:4:161:5 | access to field ch | +| GuardsStressTest.cs:161:18:161:19 | access to field ch | +| GuardsStressTest.cs:162:4:162:5 | access to field ch | +| GuardsStressTest.cs:162:18:162:19 | access to field ch | +| GuardsStressTest.cs:163:4:163:5 | access to field ch | +| GuardsStressTest.cs:163:18:163:19 | access to field ch | +| GuardsStressTest.cs:164:4:164:5 | access to field ch | +| GuardsStressTest.cs:164:18:164:19 | access to field ch | +| GuardsStressTest.cs:165:4:165:5 | access to field ch | +| GuardsStressTest.cs:165:18:165:19 | access to field ch | +| GuardsStressTest.cs:166:4:166:5 | access to field ch | +| GuardsStressTest.cs:166:18:166:19 | access to field ch | +| GuardsStressTest.cs:167:4:167:5 | access to field ch | +| GuardsStressTest.cs:167:18:167:19 | access to field ch | +| GuardsStressTest.cs:168:4:168:5 | access to field ch | +| GuardsStressTest.cs:168:18:168:19 | access to field ch | +| GuardsStressTest.cs:169:4:169:5 | access to field ch | +| GuardsStressTest.cs:169:18:169:19 | access to field ch | +| GuardsStressTest.cs:170:4:170:5 | access to field ch | +| GuardsStressTest.cs:170:18:170:19 | access to field ch | +| GuardsStressTest.cs:171:4:171:5 | access to field ch | +| GuardsStressTest.cs:172:4:172:5 | access to field ch | +| GuardsStressTest.cs:172:18:172:19 | access to field ch | +| GuardsStressTest.cs:173:4:173:5 | access to field ch | +| GuardsStressTest.cs:173:18:173:19 | access to field ch | +| GuardsStressTest.cs:174:4:174:5 | access to field ch | +| GuardsStressTest.cs:174:18:174:19 | access to field ch | +| GuardsStressTest.cs:175:4:175:5 | access to field ch | +| GuardsStressTest.cs:175:18:175:19 | access to field ch | +| GuardsStressTest.cs:176:4:176:5 | access to field ch | +| GuardsStressTest.cs:176:18:176:19 | access to field ch | +| GuardsStressTest.cs:177:4:177:5 | access to field ch | +| GuardsStressTest.cs:177:18:177:19 | access to field ch | +| GuardsStressTest.cs:178:4:178:5 | access to field ch | +| GuardsStressTest.cs:178:18:178:19 | access to field ch | +| GuardsStressTest.cs:179:4:179:5 | access to field ch | +| GuardsStressTest.cs:180:4:180:5 | access to field ch | +| GuardsStressTest.cs:180:18:180:19 | access to field ch | +| GuardsStressTest.cs:181:4:181:5 | access to field ch | +| GuardsStressTest.cs:182:4:182:5 | access to field ch | +| GuardsStressTest.cs:182:18:182:19 | access to field ch | +| GuardsStressTest.cs:183:4:183:5 | access to field ch | +| GuardsStressTest.cs:184:4:184:5 | access to field ch | +| GuardsStressTest.cs:184:18:184:19 | access to field ch | +| GuardsStressTest.cs:185:4:185:5 | access to field ch | +| GuardsStressTest.cs:185:18:185:19 | access to field ch | +| GuardsStressTest.cs:186:4:186:5 | access to field ch | +| GuardsStressTest.cs:186:18:186:19 | access to field ch | +| GuardsStressTest.cs:187:4:187:5 | access to field ch | +| GuardsStressTest.cs:187:18:187:19 | access to field ch | +| GuardsStressTest.cs:188:4:188:5 | access to field ch | +| GuardsStressTest.cs:188:18:188:19 | access to field ch | +| GuardsStressTest.cs:189:4:189:5 | access to field ch | +| GuardsStressTest.cs:189:18:189:19 | access to field ch | +| GuardsStressTest.cs:190:4:190:5 | access to field ch | +| GuardsStressTest.cs:191:4:191:5 | access to field ch | +| GuardsStressTest.cs:191:18:191:19 | access to field ch | +| GuardsStressTest.cs:192:4:192:5 | access to field ch | +| GuardsStressTest.cs:193:4:193:5 | access to field ch | +| GuardsStressTest.cs:194:4:194:5 | access to field ch | +| GuardsStressTest.cs:194:18:194:19 | access to field ch | +| GuardsStressTest.cs:195:4:195:5 | access to field ch | +| GuardsStressTest.cs:195:18:195:19 | access to field ch | +| GuardsStressTest.cs:196:4:196:5 | access to field ch | +| GuardsStressTest.cs:196:18:196:19 | access to field ch | +| GuardsStressTest.cs:197:4:197:5 | access to field ch | +| GuardsStressTest.cs:198:4:198:5 | access to field ch | +| GuardsStressTest.cs:199:4:199:5 | access to field ch | +| GuardsStressTest.cs:199:18:199:19 | access to field ch | +| GuardsStressTest.cs:200:4:200:5 | access to field ch | +| GuardsStressTest.cs:200:18:200:19 | access to field ch | +| GuardsStressTest.cs:201:4:201:5 | access to field ch | +| GuardsStressTest.cs:201:18:201:19 | access to field ch | +| GuardsStressTest.cs:202:4:202:5 | access to field ch | +| GuardsStressTest.cs:202:18:202:19 | access to field ch | +| GuardsStressTest.cs:203:4:203:5 | access to field ch | +| GuardsStressTest.cs:204:4:204:5 | access to field ch | +| GuardsStressTest.cs:204:18:204:19 | access to field ch | +| GuardsStressTest.cs:205:4:205:5 | access to field ch | +| GuardsStressTest.cs:205:18:205:19 | access to field ch | +| GuardsStressTest.cs:206:4:206:5 | access to field ch | +| GuardsStressTest.cs:206:18:206:19 | access to field ch | +| GuardsStressTest.cs:207:4:207:5 | access to field ch | +| GuardsStressTest.cs:208:4:208:5 | access to field ch | +| GuardsStressTest.cs:208:18:208:19 | access to field ch | +| GuardsStressTest.cs:209:4:209:5 | access to field ch | +| GuardsStressTest.cs:209:18:209:19 | access to field ch | +| GuardsStressTest.cs:210:4:210:5 | access to field ch | +| GuardsStressTest.cs:211:4:211:5 | access to field ch | +| GuardsStressTest.cs:212:4:212:5 | access to field ch | +| GuardsStressTest.cs:213:4:213:5 | access to field ch | +| GuardsStressTest.cs:213:18:213:19 | access to field ch | +| GuardsStressTest.cs:214:4:214:5 | access to field ch | +| GuardsStressTest.cs:214:18:214:19 | access to field ch | +| GuardsStressTest.cs:215:4:215:5 | access to field ch | +| GuardsStressTest.cs:215:18:215:19 | access to field ch | +| GuardsStressTest.cs:216:4:216:5 | access to field ch | +| GuardsStressTest.cs:216:18:216:19 | access to field ch | +| GuardsStressTest.cs:217:4:217:5 | access to field ch | +| GuardsStressTest.cs:217:18:217:19 | access to field ch | +| GuardsStressTest.cs:218:4:218:5 | access to field ch | +| GuardsStressTest.cs:219:4:219:5 | access to field ch | +| GuardsStressTest.cs:219:18:219:19 | access to field ch | +| GuardsStressTest.cs:220:4:220:5 | access to field ch | +| GuardsStressTest.cs:220:18:220:19 | access to field ch | +| GuardsStressTest.cs:221:4:221:5 | access to field ch | +| GuardsStressTest.cs:221:18:221:19 | access to field ch | +| GuardsStressTest.cs:222:4:222:5 | access to field ch | +| GuardsStressTest.cs:223:4:223:5 | access to field ch | +| GuardsStressTest.cs:224:4:224:5 | access to field ch | +| GuardsStressTest.cs:224:18:224:19 | access to field ch | +| GuardsStressTest.cs:225:4:225:5 | access to field ch | +| GuardsStressTest.cs:225:18:225:19 | access to field ch | +| GuardsStressTest.cs:226:4:226:5 | access to field ch | +| GuardsStressTest.cs:226:18:226:19 | access to field ch | +| GuardsStressTest.cs:227:4:227:5 | access to field ch | +| GuardsStressTest.cs:227:18:227:19 | access to field ch | +| GuardsStressTest.cs:228:4:228:5 | access to field ch | +| GuardsStressTest.cs:229:4:229:5 | access to field ch | +| GuardsStressTest.cs:229:18:229:19 | access to field ch | +| GuardsStressTest.cs:230:4:230:5 | access to field ch | +| GuardsStressTest.cs:230:18:230:19 | access to field ch | +| GuardsStressTest.cs:231:4:231:5 | access to field ch | +| GuardsStressTest.cs:231:18:231:19 | access to field ch | +| GuardsStressTest.cs:232:4:232:5 | access to field ch | +| GuardsStressTest.cs:232:18:232:19 | access to field ch | +| GuardsStressTest.cs:233:4:233:5 | access to field ch | +| GuardsStressTest.cs:233:18:233:19 | access to field ch | +| GuardsStressTest.cs:234:4:234:5 | access to field ch | +| GuardsStressTest.cs:234:18:234:19 | access to field ch | +| GuardsStressTest.cs:235:4:235:5 | access to field ch | +| GuardsStressTest.cs:236:4:236:5 | access to field ch | +| GuardsStressTest.cs:236:18:236:19 | access to field ch | +| GuardsStressTest.cs:237:4:237:5 | access to field ch | +| GuardsStressTest.cs:237:18:237:19 | access to field ch | +| GuardsStressTest.cs:238:4:238:5 | access to field ch | +| GuardsStressTest.cs:238:18:238:19 | access to field ch | +| GuardsStressTest.cs:239:4:239:5 | access to field ch | +| GuardsStressTest.cs:239:18:239:19 | access to field ch | +| GuardsStressTest.cs:240:4:240:5 | access to field ch | +| GuardsStressTest.cs:240:18:240:19 | access to field ch | +| GuardsStressTest.cs:241:4:241:5 | access to field ch | +| GuardsStressTest.cs:241:18:241:19 | access to field ch | +| GuardsStressTest.cs:242:4:242:5 | access to field ch | +| GuardsStressTest.cs:242:18:242:19 | access to field ch | +| GuardsStressTest.cs:243:4:243:5 | access to field ch | +| GuardsStressTest.cs:243:18:243:19 | access to field ch | +| GuardsStressTest.cs:244:4:244:5 | access to field ch | +| GuardsStressTest.cs:244:18:244:19 | access to field ch | +| GuardsStressTest.cs:245:4:245:5 | access to field ch | +| GuardsStressTest.cs:245:18:245:19 | access to field ch | +| GuardsStressTest.cs:246:4:246:5 | access to field ch | +| GuardsStressTest.cs:246:18:246:19 | access to field ch | +| GuardsStressTest.cs:247:4:247:5 | access to field ch | +| GuardsStressTest.cs:247:18:247:19 | access to field ch | +| GuardsStressTest.cs:248:4:248:5 | access to field ch | +| GuardsStressTest.cs:248:18:248:19 | access to field ch | +| GuardsStressTest.cs:249:4:249:5 | access to field ch | +| GuardsStressTest.cs:249:18:249:19 | access to field ch | +| GuardsStressTest.cs:250:4:250:5 | access to field ch | +| GuardsStressTest.cs:250:18:250:19 | access to field ch | +| GuardsStressTest.cs:251:4:251:5 | access to field ch | +| GuardsStressTest.cs:251:18:251:19 | access to field ch | +| GuardsStressTest.cs:252:4:252:5 | access to field ch | +| GuardsStressTest.cs:252:18:252:19 | access to field ch | +| GuardsStressTest.cs:253:4:253:5 | access to field ch | +| GuardsStressTest.cs:253:18:253:19 | access to field ch | +| GuardsStressTest.cs:254:4:254:5 | access to field ch | +| GuardsStressTest.cs:254:18:254:19 | access to field ch | +| GuardsStressTest.cs:255:4:255:5 | access to field ch | +| GuardsStressTest.cs:255:18:255:19 | access to field ch | +| GuardsStressTest.cs:256:4:256:5 | access to field ch | +| GuardsStressTest.cs:256:18:256:19 | access to field ch | +| GuardsStressTest.cs:257:4:257:5 | access to field ch | +| GuardsStressTest.cs:258:4:258:5 | access to field ch | +| GuardsStressTest.cs:258:18:258:19 | access to field ch | +| GuardsStressTest.cs:259:4:259:5 | access to field ch | +| GuardsStressTest.cs:259:18:259:19 | access to field ch | +| GuardsStressTest.cs:260:4:260:5 | access to field ch | +| GuardsStressTest.cs:260:18:260:19 | access to field ch | +| GuardsStressTest.cs:261:4:261:5 | access to field ch | +| GuardsStressTest.cs:261:18:261:19 | access to field ch | +| GuardsStressTest.cs:262:4:262:5 | access to field ch | +| GuardsStressTest.cs:262:18:262:19 | access to field ch | +| GuardsStressTest.cs:263:4:263:5 | access to field ch | +| GuardsStressTest.cs:263:18:263:19 | access to field ch | +| GuardsStressTest.cs:264:4:264:5 | access to field ch | +| GuardsStressTest.cs:264:18:264:19 | access to field ch | +| GuardsStressTest.cs:265:4:265:5 | access to field ch | +| GuardsStressTest.cs:265:18:265:19 | access to field ch | +| GuardsStressTest.cs:266:4:266:5 | access to field ch | +| GuardsStressTest.cs:266:18:266:19 | access to field ch | +| GuardsStressTest.cs:267:4:267:5 | access to field ch | +| GuardsStressTest.cs:267:18:267:19 | access to field ch | +| GuardsStressTest.cs:268:4:268:5 | access to field ch | +| GuardsStressTest.cs:268:18:268:19 | access to field ch | +| GuardsStressTest.cs:269:4:269:5 | access to field ch | +| GuardsStressTest.cs:269:18:269:19 | access to field ch | +| GuardsStressTest.cs:270:4:270:5 | access to field ch | +| GuardsStressTest.cs:270:18:270:19 | access to field ch | +| GuardsStressTest.cs:271:4:271:5 | access to field ch | +| GuardsStressTest.cs:271:18:271:19 | access to field ch | +| GuardsStressTest.cs:272:4:272:5 | access to field ch | +| GuardsStressTest.cs:272:18:272:19 | access to field ch | +| GuardsStressTest.cs:273:4:273:5 | access to field ch | +| GuardsStressTest.cs:273:18:273:19 | access to field ch | +| GuardsStressTest.cs:274:4:274:5 | access to field ch | +| GuardsStressTest.cs:274:18:274:19 | access to field ch | +| GuardsStressTest.cs:275:4:275:5 | access to field ch | +| GuardsStressTest.cs:275:18:275:19 | access to field ch | +| GuardsStressTest.cs:276:4:276:5 | access to field ch | +| GuardsStressTest.cs:276:18:276:19 | access to field ch | +| GuardsStressTest.cs:277:4:277:5 | access to field ch | +| GuardsStressTest.cs:277:18:277:19 | access to field ch | +| GuardsStressTest.cs:278:4:278:5 | access to field ch | +| GuardsStressTest.cs:279:4:279:5 | access to field ch | +| GuardsStressTest.cs:279:18:279:19 | access to field ch | +| GuardsStressTest.cs:280:4:280:5 | access to field ch | +| GuardsStressTest.cs:280:18:280:19 | access to field ch | +| GuardsStressTest.cs:281:4:281:5 | access to field ch | +| GuardsStressTest.cs:281:18:281:19 | access to field ch | +| GuardsStressTest.cs:282:4:282:5 | access to field ch | +| GuardsStressTest.cs:282:18:282:19 | access to field ch | +| GuardsStressTest.cs:283:4:283:5 | access to field ch | +| GuardsStressTest.cs:283:18:283:19 | access to field ch | +| GuardsStressTest.cs:284:4:284:5 | access to field ch | +| GuardsStressTest.cs:284:18:284:19 | access to field ch | +| GuardsStressTest.cs:285:4:285:5 | access to field ch | +| GuardsStressTest.cs:285:18:285:19 | access to field ch | +| GuardsStressTest.cs:286:4:286:5 | access to field ch | +| GuardsStressTest.cs:286:18:286:19 | access to field ch | +| GuardsStressTest.cs:287:4:287:5 | access to field ch | +| GuardsStressTest.cs:287:18:287:19 | access to field ch | +| GuardsStressTest.cs:288:4:288:5 | access to field ch | +| GuardsStressTest.cs:288:18:288:19 | access to field ch | +| GuardsStressTest.cs:289:4:289:5 | access to field ch | +| GuardsStressTest.cs:289:18:289:19 | access to field ch | +| GuardsStressTest.cs:290:4:290:5 | access to field ch | +| GuardsStressTest.cs:290:18:290:19 | access to field ch | +| GuardsStressTest.cs:291:4:291:5 | access to field ch | +| GuardsStressTest.cs:291:18:291:19 | access to field ch | +| GuardsStressTest.cs:292:4:292:5 | access to field ch | +| GuardsStressTest.cs:292:18:292:19 | access to field ch | +| GuardsStressTest.cs:293:4:293:5 | access to field ch | +| GuardsStressTest.cs:293:18:293:19 | access to field ch | +| GuardsStressTest.cs:294:4:294:5 | access to field ch | +| GuardsStressTest.cs:295:4:295:5 | access to field ch | +| GuardsStressTest.cs:296:4:296:5 | access to field ch | +| GuardsStressTest.cs:297:4:297:5 | access to field ch | +| GuardsStressTest.cs:297:18:297:19 | access to field ch | +| GuardsStressTest.cs:298:4:298:5 | access to field ch | +| GuardsStressTest.cs:298:18:298:19 | access to field ch | +| GuardsStressTest.cs:299:4:299:5 | access to field ch | +| GuardsStressTest.cs:299:18:299:19 | access to field ch | +| GuardsStressTest.cs:300:4:300:5 | access to field ch | +| GuardsStressTest.cs:301:4:301:5 | access to field ch | +| GuardsStressTest.cs:301:18:301:19 | access to field ch | +| GuardsStressTest.cs:302:4:302:5 | access to field ch | +| GuardsStressTest.cs:302:18:302:19 | access to field ch | +| GuardsStressTest.cs:303:4:303:5 | access to field ch | +| GuardsStressTest.cs:303:18:303:19 | access to field ch | +| GuardsStressTest.cs:304:4:304:5 | access to field ch | +| GuardsStressTest.cs:304:18:304:19 | access to field ch | +| GuardsStressTest.cs:305:4:305:5 | access to field ch | +| GuardsStressTest.cs:305:18:305:19 | access to field ch | +| GuardsStressTest.cs:306:4:306:5 | access to field ch | +| GuardsStressTest.cs:306:18:306:19 | access to field ch | +| GuardsStressTest.cs:307:4:307:5 | access to field ch | +| GuardsStressTest.cs:307:18:307:19 | access to field ch | +| GuardsStressTest.cs:308:4:308:5 | access to field ch | +| GuardsStressTest.cs:308:18:308:19 | access to field ch | +| GuardsStressTest.cs:309:4:309:5 | access to field ch | +| GuardsStressTest.cs:309:18:309:19 | access to field ch | +| GuardsStressTest.cs:310:4:310:5 | access to field ch | +| GuardsStressTest.cs:311:4:311:5 | access to field ch | +| GuardsStressTest.cs:312:4:312:5 | access to field ch | +| GuardsStressTest.cs:313:4:313:5 | access to field ch | +| GuardsStressTest.cs:313:18:313:19 | access to field ch | +| GuardsStressTest.cs:314:4:314:5 | access to field ch | +| GuardsStressTest.cs:314:18:314:19 | access to field ch | +| GuardsStressTest.cs:315:4:315:5 | access to field ch | +| GuardsStressTest.cs:316:4:316:5 | access to field ch | +| GuardsStressTest.cs:316:18:316:19 | access to field ch | +| GuardsStressTest.cs:317:4:317:5 | access to field ch | +| GuardsStressTest.cs:318:4:318:5 | access to field ch | +| GuardsStressTest.cs:319:4:319:5 | access to field ch | +| GuardsStressTest.cs:319:18:319:19 | access to field ch | +| GuardsStressTest.cs:320:4:320:5 | access to field ch | +| GuardsStressTest.cs:321:4:321:5 | access to field ch | +| GuardsStressTest.cs:321:18:321:19 | access to field ch | +| GuardsStressTest.cs:322:4:322:5 | access to field ch | +| GuardsStressTest.cs:323:4:323:5 | access to field ch | +| GuardsStressTest.cs:324:4:324:5 | access to field ch | +| GuardsStressTest.cs:325:4:325:5 | access to field ch | +| GuardsStressTest.cs:325:18:325:19 | access to field ch | +| GuardsStressTest.cs:326:4:326:5 | access to field ch | +| GuardsStressTest.cs:326:18:326:19 | access to field ch | +| GuardsStressTest.cs:327:4:327:5 | access to field ch | +| GuardsStressTest.cs:327:18:327:19 | access to field ch | +| GuardsStressTest.cs:328:4:328:5 | access to field ch | +| GuardsStressTest.cs:328:18:328:19 | access to field ch | +| GuardsStressTest.cs:329:4:329:5 | access to field ch | +| GuardsStressTest.cs:330:4:330:5 | access to field ch | +| GuardsStressTest.cs:330:18:330:19 | access to field ch | +| GuardsStressTest.cs:331:4:331:5 | access to field ch | +| GuardsStressTest.cs:331:19:331:20 | access to field ch | +| GuardsStressTest.cs:332:4:332:5 | access to field ch | +| GuardsStressTest.cs:332:19:332:20 | access to field ch | +| GuardsStressTest.cs:333:4:333:5 | access to field ch | +| GuardsStressTest.cs:333:19:333:20 | access to field ch | +| GuardsStressTest.cs:334:4:334:5 | access to field ch | +| GuardsStressTest.cs:334:19:334:20 | access to field ch | +| GuardsStressTest.cs:335:4:335:5 | access to field ch | +| GuardsStressTest.cs:335:19:335:20 | access to field ch | +| GuardsStressTest.cs:336:4:336:5 | access to field ch | +| GuardsStressTest.cs:337:4:337:5 | access to field ch | +| GuardsStressTest.cs:338:4:338:5 | access to field ch | +| GuardsStressTest.cs:338:19:338:20 | access to field ch | +| GuardsStressTest.cs:339:4:339:5 | access to field ch | +| GuardsStressTest.cs:340:4:340:5 | access to field ch | +| GuardsStressTest.cs:340:19:340:20 | access to field ch | +| GuardsStressTest.cs:341:4:341:5 | access to field ch | +| GuardsStressTest.cs:341:19:341:20 | access to field ch | +| GuardsStressTest.cs:342:4:342:5 | access to field ch | +| GuardsStressTest.cs:342:19:342:20 | access to field ch | +| GuardsStressTest.cs:343:4:343:5 | access to field ch | +| GuardsStressTest.cs:343:19:343:20 | access to field ch | +| GuardsStressTest.cs:344:4:344:5 | access to field ch | +| GuardsStressTest.cs:344:19:344:20 | access to field ch | +| GuardsStressTest.cs:345:4:345:5 | access to field ch | +| GuardsStressTest.cs:345:19:345:20 | access to field ch | +| GuardsStressTest.cs:346:4:346:5 | access to field ch | +| GuardsStressTest.cs:346:19:346:20 | access to field ch | +| GuardsStressTest.cs:347:4:347:5 | access to field ch | +| GuardsStressTest.cs:347:19:347:20 | access to field ch | +| GuardsStressTest.cs:348:4:348:5 | access to field ch | +| GuardsStressTest.cs:348:19:348:20 | access to field ch | +| GuardsStressTest.cs:349:4:349:5 | access to field ch | +| GuardsStressTest.cs:349:19:349:20 | access to field ch | +| GuardsStressTest.cs:350:4:350:5 | access to field ch | +| GuardsStressTest.cs:351:4:351:5 | access to field ch | +| GuardsStressTest.cs:351:19:351:20 | access to field ch | +| GuardsStressTest.cs:352:4:352:5 | access to field ch | +| GuardsStressTest.cs:352:19:352:20 | access to field ch | +| GuardsStressTest.cs:353:4:353:5 | access to field ch | +| GuardsStressTest.cs:353:19:353:20 | access to field ch | +| GuardsStressTest.cs:354:4:354:5 | access to field ch | +| GuardsStressTest.cs:354:19:354:20 | access to field ch | +| GuardsStressTest.cs:355:4:355:5 | access to field ch | +| GuardsStressTest.cs:355:19:355:20 | access to field ch | +| GuardsStressTest.cs:356:4:356:5 | access to field ch | +| GuardsStressTest.cs:356:19:356:20 | access to field ch | +| GuardsStressTest.cs:357:4:357:5 | access to field ch | +| GuardsStressTest.cs:357:19:357:20 | access to field ch | +| GuardsStressTest.cs:358:4:358:5 | access to field ch | +| GuardsStressTest.cs:358:19:358:20 | access to field ch | +| GuardsStressTest.cs:359:4:359:5 | access to field ch | +| GuardsStressTest.cs:359:19:359:20 | access to field ch | +| GuardsStressTest.cs:360:4:360:5 | access to field ch | +| GuardsStressTest.cs:360:19:360:20 | access to field ch | +| GuardsStressTest.cs:361:4:361:5 | access to field ch | +| GuardsStressTest.cs:361:19:361:20 | access to field ch | +| GuardsStressTest.cs:362:4:362:5 | access to field ch | +| GuardsStressTest.cs:362:19:362:20 | access to field ch | +| GuardsStressTest.cs:363:4:363:5 | access to field ch | +| GuardsStressTest.cs:363:19:363:20 | access to field ch | +| GuardsStressTest.cs:364:4:364:5 | access to field ch | +| GuardsStressTest.cs:364:19:364:20 | access to field ch | +| GuardsStressTest.cs:365:4:365:5 | access to field ch | +| GuardsStressTest.cs:365:19:365:20 | access to field ch | +| GuardsStressTest.cs:366:4:366:5 | access to field ch | +| GuardsStressTest.cs:366:19:366:20 | access to field ch | +| GuardsStressTest.cs:367:4:367:5 | access to field ch | +| GuardsStressTest.cs:367:19:367:20 | access to field ch | +| GuardsStressTest.cs:368:4:368:5 | access to field ch | +| GuardsStressTest.cs:368:19:368:20 | access to field ch | +| GuardsStressTest.cs:369:4:369:5 | access to field ch | +| GuardsStressTest.cs:369:19:369:20 | access to field ch | +| GuardsStressTest.cs:370:4:370:5 | access to field ch | +| GuardsStressTest.cs:370:19:370:20 | access to field ch | +| GuardsStressTest.cs:371:4:371:5 | access to field ch | +| GuardsStressTest.cs:371:19:371:20 | access to field ch | +| GuardsStressTest.cs:372:4:372:5 | access to field ch | +| GuardsStressTest.cs:372:19:372:20 | access to field ch | +| GuardsStressTest.cs:373:4:373:5 | access to field ch | +| GuardsStressTest.cs:373:19:373:20 | access to field ch | +| GuardsStressTest.cs:374:4:374:5 | access to field ch | +| GuardsStressTest.cs:374:19:374:20 | access to field ch | +| GuardsStressTest.cs:375:4:375:5 | access to field ch | +| GuardsStressTest.cs:375:19:375:20 | access to field ch | +| GuardsStressTest.cs:376:4:376:5 | access to field ch | +| GuardsStressTest.cs:376:19:376:20 | access to field ch | +| GuardsStressTest.cs:377:4:377:5 | access to field ch | +| GuardsStressTest.cs:377:19:377:20 | access to field ch | +| GuardsStressTest.cs:378:4:378:5 | access to field ch | +| GuardsStressTest.cs:378:19:378:20 | access to field ch | +| GuardsStressTest.cs:379:4:379:5 | access to field ch | +| GuardsStressTest.cs:379:19:379:20 | access to field ch | +| GuardsStressTest.cs:380:4:380:5 | access to field ch | +| GuardsStressTest.cs:380:19:380:20 | access to field ch | +| GuardsStressTest.cs:381:4:381:5 | access to field ch | +| GuardsStressTest.cs:381:19:381:20 | access to field ch | +| GuardsStressTest.cs:382:4:382:5 | access to field ch | +| GuardsStressTest.cs:382:19:382:20 | access to field ch | +| GuardsStressTest.cs:383:4:383:5 | access to field ch | +| GuardsStressTest.cs:383:19:383:20 | access to field ch | +| GuardsStressTest.cs:384:4:384:5 | access to field ch | +| GuardsStressTest.cs:385:4:385:5 | access to field ch | +| GuardsStressTest.cs:385:19:385:20 | access to field ch | +| GuardsStressTest.cs:386:4:386:5 | access to field ch | +| GuardsStressTest.cs:386:19:386:20 | access to field ch | +| GuardsStressTest.cs:387:4:387:5 | access to field ch | +| GuardsStressTest.cs:387:19:387:20 | access to field ch | +| GuardsStressTest.cs:388:4:388:5 | access to field ch | +| GuardsStressTest.cs:388:19:388:20 | access to field ch | +| GuardsStressTest.cs:389:4:389:5 | access to field ch | +| GuardsStressTest.cs:389:19:389:20 | access to field ch | +| GuardsStressTest.cs:390:4:390:5 | access to field ch | +| GuardsStressTest.cs:390:19:390:20 | access to field ch | +| GuardsStressTest.cs:391:4:391:5 | access to field ch | +| GuardsStressTest.cs:391:19:391:20 | access to field ch | +| GuardsStressTest.cs:392:4:392:5 | access to field ch | +| GuardsStressTest.cs:392:19:392:20 | access to field ch | +| GuardsStressTest.cs:393:4:393:5 | access to field ch | +| GuardsStressTest.cs:393:19:393:20 | access to field ch | +| GuardsStressTest.cs:394:4:394:5 | access to field ch | +| GuardsStressTest.cs:394:19:394:20 | access to field ch | +| GuardsStressTest.cs:395:4:395:5 | access to field ch | +| GuardsStressTest.cs:395:19:395:20 | access to field ch | +| GuardsStressTest.cs:396:4:396:5 | access to field ch | +| GuardsStressTest.cs:396:19:396:20 | access to field ch | +| GuardsStressTest.cs:397:4:397:5 | access to field ch | +| GuardsStressTest.cs:397:19:397:20 | access to field ch | +| GuardsStressTest.cs:398:4:398:5 | access to field ch | +| GuardsStressTest.cs:398:19:398:20 | access to field ch | +| GuardsStressTest.cs:399:4:399:5 | access to field ch | +| GuardsStressTest.cs:399:19:399:20 | access to field ch | +| GuardsStressTest.cs:400:4:400:5 | access to field ch | +| GuardsStressTest.cs:400:19:400:20 | access to field ch | +| GuardsStressTest.cs:401:4:401:5 | access to field ch | +| GuardsStressTest.cs:401:19:401:20 | access to field ch | +| GuardsStressTest.cs:402:4:402:5 | access to field ch | +| GuardsStressTest.cs:402:19:402:20 | access to field ch | +| GuardsStressTest.cs:403:4:403:5 | access to field ch | +| GuardsStressTest.cs:403:19:403:20 | access to field ch | +| GuardsStressTest.cs:404:4:404:5 | access to field ch | +| GuardsStressTest.cs:404:19:404:20 | access to field ch | +| GuardsStressTest.cs:405:4:405:5 | access to field ch | +| GuardsStressTest.cs:405:19:405:20 | access to field ch | +| GuardsStressTest.cs:406:4:406:5 | access to field ch | +| GuardsStressTest.cs:406:19:406:20 | access to field ch | +| GuardsStressTest.cs:407:4:407:5 | access to field ch | +| GuardsStressTest.cs:407:19:407:20 | access to field ch | +| GuardsStressTest.cs:408:4:408:5 | access to field ch | +| GuardsStressTest.cs:408:19:408:20 | access to field ch | +| GuardsStressTest.cs:409:4:409:5 | access to field ch | +| GuardsStressTest.cs:409:19:409:20 | access to field ch | +| GuardsStressTest.cs:410:4:410:5 | access to field ch | +| GuardsStressTest.cs:410:19:410:20 | access to field ch | +| GuardsStressTest.cs:411:4:411:5 | access to field ch | +| GuardsStressTest.cs:411:19:411:20 | access to field ch | +| GuardsStressTest.cs:412:4:412:5 | access to field ch | +| GuardsStressTest.cs:412:19:412:20 | access to field ch | +| GuardsStressTest.cs:413:4:413:5 | access to field ch | +| GuardsStressTest.cs:413:19:413:20 | access to field ch | +| GuardsStressTest.cs:414:4:414:5 | access to field ch | +| GuardsStressTest.cs:414:19:414:20 | access to field ch | +| GuardsStressTest.cs:415:4:415:5 | access to field ch | +| GuardsStressTest.cs:415:19:415:20 | access to field ch | +| GuardsStressTest.cs:416:4:416:5 | access to field ch | +| GuardsStressTest.cs:416:19:416:20 | access to field ch | +| GuardsStressTest.cs:417:4:417:5 | access to field ch | +| GuardsStressTest.cs:418:4:418:5 | access to field ch | +| GuardsStressTest.cs:418:19:418:20 | access to field ch | +| GuardsStressTest.cs:419:4:419:5 | access to field ch | +| GuardsStressTest.cs:419:19:419:20 | access to field ch | +| GuardsStressTest.cs:420:4:420:5 | access to field ch | +| GuardsStressTest.cs:420:19:420:20 | access to field ch | +| GuardsStressTest.cs:421:4:421:5 | access to field ch | +| GuardsStressTest.cs:421:19:421:20 | access to field ch | +| GuardsStressTest.cs:422:4:422:5 | access to field ch | +| GuardsStressTest.cs:422:19:422:20 | access to field ch | +| GuardsStressTest.cs:423:4:423:5 | access to field ch | +| GuardsStressTest.cs:423:19:423:20 | access to field ch | +| GuardsStressTest.cs:424:4:424:5 | access to field ch | +| GuardsStressTest.cs:424:19:424:20 | access to field ch | +| GuardsStressTest.cs:425:4:425:5 | access to field ch | +| GuardsStressTest.cs:425:19:425:20 | access to field ch | +| GuardsStressTest.cs:426:4:426:5 | access to field ch | +| GuardsStressTest.cs:426:19:426:20 | access to field ch | +| GuardsStressTest.cs:427:4:427:5 | access to field ch | +| GuardsStressTest.cs:427:19:427:20 | access to field ch | +| GuardsStressTest.cs:428:4:428:5 | access to field ch | +| GuardsStressTest.cs:428:19:428:20 | access to field ch | +| GuardsStressTest.cs:429:4:429:5 | access to field ch | +| GuardsStressTest.cs:429:19:429:20 | access to field ch | +| GuardsStressTest.cs:430:4:430:5 | access to field ch | +| GuardsStressTest.cs:430:19:430:20 | access to field ch | +| GuardsStressTest.cs:431:4:431:5 | access to field ch | +| GuardsStressTest.cs:431:19:431:20 | access to field ch | +| GuardsStressTest.cs:432:4:432:5 | access to field ch | +| GuardsStressTest.cs:432:19:432:20 | access to field ch | +| GuardsStressTest.cs:433:4:433:5 | access to field ch | +| GuardsStressTest.cs:434:4:434:5 | access to field ch | +| GuardsStressTest.cs:434:19:434:20 | access to field ch | +| GuardsStressTest.cs:435:4:435:5 | access to field ch | +| GuardsStressTest.cs:435:19:435:20 | access to field ch | +| GuardsStressTest.cs:436:4:436:5 | access to field ch | +| GuardsStressTest.cs:436:19:436:20 | access to field ch | +| GuardsStressTest.cs:437:4:437:5 | access to field ch | +| GuardsStressTest.cs:437:19:437:20 | access to field ch | +| GuardsStressTest.cs:438:4:438:5 | access to field ch | +| GuardsStressTest.cs:438:19:438:20 | access to field ch | +| GuardsStressTest.cs:439:4:439:5 | access to field ch | +| GuardsStressTest.cs:439:19:439:20 | access to field ch | +| GuardsStressTest.cs:439:41:439:42 | access to field ch | +| GuardsStressTest.cs:440:23:440:24 | access to field ch | diff --git a/csharp/ql/test/library-tests/controlflow/guards-large/GuardedExpr.ql b/csharp/ql/test/library-tests/controlflow/guards-large/GuardedExpr.ql new file mode 100644 index 00000000000..dbb472735b4 --- /dev/null +++ b/csharp/ql/test/library-tests/controlflow/guards-large/GuardedExpr.ql @@ -0,0 +1,5 @@ +import csharp +import semmle.code.csharp.controlflow.Guards + +from GuardedExpr ge +select ge diff --git a/csharp/ql/test/library-tests/controlflow/guards-large/GuardsStressTest.cs b/csharp/ql/test/library-tests/controlflow/guards-large/GuardsStressTest.cs new file mode 100644 index 00000000000..200c51d89fd --- /dev/null +++ b/csharp/ql/test/library-tests/controlflow/guards-large/GuardsStressTest.cs @@ -0,0 +1,443 @@ +using System; +#nullable enable +public class StressTest +{ + int ch; + int Ors() + { + if (ch >= '0' && ch <= '9' +|| ch >= 'A' && ch <= 'Z' +|| ch == '_' +|| ch >= 'a' && ch <= 'z' +|| ch == 170 +|| ch == 181 +|| ch == 186 +|| ch >= 192 && ch <= 214 +|| ch >= 216 && ch <= 246 +|| ch >= 248 && ch <= 705 +|| ch >= 710 && ch <= 721 +|| ch >= 736 && ch <= 740 +|| ch == 748 +|| ch == 750 +|| ch >= 768 && ch <= 884 +|| ch >= 886 && ch <= 887 +|| ch >= 890 && ch <= 893 +|| ch == 902 +|| ch >= 904 && ch <= 906 +|| ch == 908 +|| ch >= 910 && ch <= 929 +|| ch >= 931 && ch <= 1013 +|| ch >= 1015 && ch <= 1153 +|| ch >= 1155 && ch <= 1159 +|| ch >= 1162 && ch <= 1319 +|| ch >= 1329 && ch <= 1366 +|| ch == 1369 +|| ch >= 1377 && ch <= 1415 +|| ch >= 1425 && ch <= 1469 +|| ch == 1471 +|| ch >= 1473 && ch <= 1474 +|| ch >= 1476 && ch <= 1477 +|| ch == 1479 +|| ch >= 1488 && ch <= 1514 +|| ch >= 1520 && ch <= 1522 +|| ch >= 1552 && ch <= 1562 +|| ch >= 1568 && ch <= 1641 +|| ch >= 1646 && ch <= 1747 +|| ch >= 1749 && ch <= 1756 +|| ch >= 1759 && ch <= 1768 +|| ch >= 1770 && ch <= 1788 +|| ch == 1791 +|| ch >= 1808 && ch <= 1866 +|| ch >= 1869 && ch <= 1969 +|| ch >= 1984 && ch <= 2037 +|| ch == 2042 +|| ch >= 2048 && ch <= 2093 +|| ch >= 2112 && ch <= 2139 +|| ch == 2208 +|| ch >= 2210 && ch <= 2220 +|| ch >= 2276 && ch <= 2302 +|| ch >= 2304 && ch <= 2403 +|| ch >= 2406 && ch <= 2415 +|| ch >= 2417 && ch <= 2423 +|| ch >= 2425 && ch <= 2431 +|| ch >= 2433 && ch <= 2435 +|| ch >= 2437 && ch <= 2444 +|| ch >= 2447 && ch <= 2448 +|| ch >= 2451 && ch <= 2472 +|| ch >= 2474 && ch <= 2480 +|| ch == 2482 +|| ch >= 2486 && ch <= 2489 +|| ch >= 2492 && ch <= 2500 +|| ch >= 2503 && ch <= 2504 +|| ch >= 2507 && ch <= 2510 +|| ch == 2519 +|| ch >= 2524 && ch <= 2525 +|| ch >= 2527 && ch <= 2531 +|| ch >= 2534 && ch <= 2545 +|| ch >= 2561 && ch <= 2563 +|| ch >= 2565 && ch <= 2570 +|| ch >= 2575 && ch <= 2576 +|| ch >= 2579 && ch <= 2600 +|| ch >= 2602 && ch <= 2608 +|| ch >= 2610 && ch <= 2611 +|| ch >= 2613 && ch <= 2614 +|| ch >= 2616 && ch <= 2617 +|| ch == 2620 +|| ch >= 2622 && ch <= 2626 +|| ch >= 2631 && ch <= 2632 +|| ch >= 2635 && ch <= 2637 +|| ch == 2641 +|| ch >= 2649 && ch <= 2652 +|| ch == 2654 +|| ch >= 2662 && ch <= 2677 +|| ch >= 2689 && ch <= 2691 +|| ch >= 2693 && ch <= 2701 +|| ch >= 2703 && ch <= 2705 +|| ch >= 2707 && ch <= 2728 +|| ch >= 2730 && ch <= 2736 +|| ch >= 2738 && ch <= 2739 +|| ch >= 2741 && ch <= 2745 +|| ch >= 2748 && ch <= 2757 +|| ch >= 2759 && ch <= 2761 +|| ch >= 2763 && ch <= 2765 +|| ch == 2768 +|| ch >= 2784 && ch <= 2787 +|| ch >= 2790 && ch <= 2799 +|| ch >= 2817 && ch <= 2819 +|| ch >= 2821 && ch <= 2828 +|| ch >= 2831 && ch <= 2832 +|| ch >= 2835 && ch <= 2856 +|| ch >= 2858 && ch <= 2864 +|| ch >= 2866 && ch <= 2867 +|| ch >= 2869 && ch <= 2873 +|| ch >= 2876 && ch <= 2884 +|| ch >= 2887 && ch <= 2888 +|| ch >= 2891 && ch <= 2893 +|| ch >= 2902 && ch <= 2903 +|| ch >= 2908 && ch <= 2909 +|| ch >= 2911 && ch <= 2915 +|| ch >= 2918 && ch <= 2927 +|| ch == 2929 +|| ch >= 2946 && ch <= 2947 +|| ch >= 2949 && ch <= 2954 +|| ch >= 2958 && ch <= 2960 +|| ch >= 2962 && ch <= 2965 +|| ch >= 2969 && ch <= 2970 +|| ch == 2972 +|| ch >= 2974 && ch <= 2975 +|| ch >= 2979 && ch <= 2980 +|| ch >= 2984 && ch <= 2986 +|| ch >= 2990 && ch <= 3001 +|| ch >= 3006 && ch <= 3010 +|| ch >= 3014 && ch <= 3016 +|| ch >= 3018 && ch <= 3021 +|| ch == 3024 +|| ch == 3031 +|| ch >= 3046 && ch <= 3055 +|| ch >= 3073 && ch <= 3075 +|| ch >= 3077 && ch <= 3084 +|| ch >= 3086 && ch <= 3088 +|| ch >= 3090 && ch <= 3112 +|| ch >= 3114 && ch <= 3123 +|| ch >= 3125 && ch <= 3129 +|| ch >= 3133 && ch <= 3140 +|| ch >= 3142 && ch <= 3144 +|| ch >= 3146 && ch <= 3149 +|| ch >= 3157 && ch <= 3158 +|| ch >= 3160 && ch <= 3161 +|| ch >= 3168 && ch <= 3171 +|| ch >= 3174 && ch <= 3183 +|| ch >= 3202 && ch <= 3203 +|| ch >= 3205 && ch <= 3212 +|| ch >= 3214 && ch <= 3216 +|| ch >= 3218 && ch <= 3240 +|| ch >= 3242 && ch <= 3251 +|| ch >= 3253 && ch <= 3257 +|| ch >= 3260 && ch <= 3268 +|| ch >= 3270 && ch <= 3272 +|| ch >= 3274 && ch <= 3277 +|| ch >= 3285 && ch <= 3286 +|| ch == 3294 +|| ch >= 3296 && ch <= 3299 +|| ch >= 3302 && ch <= 3311 +|| ch >= 3313 && ch <= 3314 +|| ch >= 3330 && ch <= 3331 +|| ch >= 3333 && ch <= 3340 +|| ch >= 3342 && ch <= 3344 +|| ch >= 3346 && ch <= 3386 +|| ch >= 3389 && ch <= 3396 +|| ch >= 3398 && ch <= 3400 +|| ch >= 3402 && ch <= 3406 +|| ch == 3415 +|| ch >= 3424 && ch <= 3427 +|| ch >= 3430 && ch <= 3439 +|| ch >= 3450 && ch <= 3455 +|| ch >= 3458 && ch <= 3459 +|| ch >= 3461 && ch <= 3478 +|| ch >= 3482 && ch <= 3505 +|| ch >= 3507 && ch <= 3515 +|| ch == 3517 +|| ch >= 3520 && ch <= 3526 +|| ch == 3530 +|| ch >= 3535 && ch <= 3540 +|| ch == 3542 +|| ch >= 3544 && ch <= 3551 +|| ch >= 3570 && ch <= 3571 +|| ch >= 3585 && ch <= 3642 +|| ch >= 3648 && ch <= 3662 +|| ch >= 3664 && ch <= 3673 +|| ch >= 3713 && ch <= 3714 +|| ch == 3716 +|| ch >= 3719 && ch <= 3720 +|| ch == 3722 +|| ch == 3725 +|| ch >= 3732 && ch <= 3735 +|| ch >= 3737 && ch <= 3743 +|| ch >= 3745 && ch <= 3747 +|| ch == 3749 +|| ch == 3751 +|| ch >= 3754 && ch <= 3755 +|| ch >= 3757 && ch <= 3769 +|| ch >= 3771 && ch <= 3773 +|| ch >= 3776 && ch <= 3780 +|| ch == 3782 +|| ch >= 3784 && ch <= 3789 +|| ch >= 3792 && ch <= 3801 +|| ch >= 3804 && ch <= 3807 +|| ch == 3840 +|| ch >= 3864 && ch <= 3865 +|| ch >= 3872 && ch <= 3881 +|| ch == 3893 +|| ch == 3895 +|| ch == 3897 +|| ch >= 3902 && ch <= 3911 +|| ch >= 3913 && ch <= 3948 +|| ch >= 3953 && ch <= 3972 +|| ch >= 3974 && ch <= 3991 +|| ch >= 3993 && ch <= 4028 +|| ch == 4038 +|| ch >= 4096 && ch <= 4169 +|| ch >= 4176 && ch <= 4253 +|| ch >= 4256 && ch <= 4293 +|| ch == 4295 +|| ch == 4301 +|| ch >= 4304 && ch <= 4346 +|| ch >= 4348 && ch <= 4680 +|| ch >= 4682 && ch <= 4685 +|| ch >= 4688 && ch <= 4694 +|| ch == 4696 +|| ch >= 4698 && ch <= 4701 +|| ch >= 4704 && ch <= 4744 +|| ch >= 4746 && ch <= 4749 +|| ch >= 4752 && ch <= 4784 +|| ch >= 4786 && ch <= 4789 +|| ch >= 4792 && ch <= 4798 +|| ch == 4800 +|| ch >= 4802 && ch <= 4805 +|| ch >= 4808 && ch <= 4822 +|| ch >= 4824 && ch <= 4880 +|| ch >= 4882 && ch <= 4885 +|| ch >= 4888 && ch <= 4954 +|| ch >= 4957 && ch <= 4959 +|| ch >= 4992 && ch <= 5007 +|| ch >= 5024 && ch <= 5108 +|| ch >= 5121 && ch <= 5740 +|| ch >= 5743 && ch <= 5759 +|| ch >= 5761 && ch <= 5786 +|| ch >= 5792 && ch <= 5866 +|| ch >= 5870 && ch <= 5872 +|| ch >= 5888 && ch <= 5900 +|| ch >= 5902 && ch <= 5908 +|| ch >= 5920 && ch <= 5940 +|| ch >= 5952 && ch <= 5971 +|| ch >= 5984 && ch <= 5996 +|| ch >= 5998 && ch <= 6000 +|| ch >= 6002 && ch <= 6003 +|| ch >= 6016 && ch <= 6099 +|| ch == 6103 +|| ch >= 6108 && ch <= 6109 +|| ch >= 6112 && ch <= 6121 +|| ch >= 6155 && ch <= 6157 +|| ch >= 6160 && ch <= 6169 +|| ch >= 6176 && ch <= 6263 +|| ch >= 6272 && ch <= 6314 +|| ch >= 6320 && ch <= 6389 +|| ch >= 6400 && ch <= 6428 +|| ch >= 6432 && ch <= 6443 +|| ch >= 6448 && ch <= 6459 +|| ch >= 6470 && ch <= 6509 +|| ch >= 6512 && ch <= 6516 +|| ch >= 6528 && ch <= 6571 +|| ch >= 6576 && ch <= 6601 +|| ch >= 6608 && ch <= 6617 +|| ch >= 6656 && ch <= 6683 +|| ch >= 6688 && ch <= 6750 +|| ch >= 6752 && ch <= 6780 +|| ch >= 6783 && ch <= 6793 +|| ch >= 6800 && ch <= 6809 +|| ch == 6823 +|| ch >= 6912 && ch <= 6987 +|| ch >= 6992 && ch <= 7001 +|| ch >= 7019 && ch <= 7027 +|| ch >= 7040 && ch <= 7155 +|| ch >= 7168 && ch <= 7223 +|| ch >= 7232 && ch <= 7241 +|| ch >= 7245 && ch <= 7293 +|| ch >= 7376 && ch <= 7378 +|| ch >= 7380 && ch <= 7414 +|| ch >= 7424 && ch <= 7654 +|| ch >= 7676 && ch <= 7957 +|| ch >= 7960 && ch <= 7965 +|| ch >= 7968 && ch <= 8005 +|| ch >= 8008 && ch <= 8013 +|| ch >= 8016 && ch <= 8023 +|| ch == 8025 +|| ch == 8027 +|| ch == 8029 +|| ch >= 8031 && ch <= 8061 +|| ch >= 8064 && ch <= 8116 +|| ch >= 8118 && ch <= 8124 +|| ch == 8126 +|| ch >= 8130 && ch <= 8132 +|| ch >= 8134 && ch <= 8140 +|| ch >= 8144 && ch <= 8147 +|| ch >= 8150 && ch <= 8155 +|| ch >= 8160 && ch <= 8172 +|| ch >= 8178 && ch <= 8180 +|| ch >= 8182 && ch <= 8188 +|| ch >= 8204 && ch <= 8205 +|| ch >= 8255 && ch <= 8256 +|| ch == 8276 +|| ch == 8305 +|| ch == 8319 +|| ch >= 8336 && ch <= 8348 +|| ch >= 8400 && ch <= 8412 +|| ch == 8417 +|| ch >= 8421 && ch <= 8432 +|| ch == 8450 +|| ch == 8455 +|| ch >= 8458 && ch <= 8467 +|| ch == 8469 +|| ch >= 8473 && ch <= 8477 +|| ch == 8484 +|| ch == 8486 +|| ch == 8488 +|| ch >= 8490 && ch <= 8493 +|| ch >= 8495 && ch <= 8505 +|| ch >= 8508 && ch <= 8511 +|| ch >= 8517 && ch <= 8521 +|| ch == 8526 +|| ch >= 8544 && ch <= 8584 +|| ch >= 11264 && ch <= 11310 +|| ch >= 11312 && ch <= 11358 +|| ch >= 11360 && ch <= 11492 +|| ch >= 11499 && ch <= 11507 +|| ch >= 11520 && ch <= 11557 +|| ch == 11559 +|| ch == 11565 +|| ch >= 11568 && ch <= 11623 +|| ch == 11631 +|| ch >= 11647 && ch <= 11670 +|| ch >= 11680 && ch <= 11686 +|| ch >= 11688 && ch <= 11694 +|| ch >= 11696 && ch <= 11702 +|| ch >= 11704 && ch <= 11710 +|| ch >= 11712 && ch <= 11718 +|| ch >= 11720 && ch <= 11726 +|| ch >= 11728 && ch <= 11734 +|| ch >= 11736 && ch <= 11742 +|| ch >= 11744 && ch <= 11775 +|| ch == 11823 +|| ch >= 12293 && ch <= 12295 +|| ch >= 12321 && ch <= 12335 +|| ch >= 12337 && ch <= 12341 +|| ch >= 12344 && ch <= 12348 +|| ch >= 12353 && ch <= 12438 +|| ch >= 12441 && ch <= 12442 +|| ch >= 12445 && ch <= 12447 +|| ch >= 12449 && ch <= 12538 +|| ch >= 12540 && ch <= 12543 +|| ch >= 12549 && ch <= 12589 +|| ch >= 12593 && ch <= 12686 +|| ch >= 12704 && ch <= 12730 +|| ch >= 12784 && ch <= 12799 +|| ch >= 13312 && ch <= 19893 +|| ch >= 19968 && ch <= 40908 +|| ch >= 40960 && ch <= 42124 +|| ch >= 42192 && ch <= 42237 +|| ch >= 42240 && ch <= 42508 +|| ch >= 42512 && ch <= 42539 +|| ch >= 42560 && ch <= 42607 +|| ch >= 42612 && ch <= 42621 +|| ch >= 42623 && ch <= 42647 +|| ch >= 42655 && ch <= 42737 +|| ch >= 42775 && ch <= 42783 +|| ch >= 42786 && ch <= 42888 +|| ch >= 42891 && ch <= 42894 +|| ch >= 42896 && ch <= 42899 +|| ch >= 42912 && ch <= 42922 +|| ch >= 43000 && ch <= 43047 +|| ch >= 43072 && ch <= 43123 +|| ch >= 43136 && ch <= 43204 +|| ch >= 43216 && ch <= 43225 +|| ch >= 43232 && ch <= 43255 +|| ch == 43259 +|| ch >= 43264 && ch <= 43309 +|| ch >= 43312 && ch <= 43347 +|| ch >= 43360 && ch <= 43388 +|| ch >= 43392 && ch <= 43456 +|| ch >= 43471 && ch <= 43481 +|| ch >= 43520 && ch <= 43574 +|| ch >= 43584 && ch <= 43597 +|| ch >= 43600 && ch <= 43609 +|| ch >= 43616 && ch <= 43638 +|| ch >= 43642 && ch <= 43643 +|| ch >= 43648 && ch <= 43714 +|| ch >= 43739 && ch <= 43741 +|| ch >= 43744 && ch <= 43759 +|| ch >= 43762 && ch <= 43766 +|| ch >= 43777 && ch <= 43782 +|| ch >= 43785 && ch <= 43790 +|| ch >= 43793 && ch <= 43798 +|| ch >= 43808 && ch <= 43814 +|| ch >= 43816 && ch <= 43822 +|| ch >= 43968 && ch <= 44010 +|| ch >= 44012 && ch <= 44013 +|| ch >= 44016 && ch <= 44025 +|| ch >= 44032 && ch <= 55203 +|| ch >= 55216 && ch <= 55238 +|| ch >= 55243 && ch <= 55291 +|| ch >= 63744 && ch <= 64109 +|| ch >= 64112 && ch <= 64217 +|| ch >= 64256 && ch <= 64262 +|| ch >= 64275 && ch <= 64279 +|| ch >= 64285 && ch <= 64296 +|| ch >= 64298 && ch <= 64310 +|| ch >= 64312 && ch <= 64316 +|| ch == 64318 +|| ch >= 64320 && ch <= 64321 +|| ch >= 64323 && ch <= 64324 +|| ch >= 64326 && ch <= 64433 +|| ch >= 64467 && ch <= 64829 +|| ch >= 64848 && ch <= 64911 +|| ch >= 64914 && ch <= 64967 +|| ch >= 65008 && ch <= 65019 +|| ch >= 65024 && ch <= 65039 +|| ch >= 65056 && ch <= 65062 +|| ch >= 65075 && ch <= 65076 +|| ch >= 65101 && ch <= 65103 +|| ch >= 65136 && ch <= 65140 +|| ch >= 65142 && ch <= 65276 +|| ch >= 65296 && ch <= 65305 +|| ch >= 65313 && ch <= 65338 +|| ch == 65343 +|| ch >= 65345 && ch <= 65370 +|| ch >= 65382 && ch <= 65470 +|| ch >= 65474 && ch <= 65479 +|| ch >= 65482 && ch <= 65487 +|| ch >= 65490 && ch <= 65495 +|| ch >= 65498 && ch <= 65500) { return ch; } + else { return ch + 1; } + } +} + diff --git a/csharp/ql/test/library-tests/controlflow/guards/BooleanGuardedExpr.expected b/csharp/ql/test/library-tests/controlflow/guards/BooleanGuardedExpr.expected index 120e3e2c5ce..40d144f3719 100644 --- a/csharp/ql/test/library-tests/controlflow/guards/BooleanGuardedExpr.expected +++ b/csharp/ql/test/library-tests/controlflow/guards/BooleanGuardedExpr.expected @@ -5,16 +5,12 @@ | Assert.cs:53:27:53:27 | access to local variable s | Assert.cs:52:24:52:32 | ... == ... | Assert.cs:52:24:52:24 | access to local variable s | false | | Assert.cs:59:36:59:36 | access to parameter b | Assert.cs:58:20:58:20 | access to parameter b | Assert.cs:58:20:58:20 | access to parameter b | false | | Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:31 | ... != ... | Assert.cs:59:23:59:23 | access to local variable s | true | -| Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:36 | ... && ... | Assert.cs:59:23:59:23 | access to local variable s | true | | Assert.cs:66:37:66:37 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | false | | Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:32 | ... == ... | Assert.cs:66:24:66:24 | access to local variable s | false | -| Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:37 | ... \|\| ... | Assert.cs:66:24:66:24 | access to local variable s | false | | Assert.cs:73:36:73:36 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | true | | Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:31 | ... == ... | Assert.cs:73:23:73:23 | access to local variable s | true | -| Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:36 | ... && ... | Assert.cs:73:23:73:23 | access to local variable s | true | | Assert.cs:80:37:80:37 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | true | | Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:32 | ... != ... | Assert.cs:80:24:80:24 | access to local variable s | false | -| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:37 | ... \|\| ... | Assert.cs:80:24:80:24 | access to local variable s | false | | Assert.cs:94:16:94:17 | access to parameter b1 | Assert.cs:93:25:93:26 | access to parameter b1 | Assert.cs:93:25:93:26 | access to parameter b1 | true | | Assert.cs:94:23:94:24 | access to parameter b2 | Assert.cs:93:29:93:30 | access to parameter b2 | Assert.cs:93:29:93:30 | access to parameter b2 | false | | Collections.cs:52:17:52:20 | access to parameter args | Collections.cs:50:13:50:27 | ... == ... | Collections.cs:50:13:50:16 | access to parameter args | false | @@ -22,39 +18,17 @@ | Collections.cs:54:13:54:16 | access to parameter args | Collections.cs:50:13:50:27 | ... == ... | Collections.cs:50:13:50:16 | access to parameter args | false | | Collections.cs:67:13:67:13 | access to local variable x | Collections.cs:65:13:65:24 | ... == ... | Collections.cs:65:13:65:13 | access to local variable x | true | | Collections.cs:68:13:68:13 | access to local variable x | Collections.cs:65:13:65:24 | ... == ... | Collections.cs:65:13:65:13 | access to local variable x | true | -| Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:13:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | false | -| Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:14:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | true | | Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:16:10:24 | ... == ... | Guards.cs:10:16:10:16 | access to parameter s | false | -| Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:13:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | false | -| Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:14:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | true | | Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:16:10:24 | ... == ... | Guards.cs:10:16:10:16 | access to parameter s | false | | Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:12:13:12:24 | ... > ... | Guards.cs:12:13:12:13 | access to parameter s | true | | Guards.cs:26:31:26:31 | access to parameter s | Guards.cs:24:13:24:21 | ... != ... | Guards.cs:24:13:24:13 | access to parameter s | true | -| Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:13:32:36 | !... | Guards.cs:32:35:32:35 | access to parameter x | true | -| Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:13:32:51 | ... & ... | Guards.cs:32:35:32:35 | access to parameter x | true | | Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:14:32:36 | call to method IsNullOrEmpty | Guards.cs:32:35:32:35 | access to parameter x | false | -| Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:13:32:51 | ... & ... | Guards.cs:32:42:32:42 | access to parameter y | true | -| Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:40:32:51 | !... | Guards.cs:32:42:32:42 | access to parameter y | true | | Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:42:32:50 | ... == ... | Guards.cs:32:42:32:42 | access to parameter y | false | | Guards.cs:36:32:36:32 | access to parameter x | Guards.cs:35:13:35:21 | ... == ... | Guards.cs:35:13:35:13 | access to parameter x | false | -| Guards.cs:36:32:36:32 | access to parameter x | Guards.cs:35:13:35:34 | ... \|\| ... | Guards.cs:35:13:35:13 | access to parameter x | false | -| Guards.cs:36:36:36:36 | access to parameter y | Guards.cs:35:13:35:34 | ... \|\| ... | Guards.cs:35:26:35:26 | access to parameter y | false | | Guards.cs:36:36:36:36 | access to parameter y | Guards.cs:35:26:35:34 | ... == ... | Guards.cs:35:26:35:26 | access to parameter y | false | -| Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:13:38:37 | !... | Guards.cs:38:15:38:15 | access to parameter x | true | | Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:15:38:23 | ... == ... | Guards.cs:38:15:38:15 | access to parameter x | false | -| Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:15:38:36 | ... \|\| ... | Guards.cs:38:15:38:15 | access to parameter x | false | -| Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:13:38:37 | !... | Guards.cs:38:28:38:28 | access to parameter y | true | -| Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:15:38:36 | ... \|\| ... | Guards.cs:38:28:38:28 | access to parameter y | false | | Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:28:38:36 | ... == ... | Guards.cs:38:28:38:28 | access to parameter y | false | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:13:41:39 | !... | Guards.cs:41:17:41:17 | access to parameter x | false | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:14:41:39 | !... | Guards.cs:41:17:41:17 | access to parameter x | true | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:15:41:39 | !... | Guards.cs:41:17:41:17 | access to parameter x | false | | Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:17:41:25 | ... != ... | Guards.cs:41:17:41:17 | access to parameter x | true | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:17:41:38 | ... && ... | Guards.cs:41:17:41:17 | access to parameter x | true | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:13:41:39 | !... | Guards.cs:41:30:41:30 | access to parameter y | false | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:14:41:39 | !... | Guards.cs:41:30:41:30 | access to parameter y | true | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:15:41:39 | !... | Guards.cs:41:30:41:30 | access to parameter y | false | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:17:41:38 | ... && ... | Guards.cs:41:30:41:30 | access to parameter y | true | | Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:30:41:38 | ... != ... | Guards.cs:41:30:41:30 | access to parameter y | true | | Guards.cs:48:31:48:40 | access to field Field | Guards.cs:47:13:47:25 | ... != ... | Guards.cs:47:13:47:17 | access to field Field | true | | Guards.cs:55:27:55:27 | access to parameter g | Guards.cs:53:13:53:27 | ... == ... | Guards.cs:53:13:53:13 | access to parameter g | false | @@ -85,16 +59,11 @@ | Guards.cs:138:20:138:20 | access to parameter s | Guards.cs:137:13:137:25 | ... is ... | Guards.cs:137:13:137:13 | access to parameter s | true | | Guards.cs:139:16:139:16 | access to parameter s | Guards.cs:137:13:137:25 | ... is ... | Guards.cs:137:13:137:13 | access to parameter s | false | | Guards.cs:146:16:146:16 | access to parameter o | Guards.cs:144:13:144:25 | ... is ... | Guards.cs:144:13:144:13 | access to parameter o | false | -| Guards.cs:169:31:169:31 | access to parameter x | Guards.cs:168:13:168:41 | !... | Guards.cs:168:40:168:40 | access to parameter x | true | | Guards.cs:169:31:169:31 | access to parameter x | Guards.cs:168:14:168:41 | call to method IsNullOrWhiteSpace | Guards.cs:168:40:168:40 | access to parameter x | false | -| Guards.cs:190:31:190:31 | access to parameter s | Guards.cs:189:13:189:25 | !... | Guards.cs:189:24:189:24 | access to parameter s | true | | Guards.cs:190:31:190:31 | access to parameter s | Guards.cs:189:14:189:25 | call to method NullTest1 | Guards.cs:189:24:189:24 | access to parameter s | false | -| Guards.cs:192:31:192:31 | access to parameter s | Guards.cs:191:13:191:25 | !... | Guards.cs:191:24:191:24 | access to parameter s | true | | Guards.cs:192:31:192:31 | access to parameter s | Guards.cs:191:14:191:25 | call to method NullTest2 | Guards.cs:191:24:191:24 | access to parameter s | false | -| Guards.cs:194:31:194:31 | access to parameter s | Guards.cs:193:13:193:25 | !... | Guards.cs:193:24:193:24 | access to parameter s | true | | Guards.cs:194:31:194:31 | access to parameter s | Guards.cs:193:14:193:25 | call to method NullTest3 | Guards.cs:193:24:193:24 | access to parameter s | false | | Guards.cs:196:31:196:31 | access to parameter s | Guards.cs:195:13:195:27 | call to method NotNullTest4 | Guards.cs:195:26:195:26 | access to parameter s | true | -| Guards.cs:198:31:198:31 | access to parameter s | Guards.cs:197:13:197:29 | !... | Guards.cs:197:28:197:28 | access to parameter s | true | | Guards.cs:198:31:198:31 | access to parameter s | Guards.cs:197:14:197:29 | call to method NullTestWrong | Guards.cs:197:28:197:28 | access to parameter s | false | | Guards.cs:205:13:205:13 | access to parameter o | Guards.cs:203:13:203:21 | ... != ... | Guards.cs:203:13:203:13 | access to parameter o | true | | Guards.cs:208:17:208:17 | access to parameter o | Guards.cs:203:13:203:21 | ... != ... | Guards.cs:203:13:203:13 | access to parameter o | true | @@ -102,7 +71,6 @@ | Guards.cs:271:13:271:14 | access to parameter o1 | Guards.cs:270:13:270:42 | call to operator == | Guards.cs:270:13:270:14 | access to parameter o1 | true | | Guards.cs:342:27:342:27 | access to parameter b | Guards.cs:341:20:341:20 | access to parameter b | Guards.cs:341:20:341:20 | access to parameter b | false | | Guards.cs:343:31:343:31 | access to local variable s | Guards.cs:342:13:342:21 | ... != ... | Guards.cs:342:13:342:13 | access to local variable s | true | -| Guards.cs:343:31:343:31 | access to local variable s | Guards.cs:342:13:342:27 | ... && ... | Guards.cs:342:13:342:13 | access to local variable s | true | | Guards.cs:349:13:349:13 | access to parameter o | Guards.cs:348:13:348:25 | ... is ... | Guards.cs:348:13:348:13 | access to parameter o | true | | Splitting.cs:13:17:13:17 | access to parameter o | Splitting.cs:12:17:12:25 | ... != ... | Splitting.cs:12:17:12:17 | access to parameter o | true | | Splitting.cs:23:24:23:24 | access to parameter o | Splitting.cs:22:17:22:25 | ... != ... | Splitting.cs:22:17:22:17 | access to parameter o | true | diff --git a/csharp/ql/test/library-tests/controlflow/guards/GuardedControlFlowNode.expected b/csharp/ql/test/library-tests/controlflow/guards/GuardedControlFlowNode.expected index 3db5997056c..404c0197556 100644 --- a/csharp/ql/test/library-tests/controlflow/guards/GuardedControlFlowNode.expected +++ b/csharp/ql/test/library-tests/controlflow/guards/GuardedControlFlowNode.expected @@ -17,7 +17,6 @@ | Assert.cs:59:36:59:36 | [b (line 56): true] access to parameter b | Assert.cs:58:20:58:32 | ... ? ... : ... | Assert.cs:58:20:58:20 | access to parameter b | non-null | | Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:23 | access to local variable s | Assert.cs:59:23:59:23 | access to local variable s | non-null | | Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:31 | ... != ... | Assert.cs:59:23:59:23 | access to local variable s | true | -| Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:36 | ... && ... | Assert.cs:59:23:59:23 | access to local variable s | true | | Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | false | | Assert.cs:66:37:66:37 | [b (line 63): false] access to parameter b | Assert.cs:65:20:65:32 | ... ? ... : ... | Assert.cs:65:20:65:20 | access to parameter b | non-null | | Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | false | @@ -25,7 +24,6 @@ | Assert.cs:66:37:66:37 | [b (line 63): true] access to parameter b | Assert.cs:65:20:65:32 | ... ? ... : ... | Assert.cs:65:20:65:20 | access to parameter b | non-null | | Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:24 | access to local variable s | Assert.cs:66:24:66:24 | access to local variable s | non-null | | Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:32 | ... == ... | Assert.cs:66:24:66:24 | access to local variable s | false | -| Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:37 | ... \|\| ... | Assert.cs:66:24:66:24 | access to local variable s | false | | Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | false | | Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | true | | Assert.cs:73:36:73:36 | [b (line 70): false] access to parameter b | Assert.cs:72:20:72:32 | ... ? ... : ... | Assert.cs:72:20:72:20 | access to parameter b | null | @@ -33,7 +31,6 @@ | Assert.cs:73:36:73:36 | [b (line 70): true] access to parameter b | Assert.cs:72:20:72:32 | ... ? ... : ... | Assert.cs:72:20:72:20 | access to parameter b | null | | Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:23 | access to local variable s | Assert.cs:73:23:73:23 | access to local variable s | null | | Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:31 | ... == ... | Assert.cs:73:23:73:23 | access to local variable s | true | -| Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:36 | ... && ... | Assert.cs:73:23:73:23 | access to local variable s | true | | Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | false | | Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | true | | Assert.cs:80:37:80:37 | [b (line 77): false] access to parameter b | Assert.cs:79:20:79:32 | ... ? ... : ... | Assert.cs:79:20:79:20 | access to parameter b | null | @@ -41,7 +38,6 @@ | Assert.cs:80:37:80:37 | [b (line 77): true] access to parameter b | Assert.cs:79:20:79:32 | ... ? ... : ... | Assert.cs:79:20:79:20 | access to parameter b | null | | Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:24 | access to local variable s | Assert.cs:80:24:80:24 | access to local variable s | null | | Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:32 | ... != ... | Assert.cs:80:24:80:24 | access to local variable s | false | -| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:37 | ... \|\| ... | Assert.cs:80:24:80:24 | access to local variable s | false | | Assert.cs:93:33:93:34 | [assertion failure, b1 (line 91): false] access to parameter b2 | Assert.cs:93:29:93:30 | access to parameter b2 | Assert.cs:93:29:93:30 | access to parameter b2 | false | | Assert.cs:93:33:93:34 | [assertion failure, b1 (line 91): false] access to parameter b2 | Assert.cs:93:29:93:30 | access to parameter b2 | Assert.cs:93:29:93:30 | access to parameter b2 | true | | Assert.cs:93:33:93:34 | [assertion failure, b1 (line 91): true] access to parameter b2 | Assert.cs:93:29:93:30 | access to parameter b2 | Assert.cs:93:29:93:30 | access to parameter b2 | true | @@ -56,49 +52,27 @@ | Collections.cs:67:13:67:13 | access to local variable x | Collections.cs:65:13:65:24 | ... == ... | Collections.cs:65:13:65:13 | access to local variable x | true | | Collections.cs:68:13:68:13 | access to local variable x | Collections.cs:65:13:65:24 | ... == ... | Collections.cs:65:13:65:13 | access to local variable x | true | | Collections.cs:96:31:96:34 | access to parameter args | Collections.cs:95:29:95:32 | access to parameter args | Collections.cs:95:29:95:32 | access to parameter args | non-empty | -| Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:13:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | false | -| Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:14:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | true | | Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | non-null | | Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:16:10:24 | ... == ... | Guards.cs:10:16:10:16 | access to parameter s | false | -| Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:13:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | false | -| Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:14:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | true | | Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | non-null | | Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:16:10:24 | ... == ... | Guards.cs:10:16:10:16 | access to parameter s | false | | Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:12:13:12:24 | ... > ... | Guards.cs:12:13:12:13 | access to parameter s | true | | Guards.cs:26:31:26:31 | access to parameter s | Guards.cs:24:13:24:13 | access to parameter s | Guards.cs:24:13:24:13 | access to parameter s | non-null | | Guards.cs:26:31:26:31 | access to parameter s | Guards.cs:24:13:24:21 | ... != ... | Guards.cs:24:13:24:13 | access to parameter s | true | -| Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:13:32:36 | !... | Guards.cs:32:35:32:35 | access to parameter x | true | -| Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:13:32:51 | ... & ... | Guards.cs:32:35:32:35 | access to parameter x | true | | Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:14:32:36 | call to method IsNullOrEmpty | Guards.cs:32:35:32:35 | access to parameter x | false | | Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:35:32:35 | access to parameter x | Guards.cs:32:35:32:35 | access to parameter x | non-null | -| Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:13:32:51 | ... & ... | Guards.cs:32:42:32:42 | access to parameter y | true | -| Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:40:32:51 | !... | Guards.cs:32:42:32:42 | access to parameter y | true | | Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:42:32:42 | access to parameter y | Guards.cs:32:42:32:42 | access to parameter y | non-null | | Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:42:32:50 | ... == ... | Guards.cs:32:42:32:42 | access to parameter y | false | | Guards.cs:36:32:36:32 | access to parameter x | Guards.cs:35:13:35:13 | access to parameter x | Guards.cs:35:13:35:13 | access to parameter x | non-null | | Guards.cs:36:32:36:32 | access to parameter x | Guards.cs:35:13:35:21 | ... == ... | Guards.cs:35:13:35:13 | access to parameter x | false | -| Guards.cs:36:32:36:32 | access to parameter x | Guards.cs:35:13:35:34 | ... \|\| ... | Guards.cs:35:13:35:13 | access to parameter x | false | -| Guards.cs:36:36:36:36 | access to parameter y | Guards.cs:35:13:35:34 | ... \|\| ... | Guards.cs:35:26:35:26 | access to parameter y | false | | Guards.cs:36:36:36:36 | access to parameter y | Guards.cs:35:26:35:26 | access to parameter y | Guards.cs:35:26:35:26 | access to parameter y | non-null | | Guards.cs:36:36:36:36 | access to parameter y | Guards.cs:35:26:35:34 | ... == ... | Guards.cs:35:26:35:26 | access to parameter y | false | -| Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:13:38:37 | !... | Guards.cs:38:15:38:15 | access to parameter x | true | | Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:15:38:15 | access to parameter x | Guards.cs:38:15:38:15 | access to parameter x | non-null | | Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:15:38:23 | ... == ... | Guards.cs:38:15:38:15 | access to parameter x | false | -| Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:15:38:36 | ... \|\| ... | Guards.cs:38:15:38:15 | access to parameter x | false | -| Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:13:38:37 | !... | Guards.cs:38:28:38:28 | access to parameter y | true | -| Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:15:38:36 | ... \|\| ... | Guards.cs:38:28:38:28 | access to parameter y | false | | Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:28:38:28 | access to parameter y | Guards.cs:38:28:38:28 | access to parameter y | non-null | | Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:28:38:36 | ... == ... | Guards.cs:38:28:38:28 | access to parameter y | false | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:13:41:39 | !... | Guards.cs:41:17:41:17 | access to parameter x | false | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:14:41:39 | !... | Guards.cs:41:17:41:17 | access to parameter x | true | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:15:41:39 | !... | Guards.cs:41:17:41:17 | access to parameter x | false | | Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:17:41:17 | access to parameter x | Guards.cs:41:17:41:17 | access to parameter x | non-null | | Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:17:41:25 | ... != ... | Guards.cs:41:17:41:17 | access to parameter x | true | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:17:41:38 | ... && ... | Guards.cs:41:17:41:17 | access to parameter x | true | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:13:41:39 | !... | Guards.cs:41:30:41:30 | access to parameter y | false | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:14:41:39 | !... | Guards.cs:41:30:41:30 | access to parameter y | true | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:15:41:39 | !... | Guards.cs:41:30:41:30 | access to parameter y | false | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:17:41:38 | ... && ... | Guards.cs:41:30:41:30 | access to parameter y | true | | Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:30:41:30 | access to parameter y | Guards.cs:41:30:41:30 | access to parameter y | non-null | | Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:30:41:38 | ... != ... | Guards.cs:41:30:41:30 | access to parameter y | true | | Guards.cs:48:31:48:40 | access to field Field | Guards.cs:47:13:47:17 | access to field Field | Guards.cs:47:13:47:17 | access to field Field | non-null | @@ -193,21 +167,16 @@ | Guards.cs:162:24:162:24 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | non-match access to type Action | | Guards.cs:162:24:162:24 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | non-match null | | Guards.cs:162:24:162:24 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | non-null | -| Guards.cs:169:31:169:31 | access to parameter x | Guards.cs:168:13:168:41 | !... | Guards.cs:168:40:168:40 | access to parameter x | true | | Guards.cs:169:31:169:31 | access to parameter x | Guards.cs:168:14:168:41 | call to method IsNullOrWhiteSpace | Guards.cs:168:40:168:40 | access to parameter x | false | | Guards.cs:169:31:169:31 | access to parameter x | Guards.cs:168:40:168:40 | access to parameter x | Guards.cs:168:40:168:40 | access to parameter x | non-null | -| Guards.cs:190:31:190:31 | access to parameter s | Guards.cs:189:13:189:25 | !... | Guards.cs:189:24:189:24 | access to parameter s | true | | Guards.cs:190:31:190:31 | access to parameter s | Guards.cs:189:14:189:25 | call to method NullTest1 | Guards.cs:189:24:189:24 | access to parameter s | false | | Guards.cs:190:31:190:31 | access to parameter s | Guards.cs:189:24:189:24 | access to parameter s | Guards.cs:189:24:189:24 | access to parameter s | non-null | -| Guards.cs:192:31:192:31 | access to parameter s | Guards.cs:191:13:191:25 | !... | Guards.cs:191:24:191:24 | access to parameter s | true | | Guards.cs:192:31:192:31 | access to parameter s | Guards.cs:191:14:191:25 | call to method NullTest2 | Guards.cs:191:24:191:24 | access to parameter s | false | | Guards.cs:192:31:192:31 | access to parameter s | Guards.cs:191:24:191:24 | access to parameter s | Guards.cs:191:24:191:24 | access to parameter s | non-null | -| Guards.cs:194:31:194:31 | access to parameter s | Guards.cs:193:13:193:25 | !... | Guards.cs:193:24:193:24 | access to parameter s | true | | Guards.cs:194:31:194:31 | access to parameter s | Guards.cs:193:14:193:25 | call to method NullTest3 | Guards.cs:193:24:193:24 | access to parameter s | false | | Guards.cs:194:31:194:31 | access to parameter s | Guards.cs:193:24:193:24 | access to parameter s | Guards.cs:193:24:193:24 | access to parameter s | non-null | | Guards.cs:196:31:196:31 | access to parameter s | Guards.cs:195:13:195:27 | call to method NotNullTest4 | Guards.cs:195:26:195:26 | access to parameter s | true | | Guards.cs:196:31:196:31 | access to parameter s | Guards.cs:195:26:195:26 | access to parameter s | Guards.cs:195:26:195:26 | access to parameter s | non-null | -| Guards.cs:198:31:198:31 | access to parameter s | Guards.cs:197:13:197:29 | !... | Guards.cs:197:28:197:28 | access to parameter s | true | | Guards.cs:198:31:198:31 | access to parameter s | Guards.cs:197:14:197:29 | call to method NullTestWrong | Guards.cs:197:28:197:28 | access to parameter s | false | | Guards.cs:205:13:205:13 | access to parameter o | Guards.cs:203:13:203:13 | access to parameter o | Guards.cs:203:13:203:13 | access to parameter o | non-null | | Guards.cs:205:13:205:13 | access to parameter o | Guards.cs:203:13:203:21 | ... != ... | Guards.cs:203:13:203:13 | access to parameter o | true | @@ -241,7 +210,6 @@ | Guards.cs:342:27:342:27 | [b (line 339): true] access to parameter b | Guards.cs:341:20:341:32 | ... ? ... : ... | Guards.cs:341:20:341:20 | access to parameter b | non-null | | Guards.cs:343:31:343:31 | access to local variable s | Guards.cs:342:13:342:13 | access to local variable s | Guards.cs:342:13:342:13 | access to local variable s | non-null | | Guards.cs:343:31:343:31 | access to local variable s | Guards.cs:342:13:342:21 | ... != ... | Guards.cs:342:13:342:13 | access to local variable s | true | -| Guards.cs:343:31:343:31 | access to local variable s | Guards.cs:342:13:342:27 | ... && ... | Guards.cs:342:13:342:13 | access to local variable s | true | | Guards.cs:349:13:349:13 | access to parameter o | Guards.cs:348:13:348:13 | access to parameter o | Guards.cs:348:13:348:13 | access to parameter o | non-null | | Guards.cs:349:13:349:13 | access to parameter o | Guards.cs:348:13:348:25 | ... is ... | Guards.cs:348:13:348:13 | access to parameter o | true | | Splitting.cs:13:17:13:17 | [b (line 9): true] access to parameter o | Splitting.cs:12:17:12:17 | access to parameter o | Splitting.cs:12:17:12:17 | access to parameter o | non-null | diff --git a/csharp/ql/test/library-tests/controlflow/guards/GuardedExpr.expected b/csharp/ql/test/library-tests/controlflow/guards/GuardedExpr.expected index aad97097f21..bd5e3dbf1a1 100644 --- a/csharp/ql/test/library-tests/controlflow/guards/GuardedExpr.expected +++ b/csharp/ql/test/library-tests/controlflow/guards/GuardedExpr.expected @@ -14,22 +14,18 @@ | Assert.cs:59:36:59:36 | access to parameter b | Assert.cs:58:20:58:32 | ... ? ... : ... | Assert.cs:58:20:58:20 | access to parameter b | non-null | | Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:23 | access to local variable s | Assert.cs:59:23:59:23 | access to local variable s | non-null | | Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:31 | ... != ... | Assert.cs:59:23:59:23 | access to local variable s | true | -| Assert.cs:60:27:60:27 | access to local variable s | Assert.cs:59:23:59:36 | ... && ... | Assert.cs:59:23:59:23 | access to local variable s | true | | Assert.cs:66:37:66:37 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | Assert.cs:65:20:65:20 | access to parameter b | false | | Assert.cs:66:37:66:37 | access to parameter b | Assert.cs:65:20:65:32 | ... ? ... : ... | Assert.cs:65:20:65:20 | access to parameter b | non-null | | Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:24 | access to local variable s | Assert.cs:66:24:66:24 | access to local variable s | non-null | | Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:32 | ... == ... | Assert.cs:66:24:66:24 | access to local variable s | false | -| Assert.cs:67:27:67:27 | access to local variable s | Assert.cs:66:24:66:37 | ... \|\| ... | Assert.cs:66:24:66:24 | access to local variable s | false | | Assert.cs:73:36:73:36 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | Assert.cs:72:20:72:20 | access to parameter b | true | | Assert.cs:73:36:73:36 | access to parameter b | Assert.cs:72:20:72:32 | ... ? ... : ... | Assert.cs:72:20:72:20 | access to parameter b | null | | Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:23 | access to local variable s | Assert.cs:73:23:73:23 | access to local variable s | null | | Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:31 | ... == ... | Assert.cs:73:23:73:23 | access to local variable s | true | -| Assert.cs:74:27:74:27 | access to local variable s | Assert.cs:73:23:73:36 | ... && ... | Assert.cs:73:23:73:23 | access to local variable s | true | | Assert.cs:80:37:80:37 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | Assert.cs:79:20:79:20 | access to parameter b | true | | Assert.cs:80:37:80:37 | access to parameter b | Assert.cs:79:20:79:32 | ... ? ... : ... | Assert.cs:79:20:79:20 | access to parameter b | null | | Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:24 | access to local variable s | Assert.cs:80:24:80:24 | access to local variable s | null | | Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:32 | ... != ... | Assert.cs:80:24:80:24 | access to local variable s | false | -| Assert.cs:81:27:81:27 | access to local variable s | Assert.cs:80:24:80:37 | ... \|\| ... | Assert.cs:80:24:80:24 | access to local variable s | false | | Assert.cs:94:16:94:17 | access to parameter b1 | Assert.cs:93:25:93:26 | access to parameter b1 | Assert.cs:93:25:93:26 | access to parameter b1 | true | | Assert.cs:94:23:94:24 | access to parameter b2 | Assert.cs:93:29:93:30 | access to parameter b2 | Assert.cs:93:29:93:30 | access to parameter b2 | false | | Collections.cs:52:17:52:20 | access to parameter args | Collections.cs:50:13:50:16 | access to parameter args | Collections.cs:50:13:50:16 | access to parameter args | non-empty | @@ -40,49 +36,27 @@ | Collections.cs:67:13:67:13 | access to local variable x | Collections.cs:65:13:65:24 | ... == ... | Collections.cs:65:13:65:13 | access to local variable x | true | | Collections.cs:68:13:68:13 | access to local variable x | Collections.cs:65:13:65:24 | ... == ... | Collections.cs:65:13:65:13 | access to local variable x | true | | Collections.cs:96:31:96:34 | access to parameter args | Collections.cs:95:29:95:32 | access to parameter args | Collections.cs:95:29:95:32 | access to parameter args | non-empty | -| Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:13:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | false | -| Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:14:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | true | | Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | non-null | | Guards.cs:12:13:12:13 | access to parameter s | Guards.cs:10:16:10:24 | ... == ... | Guards.cs:10:16:10:16 | access to parameter s | false | -| Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:13:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | false | -| Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:14:10:25 | !... | Guards.cs:10:16:10:16 | access to parameter s | true | | Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | Guards.cs:10:16:10:16 | access to parameter s | non-null | | Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:10:16:10:24 | ... == ... | Guards.cs:10:16:10:16 | access to parameter s | false | | Guards.cs:14:31:14:31 | access to parameter s | Guards.cs:12:13:12:24 | ... > ... | Guards.cs:12:13:12:13 | access to parameter s | true | | Guards.cs:26:31:26:31 | access to parameter s | Guards.cs:24:13:24:13 | access to parameter s | Guards.cs:24:13:24:13 | access to parameter s | non-null | | Guards.cs:26:31:26:31 | access to parameter s | Guards.cs:24:13:24:21 | ... != ... | Guards.cs:24:13:24:13 | access to parameter s | true | -| Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:13:32:36 | !... | Guards.cs:32:35:32:35 | access to parameter x | true | -| Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:13:32:51 | ... & ... | Guards.cs:32:35:32:35 | access to parameter x | true | | Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:14:32:36 | call to method IsNullOrEmpty | Guards.cs:32:35:32:35 | access to parameter x | false | | Guards.cs:33:31:33:31 | access to parameter x | Guards.cs:32:35:32:35 | access to parameter x | Guards.cs:32:35:32:35 | access to parameter x | non-null | -| Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:13:32:51 | ... & ... | Guards.cs:32:42:32:42 | access to parameter y | true | -| Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:40:32:51 | !... | Guards.cs:32:42:32:42 | access to parameter y | true | | Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:42:32:42 | access to parameter y | Guards.cs:32:42:32:42 | access to parameter y | non-null | | Guards.cs:33:35:33:35 | access to parameter y | Guards.cs:32:42:32:50 | ... == ... | Guards.cs:32:42:32:42 | access to parameter y | false | | Guards.cs:36:32:36:32 | access to parameter x | Guards.cs:35:13:35:13 | access to parameter x | Guards.cs:35:13:35:13 | access to parameter x | non-null | | Guards.cs:36:32:36:32 | access to parameter x | Guards.cs:35:13:35:21 | ... == ... | Guards.cs:35:13:35:13 | access to parameter x | false | -| Guards.cs:36:32:36:32 | access to parameter x | Guards.cs:35:13:35:34 | ... \|\| ... | Guards.cs:35:13:35:13 | access to parameter x | false | -| Guards.cs:36:36:36:36 | access to parameter y | Guards.cs:35:13:35:34 | ... \|\| ... | Guards.cs:35:26:35:26 | access to parameter y | false | | Guards.cs:36:36:36:36 | access to parameter y | Guards.cs:35:26:35:26 | access to parameter y | Guards.cs:35:26:35:26 | access to parameter y | non-null | | Guards.cs:36:36:36:36 | access to parameter y | Guards.cs:35:26:35:34 | ... == ... | Guards.cs:35:26:35:26 | access to parameter y | false | -| Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:13:38:37 | !... | Guards.cs:38:15:38:15 | access to parameter x | true | | Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:15:38:15 | access to parameter x | Guards.cs:38:15:38:15 | access to parameter x | non-null | | Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:15:38:23 | ... == ... | Guards.cs:38:15:38:15 | access to parameter x | false | -| Guards.cs:39:31:39:31 | access to parameter x | Guards.cs:38:15:38:36 | ... \|\| ... | Guards.cs:38:15:38:15 | access to parameter x | false | -| Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:13:38:37 | !... | Guards.cs:38:28:38:28 | access to parameter y | true | -| Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:15:38:36 | ... \|\| ... | Guards.cs:38:28:38:28 | access to parameter y | false | | Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:28:38:28 | access to parameter y | Guards.cs:38:28:38:28 | access to parameter y | non-null | | Guards.cs:39:35:39:35 | access to parameter y | Guards.cs:38:28:38:36 | ... == ... | Guards.cs:38:28:38:28 | access to parameter y | false | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:13:41:39 | !... | Guards.cs:41:17:41:17 | access to parameter x | false | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:14:41:39 | !... | Guards.cs:41:17:41:17 | access to parameter x | true | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:15:41:39 | !... | Guards.cs:41:17:41:17 | access to parameter x | false | | Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:17:41:17 | access to parameter x | Guards.cs:41:17:41:17 | access to parameter x | non-null | | Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:17:41:25 | ... != ... | Guards.cs:41:17:41:17 | access to parameter x | true | -| Guards.cs:42:32:42:32 | access to parameter x | Guards.cs:41:17:41:38 | ... && ... | Guards.cs:41:17:41:17 | access to parameter x | true | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:13:41:39 | !... | Guards.cs:41:30:41:30 | access to parameter y | false | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:14:41:39 | !... | Guards.cs:41:30:41:30 | access to parameter y | true | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:15:41:39 | !... | Guards.cs:41:30:41:30 | access to parameter y | false | -| Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:17:41:38 | ... && ... | Guards.cs:41:30:41:30 | access to parameter y | true | | Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:30:41:30 | access to parameter y | Guards.cs:41:30:41:30 | access to parameter y | non-null | | Guards.cs:42:36:42:36 | access to parameter y | Guards.cs:41:30:41:38 | ... != ... | Guards.cs:41:30:41:30 | access to parameter y | true | | Guards.cs:48:31:48:40 | access to field Field | Guards.cs:47:13:47:17 | access to field Field | Guards.cs:47:13:47:17 | access to field Field | non-null | @@ -177,21 +151,16 @@ | Guards.cs:162:24:162:24 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | non-match access to type Action | | Guards.cs:162:24:162:24 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | non-match null | | Guards.cs:162:24:162:24 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | non-null | -| Guards.cs:169:31:169:31 | access to parameter x | Guards.cs:168:13:168:41 | !... | Guards.cs:168:40:168:40 | access to parameter x | true | | Guards.cs:169:31:169:31 | access to parameter x | Guards.cs:168:14:168:41 | call to method IsNullOrWhiteSpace | Guards.cs:168:40:168:40 | access to parameter x | false | | Guards.cs:169:31:169:31 | access to parameter x | Guards.cs:168:40:168:40 | access to parameter x | Guards.cs:168:40:168:40 | access to parameter x | non-null | -| Guards.cs:190:31:190:31 | access to parameter s | Guards.cs:189:13:189:25 | !... | Guards.cs:189:24:189:24 | access to parameter s | true | | Guards.cs:190:31:190:31 | access to parameter s | Guards.cs:189:14:189:25 | call to method NullTest1 | Guards.cs:189:24:189:24 | access to parameter s | false | | Guards.cs:190:31:190:31 | access to parameter s | Guards.cs:189:24:189:24 | access to parameter s | Guards.cs:189:24:189:24 | access to parameter s | non-null | -| Guards.cs:192:31:192:31 | access to parameter s | Guards.cs:191:13:191:25 | !... | Guards.cs:191:24:191:24 | access to parameter s | true | | Guards.cs:192:31:192:31 | access to parameter s | Guards.cs:191:14:191:25 | call to method NullTest2 | Guards.cs:191:24:191:24 | access to parameter s | false | | Guards.cs:192:31:192:31 | access to parameter s | Guards.cs:191:24:191:24 | access to parameter s | Guards.cs:191:24:191:24 | access to parameter s | non-null | -| Guards.cs:194:31:194:31 | access to parameter s | Guards.cs:193:13:193:25 | !... | Guards.cs:193:24:193:24 | access to parameter s | true | | Guards.cs:194:31:194:31 | access to parameter s | Guards.cs:193:14:193:25 | call to method NullTest3 | Guards.cs:193:24:193:24 | access to parameter s | false | | Guards.cs:194:31:194:31 | access to parameter s | Guards.cs:193:24:193:24 | access to parameter s | Guards.cs:193:24:193:24 | access to parameter s | non-null | | Guards.cs:196:31:196:31 | access to parameter s | Guards.cs:195:13:195:27 | call to method NotNullTest4 | Guards.cs:195:26:195:26 | access to parameter s | true | | Guards.cs:196:31:196:31 | access to parameter s | Guards.cs:195:26:195:26 | access to parameter s | Guards.cs:195:26:195:26 | access to parameter s | non-null | -| Guards.cs:198:31:198:31 | access to parameter s | Guards.cs:197:13:197:29 | !... | Guards.cs:197:28:197:28 | access to parameter s | true | | Guards.cs:198:31:198:31 | access to parameter s | Guards.cs:197:14:197:29 | call to method NullTestWrong | Guards.cs:197:28:197:28 | access to parameter s | false | | Guards.cs:205:13:205:13 | access to parameter o | Guards.cs:203:13:203:13 | access to parameter o | Guards.cs:203:13:203:13 | access to parameter o | non-null | | Guards.cs:205:13:205:13 | access to parameter o | Guards.cs:203:13:203:21 | ... != ... | Guards.cs:203:13:203:13 | access to parameter o | true | @@ -222,7 +191,6 @@ | Guards.cs:342:27:342:27 | access to parameter b | Guards.cs:341:20:341:32 | ... ? ... : ... | Guards.cs:341:20:341:20 | access to parameter b | non-null | | Guards.cs:343:31:343:31 | access to local variable s | Guards.cs:342:13:342:13 | access to local variable s | Guards.cs:342:13:342:13 | access to local variable s | non-null | | Guards.cs:343:31:343:31 | access to local variable s | Guards.cs:342:13:342:21 | ... != ... | Guards.cs:342:13:342:13 | access to local variable s | true | -| Guards.cs:343:31:343:31 | access to local variable s | Guards.cs:342:13:342:27 | ... && ... | Guards.cs:342:13:342:13 | access to local variable s | true | | Guards.cs:349:13:349:13 | access to parameter o | Guards.cs:348:13:348:13 | access to parameter o | Guards.cs:348:13:348:13 | access to parameter o | non-null | | Guards.cs:349:13:349:13 | access to parameter o | Guards.cs:348:13:348:25 | ... is ... | Guards.cs:348:13:348:13 | access to parameter o | true | | Splitting.cs:13:17:13:17 | access to parameter o | Splitting.cs:12:17:12:17 | access to parameter o | Splitting.cs:12:17:12:17 | access to parameter o | non-null | diff --git a/csharp/ql/test/library-tests/controlflow/splits/SplittingStressTest.ql b/csharp/ql/test/library-tests/controlflow/splits/SplittingStressTest.ql index 4a07618c373..2c637ff0db1 100644 --- a/csharp/ql/test/library-tests/controlflow/splits/SplittingStressTest.ql +++ b/csharp/ql/test/library-tests/controlflow/splits/SplittingStressTest.ql @@ -1,6 +1,7 @@ import csharp query predicate countSplits(ControlFlowElement cfe, int i) { + not cfe.fromLibrary() and i = strictcount(ControlFlow::Nodes::ElementNode n | n.getElement() = cfe) } diff --git a/csharp/ql/test/library-tests/conversion/reftype/RefType.expected b/csharp/ql/test/library-tests/conversion/reftype/RefType.expected index cdcfcf78f96..88f34c17293 100644 --- a/csharp/ql/test/library-tests/conversion/reftype/RefType.expected +++ b/csharp/ql/test/library-tests/conversion/reftype/RefType.expected @@ -1,5 +1,7 @@ | Byte[] | Object | | Byte[] | dynamic | +| Byte[][] | Object | +| Byte[][] | dynamic | | C1 | Object | | C1 | dynamic | | C1[] | ICollection | @@ -183,6 +185,8 @@ | IReadOnlyList | dynamic | | Int16[] | Object | | Int16[] | dynamic | +| Int32[,] | Object | +| Int32[,] | dynamic | | Int32[] | Object | | Int32[] | dynamic | | Int64[] | Object | @@ -221,12 +225,15 @@ | T5 | C1 | | T5 | Object | | T5 | dynamic | +| UInt16[][] | Object | +| UInt16[][] | dynamic | | UInt32[] | Object | | UInt32[] | dynamic | | UInt64[] | Object | | UInt64[] | dynamic | | dynamic | Object | | null | Byte[] | +| null | Byte[][] | | null | C1 | | null | C1[] | | null | C2 | @@ -279,6 +286,7 @@ | null | IReadOnlyList | | null | IReadOnlyList | | null | Int16[] | +| null | Int32[,] | | null | Int32[] | | null | Int64[] | | null | Object | @@ -289,6 +297,7 @@ | null | T4 | | null | T4[] | | null | T5 | +| null | UInt16[][] | | null | UInt32[] | | null | UInt64[] | | null | dynamic | diff --git a/csharp/ql/test/library-tests/csharp7.1/DefaultLiterals.ql b/csharp/ql/test/library-tests/csharp7.1/DefaultLiterals.ql index 63e5a69b6e5..578b7e7fef4 100644 --- a/csharp/ql/test/library-tests/csharp7.1/DefaultLiterals.ql +++ b/csharp/ql/test/library-tests/csharp7.1/DefaultLiterals.ql @@ -1,4 +1,5 @@ import csharp from DefaultValueExpr l +where l.fromSource() select l, l.getValue() diff --git a/csharp/ql/test/library-tests/csharp7/LocalTaintFlow.ql b/csharp/ql/test/library-tests/csharp7/LocalTaintFlow.ql index 22f61aacdd8..ed293efc2ba 100644 --- a/csharp/ql/test/library-tests/csharp7/LocalTaintFlow.ql +++ b/csharp/ql/test/library-tests/csharp7/LocalTaintFlow.ql @@ -2,5 +2,7 @@ import csharp import semmle.code.csharp.dataflow.TaintTracking from DataFlow::Node pred, DataFlow::Node succ -where TaintTracking::localTaintStep(pred, succ) +where + TaintTracking::localTaintStep(pred, succ) and + not pred.asExpr().fromLibrary() select pred, succ diff --git a/csharp/ql/test/library-tests/csharp7/TupleTypes.expected b/csharp/ql/test/library-tests/csharp7/TupleTypes.expected index 480fb5639aa..dfb6bca2890 100644 --- a/csharp/ql/test/library-tests/csharp7/TupleTypes.expected +++ b/csharp/ql/test/library-tests/csharp7/TupleTypes.expected @@ -3,8 +3,8 @@ | (Int32,Double) | (int, double) | ValueTuple | 2 | 0 | CSharp7.cs:215:6:215:8 | Item1 | | (Int32,Double) | (int, double) | ValueTuple | 2 | 1 | CSharp7.cs:215:11:215:16 | Item2 | | (Int32,Int32) | (int, int) | ValueTuple | 2 | 0 | CSharp7.cs:64:10:64:10 | Item1 | -| (Int32,Int32) | (int, int) | ValueTuple | 2 | 1 | file://:0:0:0:0 | Item2 | +| (Int32,Int32) | (int, int) | ValueTuple | 2 | 1 | CSharp7.cs:64:17:64:17 | Item2 | | (String,Int32) | (string, int) | ValueTuple | 2 | 0 | CSharp7.cs:84:17:84:17 | Item1 | -| (String,Int32) | (string, int) | ValueTuple | 2 | 1 | file://:0:0:0:0 | Item2 | +| (String,Int32) | (string, int) | ValueTuple | 2 | 1 | CSharp7.cs:84:23:84:23 | Item2 | | (String,String) | (string, string) | ValueTuple | 2 | 0 | CSharp7.cs:89:19:89:27 | Item1 | | (String,String) | (string, string) | ValueTuple | 2 | 1 | CSharp7.cs:89:30:89:33 | Item2 | diff --git a/csharp/ql/test/library-tests/csharp8/UnmanagedGenericStructs.ql b/csharp/ql/test/library-tests/csharp8/UnmanagedGenericStructs.ql index 316e9458ae6..640454a71f9 100644 --- a/csharp/ql/test/library-tests/csharp8/UnmanagedGenericStructs.ql +++ b/csharp/ql/test/library-tests/csharp8/UnmanagedGenericStructs.ql @@ -1,5 +1,5 @@ import csharp from TypeParameter tp -where tp.getConstraints().hasUnmanagedTypeConstraint() +where tp.getConstraints().hasUnmanagedTypeConstraint() and tp.fromSource() select tp, "This type parameter is unmanaged." diff --git a/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected b/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected index ce0ed3c75af..db98e8b9d0b 100644 --- a/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected +++ b/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected @@ -167,7 +167,9 @@ | Splitting.cs:32:9:32:16 | [b (line 24): false] dynamic call to method Check | normal | Splitting.cs:32:9:32:16 | [b (line 24): false] dynamic call to method Check | | Splitting.cs:32:9:32:16 | [b (line 24): true] dynamic call to method Check | normal | Splitting.cs:32:9:32:16 | [b (line 24): true] dynamic call to method Check | | Splitting.cs:34:13:34:20 | dynamic call to method Check | normal | Splitting.cs:34:13:34:20 | dynamic call to method Check | +| This.cs:7:5:7:8 | call to constructor Object | normal | This.cs:7:5:7:8 | call to constructor Object | | This.cs:17:9:17:18 | object creation of type This | normal | This.cs:17:9:17:18 | object creation of type This | +| This.cs:22:9:22:11 | call to constructor This | normal | This.cs:22:9:22:11 | call to constructor This | | This.cs:28:13:28:21 | object creation of type Sub | normal | This.cs:28:13:28:21 | object creation of type Sub | | file://:0:0:0:0 | [summary] call to collectionSelector in SelectMany | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 1 in SelectMany | | file://:0:0:0:0 | [summary] call to collectionSelector in SelectMany | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 1 in SelectMany | @@ -225,6 +227,10 @@ | file://:0:0:0:0 | [summary] call to continuationFunction in ContinueWith | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in ContinueWith | | file://:0:0:0:0 | [summary] call to continuationFunction in ContinueWith | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in ContinueWith | | file://:0:0:0:0 | [summary] call to continuationFunction in ContinueWith | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in ContinueWith | +| file://:0:0:0:0 | [summary] call to continuationFunction in ContinueWith | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in ContinueWith | +| file://:0:0:0:0 | [summary] call to continuationFunction in ContinueWith | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in ContinueWith | +| file://:0:0:0:0 | [summary] call to continuationFunction in ContinueWith | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in ContinueWith | +| file://:0:0:0:0 | [summary] call to continuationFunction in ContinueWith | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in ContinueWith | | file://:0:0:0:0 | [summary] call to elementSelector in GroupBy | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 2 in GroupBy | | file://:0:0:0:0 | [summary] call to elementSelector in GroupBy | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 2 in GroupBy | | file://:0:0:0:0 | [summary] call to elementSelector in GroupBy | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 2 in GroupBy | @@ -311,3 +317,5 @@ | file://:0:0:0:0 | [summary] call to valueFactory in Lazy | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in Lazy | | file://:0:0:0:0 | [summary] call to valueFactory in Lazy | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in Lazy | | file://:0:0:0:0 | [summary] call to valueFactory in Lazy | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in Lazy | +| file://:0:0:0:0 | [summary] call to valueFactory in Lazy | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in Lazy | +| file://:0:0:0:0 | [summary] call to valueSelector in Task | normal | file://:0:0:0:0 | [summary] read: return (normal) of argument 0 in Task | diff --git a/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected b/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected index 30b6ee553a1..10499656cb1 100644 --- a/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected +++ b/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected @@ -1,96 +1,4 @@ | MS.Internal.Xml.Linq.ComponentModel.XDeferredAxis<>.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5, T6, T7, T8) | argument 0 -> field Item1 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5, T6, T7, T8) | argument 1 -> field Item2 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5, T6, T7, T8) | argument 2 -> field Item3 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5, T6, T7, T8) | argument 3 -> field Item4 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5, T6, T7, T8) | argument 4 -> field Item5 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5, T6, T7, T8) | argument 5 -> field Item6 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5, T6, T7, T8) | argument 6 -> field Item7 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5, T6, T7, T8) | argument 7 -> field Item8 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5, T6, T7) | argument 0 -> field Item1 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5, T6, T7) | argument 1 -> field Item2 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5, T6, T7) | argument 2 -> field Item3 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5, T6, T7) | argument 3 -> field Item4 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5, T6, T7) | argument 4 -> field Item5 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5, T6, T7) | argument 5 -> field Item6 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5, T6, T7) | argument 6 -> field Item7 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5, T6) | argument 0 -> field Item1 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5, T6) | argument 1 -> field Item2 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5, T6) | argument 2 -> field Item3 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5, T6) | argument 3 -> field Item4 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5, T6) | argument 4 -> field Item5 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5, T6) | argument 5 -> field Item6 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5) | argument 0 -> field Item1 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5) | argument 1 -> field Item2 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5) | argument 2 -> field Item3 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5) | argument 3 -> field Item4 of return (normal) | true | -| System.().Create(T1, T2, T3, T4, T5) | argument 4 -> field Item5 of return (normal) | true | -| System.().Create(T1, T2, T3, T4) | argument 0 -> field Item1 of return (normal) | true | -| System.().Create(T1, T2, T3, T4) | argument 1 -> field Item2 of return (normal) | true | -| System.().Create(T1, T2, T3, T4) | argument 2 -> field Item3 of return (normal) | true | -| System.().Create(T1, T2, T3, T4) | argument 3 -> field Item4 of return (normal) | true | -| System.().Create(T1, T2, T3) | argument 0 -> field Item1 of return (normal) | true | -| System.().Create(T1, T2, T3) | argument 1 -> field Item2 of return (normal) | true | -| System.().Create(T1, T2, T3) | argument 2 -> field Item3 of return (normal) | true | -| System.().Create(T1, T2) | argument 0 -> field Item1 of return (normal) | true | -| System.().Create(T1, T2) | argument 1 -> field Item2 of return (normal) | true | -| System.().Create(T1) | argument 0 -> field Item1 of return (normal) | true | -| System.(T1).ValueTuple(T1) | argument 0 -> field Item1 of return (normal) | true | -| System.(T1).get_Item(int) | field Item1 of argument -1 -> return (normal) | true | -| System.(T1,T2).ValueTuple(T1, T2) | argument 0 -> field Item1 of return (normal) | true | -| System.(T1,T2).ValueTuple(T1, T2) | argument 1 -> field Item2 of return (normal) | true | -| System.(T1,T2).get_Item(int) | field Item1 of argument -1 -> return (normal) | true | -| System.(T1,T2).get_Item(int) | field Item2 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3).ValueTuple(T1, T2, T3) | argument 0 -> field Item1 of return (normal) | true | -| System.(T1,T2,T3).ValueTuple(T1, T2, T3) | argument 1 -> field Item2 of return (normal) | true | -| System.(T1,T2,T3).ValueTuple(T1, T2, T3) | argument 2 -> field Item3 of return (normal) | true | -| System.(T1,T2,T3).get_Item(int) | field Item1 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3).get_Item(int) | field Item2 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3).get_Item(int) | field Item3 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4).ValueTuple(T1, T2, T3, T4) | argument 0 -> field Item1 of return (normal) | true | -| System.(T1,T2,T3,T4).ValueTuple(T1, T2, T3, T4) | argument 1 -> field Item2 of return (normal) | true | -| System.(T1,T2,T3,T4).ValueTuple(T1, T2, T3, T4) | argument 2 -> field Item3 of return (normal) | true | -| System.(T1,T2,T3,T4).ValueTuple(T1, T2, T3, T4) | argument 3 -> field Item4 of return (normal) | true | -| System.(T1,T2,T3,T4).get_Item(int) | field Item1 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4).get_Item(int) | field Item2 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4).get_Item(int) | field Item3 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4).get_Item(int) | field Item4 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4,T5).ValueTuple(T1, T2, T3, T4, T5) | argument 0 -> field Item1 of return (normal) | true | -| System.(T1,T2,T3,T4,T5).ValueTuple(T1, T2, T3, T4, T5) | argument 1 -> field Item2 of return (normal) | true | -| System.(T1,T2,T3,T4,T5).ValueTuple(T1, T2, T3, T4, T5) | argument 2 -> field Item3 of return (normal) | true | -| System.(T1,T2,T3,T4,T5).ValueTuple(T1, T2, T3, T4, T5) | argument 3 -> field Item4 of return (normal) | true | -| System.(T1,T2,T3,T4,T5).ValueTuple(T1, T2, T3, T4, T5) | argument 4 -> field Item5 of return (normal) | true | -| System.(T1,T2,T3,T4,T5).get_Item(int) | field Item1 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4,T5).get_Item(int) | field Item2 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4,T5).get_Item(int) | field Item3 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4,T5).get_Item(int) | field Item4 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4,T5).get_Item(int) | field Item5 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6).ValueTuple(T1, T2, T3, T4, T5, T6) | argument 0 -> field Item1 of return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6).ValueTuple(T1, T2, T3, T4, T5, T6) | argument 1 -> field Item2 of return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6).ValueTuple(T1, T2, T3, T4, T5, T6) | argument 2 -> field Item3 of return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6).ValueTuple(T1, T2, T3, T4, T5, T6) | argument 3 -> field Item4 of return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6).ValueTuple(T1, T2, T3, T4, T5, T6) | argument 4 -> field Item5 of return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6).ValueTuple(T1, T2, T3, T4, T5, T6) | argument 5 -> field Item6 of return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6).get_Item(int) | field Item1 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6).get_Item(int) | field Item2 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6).get_Item(int) | field Item3 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6).get_Item(int) | field Item4 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6).get_Item(int) | field Item5 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6).get_Item(int) | field Item6 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6,T7).ValueTuple(T1, T2, T3, T4, T5, T6, T7) | argument 0 -> field Item1 of return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6,T7).ValueTuple(T1, T2, T3, T4, T5, T6, T7) | argument 1 -> field Item2 of return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6,T7).ValueTuple(T1, T2, T3, T4, T5, T6, T7) | argument 2 -> field Item3 of return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6,T7).ValueTuple(T1, T2, T3, T4, T5, T6, T7) | argument 3 -> field Item4 of return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6,T7).ValueTuple(T1, T2, T3, T4, T5, T6, T7) | argument 4 -> field Item5 of return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6,T7).ValueTuple(T1, T2, T3, T4, T5, T6, T7) | argument 5 -> field Item6 of return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6,T7).ValueTuple(T1, T2, T3, T4, T5, T6, T7) | argument 6 -> field Item7 of return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6,T7).get_Item(int) | field Item1 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6,T7).get_Item(int) | field Item2 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6,T7).get_Item(int) | field Item3 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6,T7).get_Item(int) | field Item4 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6,T7).get_Item(int) | field Item5 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6,T7).get_Item(int) | field Item6 of argument -1 -> return (normal) | true | -| System.(T1,T2,T3,T4,T5,T6,T7).get_Item(int) | field Item7 of argument -1 -> return (normal) | true | | System.Array.Add(object) | argument 0 -> element of argument -1 | true | | System.Array.AsReadOnly(T[]) | element of argument 0 -> element of return (normal) | true | | System.Array.Clone() | element of argument 0 -> element of return (normal) | true | @@ -255,6 +163,7 @@ | System.Collections.Concurrent.ConcurrentStack<>.CopyTo(Array, int) | element of argument -1 -> element of return (out parameter 0) | true | | System.Collections.Concurrent.ConcurrentStack<>.CopyTo(T[], int) | element of argument -1 -> element of return (out parameter 0) | true | | System.Collections.Concurrent.ConcurrentStack<>.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | +| System.Collections.Concurrent.ConcurrentStack<>.GetEnumerator(Node) | element of argument -1 -> property Current of return (normal) | true | | System.Collections.Concurrent.IProducerConsumerCollection<>.CopyTo(T[], int) | element of argument -1 -> element of return (out parameter 0) | true | | System.Collections.Concurrent.OrderablePartitioner<>.EnumerableDropIndices.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | | System.Collections.Concurrent.Partitioner.d__7.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | @@ -405,6 +314,7 @@ | System.Collections.Generic.SortedList<,>.Add(object, object) | argument 1 -> property Value of element of argument -1 | true | | System.Collections.Generic.SortedList<,>.CopyTo(Array, int) | element of argument -1 -> element of return (out parameter 0) | true | | System.Collections.Generic.SortedList<,>.CopyTo(KeyValuePair[], int) | element of argument -1 -> element of return (out parameter 0) | true | +| System.Collections.Generic.SortedList<,>.GetByIndex(int) | property Value of element of argument -1 -> return (normal) | true | | System.Collections.Generic.SortedList<,>.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | | System.Collections.Generic.SortedList<,>.KeyList.Add(TKey) | argument 0 -> element of argument -1 | true | | System.Collections.Generic.SortedList<,>.KeyList.CopyTo(Array, int) | element of argument -1 -> element of return (out parameter 0) | true | @@ -467,6 +377,8 @@ | System.Collections.Hashtable.SyncHashtable.Clone() | element of argument 0 -> element of return (normal) | true | | System.Collections.Hashtable.SyncHashtable.CopyTo(Array, int) | element of argument -1 -> element of return (out parameter 0) | true | | System.Collections.Hashtable.SyncHashtable.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | +| System.Collections.Hashtable.SyncHashtable.SyncHashtable(Hashtable) | property Key of element of argument 0 -> property Key of element of return (normal) | true | +| System.Collections.Hashtable.SyncHashtable.SyncHashtable(Hashtable) | property Value of element of argument 0 -> property Value of element of return (normal) | true | | System.Collections.Hashtable.SyncHashtable.get_Item(object) | property Value of element of argument -1 -> return (normal) | true | | System.Collections.Hashtable.SyncHashtable.get_Keys() | property Key of element of argument -1 -> element of return (normal) | true | | System.Collections.Hashtable.SyncHashtable.get_Values() | property Value of element of argument -1 -> element of return (normal) | true | @@ -585,6 +497,8 @@ | System.Collections.SortedList.SyncSortedList.CopyTo(Array, int) | element of argument -1 -> element of return (out parameter 0) | true | | System.Collections.SortedList.SyncSortedList.GetByIndex(int) | property Value of element of argument -1 -> return (normal) | true | | System.Collections.SortedList.SyncSortedList.GetValueList() | property Value of element of argument -1 -> element of return (normal) | true | +| System.Collections.SortedList.SyncSortedList.SyncSortedList(SortedList) | property Key of element of argument 0 -> property Key of element of return (normal) | true | +| System.Collections.SortedList.SyncSortedList.SyncSortedList(SortedList) | property Value of element of argument 0 -> property Value of element of return (normal) | true | | System.Collections.SortedList.SyncSortedList.get_Item(object) | property Value of element of argument -1 -> return (normal) | true | | System.Collections.SortedList.SyncSortedList.set_Item(object, object) | argument 0 -> property Key of element of argument -1 | true | | System.Collections.SortedList.SyncSortedList.set_Item(object, object) | argument 1 -> property Value of element of argument -1 | true | @@ -633,6 +547,8 @@ | System.Collections.Specialized.OrderedDictionary.AsReadOnly() | element of argument 0 -> element of return (normal) | true | | System.Collections.Specialized.OrderedDictionary.CopyTo(Array, int) | element of argument -1 -> element of return (out parameter 0) | true | | System.Collections.Specialized.OrderedDictionary.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | +| System.Collections.Specialized.OrderedDictionary.OrderedDictionary(OrderedDictionary) | property Key of element of argument 0 -> property Key of element of return (normal) | true | +| System.Collections.Specialized.OrderedDictionary.OrderedDictionary(OrderedDictionary) | property Value of element of argument 0 -> property Value of element of return (normal) | true | | System.Collections.Specialized.OrderedDictionary.OrderedDictionaryKeyValueCollection.CopyTo(Array, int) | element of argument -1 -> element of return (out parameter 0) | true | | System.Collections.Specialized.OrderedDictionary.OrderedDictionaryKeyValueCollection.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | | System.Collections.Specialized.OrderedDictionary.get_Item(int) | property Value of element of argument -1 -> return (normal) | true | @@ -725,6 +641,10 @@ | System.ComponentModel.PropertyDescriptorCollection.PropertyDescriptorCollection(PropertyDescriptor[]) | property Value of element of argument 0 -> property Value of element of return (normal) | true | | System.ComponentModel.PropertyDescriptorCollection.PropertyDescriptorCollection(PropertyDescriptor[], bool) | property Key of element of argument 0 -> property Key of element of return (normal) | true | | System.ComponentModel.PropertyDescriptorCollection.PropertyDescriptorCollection(PropertyDescriptor[], bool) | property Value of element of argument 0 -> property Value of element of return (normal) | true | +| System.ComponentModel.PropertyDescriptorCollection.PropertyDescriptorCollection(PropertyDescriptor[], int, String[], IComparer) | property Key of element of argument 0 -> property Key of element of return (normal) | true | +| System.ComponentModel.PropertyDescriptorCollection.PropertyDescriptorCollection(PropertyDescriptor[], int, String[], IComparer) | property Key of element of argument 2 -> property Key of element of return (normal) | true | +| System.ComponentModel.PropertyDescriptorCollection.PropertyDescriptorCollection(PropertyDescriptor[], int, String[], IComparer) | property Value of element of argument 0 -> property Value of element of return (normal) | true | +| System.ComponentModel.PropertyDescriptorCollection.PropertyDescriptorCollection(PropertyDescriptor[], int, String[], IComparer) | property Value of element of argument 2 -> property Value of element of return (normal) | true | | System.ComponentModel.PropertyDescriptorCollection.get_Item(int) | element of argument -1 -> return (normal) | true | | System.ComponentModel.PropertyDescriptorCollection.get_Item(int) | property Value of element of argument -1 -> return (normal) | true | | System.ComponentModel.PropertyDescriptorCollection.get_Item(object) | element of argument -1 -> return (normal) | true | @@ -747,12 +667,28 @@ | System.Convert.ChangeType(object, Type, IFormatProvider) | argument 0 -> return (normal) | false | | System.Convert.ChangeType(object, TypeCode) | argument 0 -> return (normal) | false | | System.Convert.ChangeType(object, TypeCode, IFormatProvider) | argument 0 -> return (normal) | false | +| System.Convert.ConvertToBase64Array(char*, byte*, int, int, bool) | argument 0 -> return (normal) | false | +| System.Convert.CopyToTempBufferWithoutWhiteSpace(ReadOnlySpan, Span, out int, out int) | argument 0 -> return (normal) | false | +| System.Convert.Decode(ref char, ref sbyte) | argument 0 -> return (normal) | false | +| System.Convert.DefaultToType(IConvertible, Type, IFormatProvider) | argument 0 -> return (normal) | false | | System.Convert.FromBase64CharArray(Char[], int, int) | argument 0 -> return (normal) | false | +| System.Convert.FromBase64CharPtr(char*, int) | argument 0 -> return (normal) | false | | System.Convert.FromBase64String(string) | argument 0 -> return (normal) | false | +| System.Convert.FromBase64_ComputeResultLength(char*, int) | argument 0 -> return (normal) | false | | System.Convert.FromHexString(ReadOnlySpan) | argument 0 -> return (normal) | false | | System.Convert.FromHexString(string) | argument 0 -> return (normal) | false | | System.Convert.GetTypeCode(object) | argument 0 -> return (normal) | false | | System.Convert.IsDBNull(object) | argument 0 -> return (normal) | false | +| System.Convert.IsSpace(char) | argument 0 -> return (normal) | false | +| System.Convert.ThrowByteOverflowException() | argument 0 -> return (normal) | false | +| System.Convert.ThrowCharOverflowException() | argument 0 -> return (normal) | false | +| System.Convert.ThrowInt16OverflowException() | argument 0 -> return (normal) | false | +| System.Convert.ThrowInt32OverflowException() | argument 0 -> return (normal) | false | +| System.Convert.ThrowInt64OverflowException() | argument 0 -> return (normal) | false | +| System.Convert.ThrowSByteOverflowException() | argument 0 -> return (normal) | false | +| System.Convert.ThrowUInt16OverflowException() | argument 0 -> return (normal) | false | +| System.Convert.ThrowUInt32OverflowException() | argument 0 -> return (normal) | false | +| System.Convert.ThrowUInt64OverflowException() | argument 0 -> return (normal) | false | | System.Convert.ToBase64CharArray(Byte[], int, int, Char[], int) | argument 0 -> return (normal) | false | | System.Convert.ToBase64CharArray(Byte[], int, int, Char[], int, Base64FormattingOptions) | argument 0 -> return (normal) | false | | System.Convert.ToBase64String(Byte[]) | argument 0 -> return (normal) | false | @@ -760,6 +696,7 @@ | System.Convert.ToBase64String(Byte[], int, int) | argument 0 -> return (normal) | false | | System.Convert.ToBase64String(Byte[], int, int, Base64FormattingOptions) | argument 0 -> return (normal) | false | | System.Convert.ToBase64String(ReadOnlySpan, Base64FormattingOptions) | argument 0 -> return (normal) | false | +| System.Convert.ToBase64_CalculateAndValidateOutputLength(int, bool) | argument 0 -> return (normal) | false | | System.Convert.ToBoolean(DateTime) | argument 0 -> return (normal) | false | | System.Convert.ToBoolean(bool) | argument 0 -> return (normal) | false | | System.Convert.ToBoolean(byte) | argument 0 -> return (normal) | false | @@ -1059,9 +996,11 @@ | System.Convert.ToUInt64(uint) | argument 0 -> return (normal) | false | | System.Convert.ToUInt64(ulong) | argument 0 -> return (normal) | false | | System.Convert.ToUInt64(ushort) | argument 0 -> return (normal) | false | +| System.Convert.TryDecodeFromUtf16(ReadOnlySpan, Span, out int, out int) | argument 0 -> return (normal) | false | | System.Convert.TryFromBase64Chars(ReadOnlySpan, Span, out int) | argument 0 -> return (normal) | false | | System.Convert.TryFromBase64String(string, Span, out int) | argument 0 -> return (normal) | false | | System.Convert.TryToBase64Chars(ReadOnlySpan, Span, out int, Base64FormattingOptions) | argument 0 -> return (normal) | false | +| System.Convert.WriteThreeLowOrderBytes(ref byte, int) | argument 0 -> return (normal) | false | | System.Diagnostics.Tracing.CounterPayload.d__51.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | | System.Diagnostics.Tracing.CounterPayload.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | | System.Diagnostics.Tracing.EventPayload.Add(KeyValuePair) | argument 0 -> element of argument -1 | true | @@ -1070,6 +1009,10 @@ | System.Diagnostics.Tracing.EventPayload.Add(string, object) | argument 0 -> property Key of element of argument -1 | true | | System.Diagnostics.Tracing.EventPayload.Add(string, object) | argument 1 -> property Value of element of argument -1 | true | | System.Diagnostics.Tracing.EventPayload.CopyTo(KeyValuePair[], int) | element of argument -1 -> element of return (out parameter 0) | true | +| System.Diagnostics.Tracing.EventPayload.EventPayload(List, List) | property Key of element of argument 0 -> property Key of element of return (normal) | true | +| System.Diagnostics.Tracing.EventPayload.EventPayload(List, List) | property Key of element of argument 1 -> property Key of element of return (normal) | true | +| System.Diagnostics.Tracing.EventPayload.EventPayload(List, List) | property Value of element of argument 0 -> property Value of element of return (normal) | true | +| System.Diagnostics.Tracing.EventPayload.EventPayload(List, List) | property Value of element of argument 1 -> property Value of element of return (normal) | true | | System.Diagnostics.Tracing.EventPayload.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | | System.Diagnostics.Tracing.EventPayload.get_Item(string) | property Value of element of argument -1 -> return (normal) | true | | System.Diagnostics.Tracing.EventPayload.get_Keys() | property Key of element of argument -1 -> element of return (normal) | true | @@ -1126,8 +1069,11 @@ | System.IO.Compression.DeflateStream.CopyToStream.WriteAsync(Byte[], int, int, CancellationToken) | argument 0 -> argument -1 | false | | System.IO.Compression.DeflateStream.DeflateStream(Stream, CompressionLevel) | argument 0 -> return (normal) | false | | System.IO.Compression.DeflateStream.DeflateStream(Stream, CompressionLevel, bool) | argument 0 -> return (normal) | false | +| System.IO.Compression.DeflateStream.DeflateStream(Stream, CompressionLevel, bool, int) | argument 0 -> return (normal) | false | | System.IO.Compression.DeflateStream.DeflateStream(Stream, CompressionMode) | argument 0 -> return (normal) | false | | System.IO.Compression.DeflateStream.DeflateStream(Stream, CompressionMode, bool) | argument 0 -> return (normal) | false | +| System.IO.Compression.DeflateStream.DeflateStream(Stream, CompressionMode, bool, int, long) | argument 0 -> return (normal) | false | +| System.IO.Compression.DeflateStream.DeflateStream(Stream, CompressionMode, long) | argument 0 -> return (normal) | false | | System.IO.Compression.DeflateStream.Read(Byte[], int, int) | argument -1 -> return (out parameter 0) | false | | System.IO.Compression.DeflateStream.ReadAsync(Byte[], int, int, CancellationToken) | argument -1 -> return (out parameter 0) | false | | System.IO.Compression.DeflateStream.Write(Byte[], int, int) | argument 0 -> argument -1 | false | @@ -1181,6 +1127,7 @@ | System.IO.Path.Combine(string, string, string, string) | argument 3 -> return (normal) | false | | System.IO.Path.GetDirectoryName(ReadOnlySpan) | argument 0 -> return (normal) | false | | System.IO.Path.GetDirectoryName(string) | argument 0 -> return (normal) | false | +| System.IO.Path.GetDirectoryNameOffset(ReadOnlySpan) | argument 0 -> return (normal) | false | | System.IO.Path.GetExtension(ReadOnlySpan) | argument 0 -> return (normal) | false | | System.IO.Path.GetExtension(string) | argument 0 -> return (normal) | false | | System.IO.Path.GetFileName(ReadOnlySpan) | argument 0 -> return (normal) | false | @@ -1192,20 +1139,28 @@ | System.IO.Path.GetPathRoot(ReadOnlySpan) | argument 0 -> return (normal) | false | | System.IO.Path.GetPathRoot(string) | argument 0 -> return (normal) | false | | System.IO.Path.GetRelativePath(string, string) | argument 1 -> return (normal) | false | +| System.IO.Path.GetRelativePath(string, string, StringComparison) | argument 1 -> return (normal) | false | | System.IO.Pipes.PipeStream.BeginRead(Byte[], int, int, AsyncCallback, object) | argument -1 -> return (out parameter 0) | false | | System.IO.Pipes.PipeStream.BeginWrite(Byte[], int, int, AsyncCallback, object) | argument 0 -> argument -1 | false | | System.IO.Pipes.PipeStream.Read(Byte[], int, int) | argument -1 -> return (out parameter 0) | false | | System.IO.Pipes.PipeStream.ReadAsync(Byte[], int, int, CancellationToken) | argument -1 -> return (out parameter 0) | false | | System.IO.Pipes.PipeStream.Write(Byte[], int, int) | argument 0 -> argument -1 | false | | System.IO.Pipes.PipeStream.WriteAsync(Byte[], int, int, CancellationToken) | argument 0 -> argument -1 | false | +| System.IO.Stream.BeginEndReadAsync(Byte[], int, int) | argument -1 -> return (out parameter 0) | false | +| System.IO.Stream.BeginEndWriteAsync(Byte[], int, int) | argument 0 -> argument -1 | false | | System.IO.Stream.BeginRead(Byte[], int, int, AsyncCallback, object) | argument -1 -> return (out parameter 0) | false | +| System.IO.Stream.BeginReadInternal(Byte[], int, int, AsyncCallback, object, bool, bool) | argument -1 -> return (out parameter 0) | false | | System.IO.Stream.BeginWrite(Byte[], int, int, AsyncCallback, object) | argument 0 -> argument -1 | false | +| System.IO.Stream.BeginWriteInternal(Byte[], int, int, AsyncCallback, object, bool, bool) | argument 0 -> argument -1 | false | +| System.IO.Stream.BlockingBeginRead(Byte[], int, int, AsyncCallback, object) | argument -1 -> return (out parameter 0) | false | +| System.IO.Stream.BlockingBeginWrite(Byte[], int, int, AsyncCallback, object) | argument 0 -> argument -1 | false | | System.IO.Stream.CopyTo(Stream) | argument -1 -> return (out parameter 0) | false | | System.IO.Stream.CopyTo(Stream, int) | argument -1 -> return (out parameter 0) | false | | System.IO.Stream.CopyToAsync(Stream) | argument -1 -> return (out parameter 0) | false | | System.IO.Stream.CopyToAsync(Stream, CancellationToken) | argument -1 -> return (out parameter 0) | false | | System.IO.Stream.CopyToAsync(Stream, int) | argument -1 -> return (out parameter 0) | false | | System.IO.Stream.CopyToAsync(Stream, int, CancellationToken) | argument -1 -> return (out parameter 0) | false | +| System.IO.Stream.CopyToAsyncInternal(Stream, int, CancellationToken) | argument -1 -> return (out parameter 0) | false | | System.IO.Stream.NullStream.BeginRead(Byte[], int, int, AsyncCallback, object) | argument -1 -> return (out parameter 0) | false | | System.IO.Stream.NullStream.BeginWrite(Byte[], int, int, AsyncCallback, object) | argument 0 -> argument -1 | false | | System.IO.Stream.NullStream.CopyTo(Stream, int) | argument -1 -> return (out parameter 0) | false | @@ -1247,6 +1202,7 @@ | System.IO.TextReader.ReadBlock(Span) | argument -1 -> return (normal) | false | | System.IO.TextReader.ReadBlockAsync(Char[], int, int) | argument -1 -> return (normal) | false | | System.IO.TextReader.ReadBlockAsync(Memory, CancellationToken) | argument -1 -> return (normal) | false | +| System.IO.TextReader.ReadBlockAsyncInternal(Memory, CancellationToken) | argument -1 -> return (normal) | false | | System.IO.TextReader.ReadLine() | argument -1 -> return (normal) | false | | System.IO.TextReader.ReadLineAsync() | argument -1 -> return (normal) | false | | System.IO.TextReader.ReadToEnd() | argument -1 -> return (normal) | false | @@ -1271,6 +1227,7 @@ | System.Int32.TryParse(string, out int) | argument 0 -> return (out parameter 1) | false | | System.Lazy<>.Lazy(Func) | return (normal) of argument 0 -> property Value of return (normal) | true | | System.Lazy<>.Lazy(Func, LazyThreadSafetyMode) | return (normal) of argument 0 -> property Value of return (normal) | true | +| System.Lazy<>.Lazy(Func, LazyThreadSafetyMode, bool) | return (normal) of argument 0 -> property Value of return (normal) | true | | System.Lazy<>.Lazy(Func, bool) | return (normal) of argument 0 -> property Value of return (normal) | true | | System.Lazy<>.get_Value() | argument -1 -> return (normal) | false | | System.Linq.EmptyPartition<>.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | @@ -1529,13 +1486,16 @@ | System.Linq.Lookup<,>.d__19<>.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | | System.Linq.Lookup<,>.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | | System.Linq.OrderedEnumerable<>.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | +| System.Linq.OrderedEnumerable<>.GetEnumerator(int, int) | element of argument -1 -> property Current of return (normal) | true | | System.Linq.OrderedPartition<>.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | | System.Linq.Parallel.CancellableEnumerable.d__0<>.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | | System.Linq.Parallel.EnumerableWrapperWeakToStrong.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | | System.Linq.Parallel.ExceptionAggregator.d__0<>.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | | System.Linq.Parallel.ExceptionAggregator.d__1<,>.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | | System.Linq.Parallel.GroupByGrouping<,>.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | +| System.Linq.Parallel.ListChunk<>.Add(TInputOutput) | argument 0 -> element of argument -1 | true | | System.Linq.Parallel.ListChunk<>.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | +| System.Linq.Parallel.Lookup<,>.Add(IGrouping) | argument 0 -> element of argument -1 | true | | System.Linq.Parallel.Lookup<,>.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | | System.Linq.Parallel.MergeExecutor<>.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | | System.Linq.Parallel.OrderedGroupByGrouping<,,>.GetEnumerator() | element of argument -1 -> property Current of return (normal) | true | @@ -1556,8 +1516,12 @@ | System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, TAccumulate, Func) | argument 1 -> parameter 0 of argument 2 | true | | System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, TAccumulate, Func) | element of argument 0 -> parameter 1 of argument 2 | true | | System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, TAccumulate, Func) | return (normal) of argument 2 -> return (normal) | true | +| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, TAccumulate, Func, QueryAggregationOptions) | argument 1 -> parameter 0 of argument 2 | true | +| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, TAccumulate, Func, QueryAggregationOptions) | element of argument 0 -> parameter 1 of argument 2 | true | +| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, TAccumulate, Func, QueryAggregationOptions) | return (normal) of argument 3 -> return (normal) | true | | System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, Func) | element of argument 0 -> parameter 1 of argument 1 | true | | System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, Func) | return (normal) of argument 1 -> return (normal) | true | +| System.Linq.ParallelEnumerable.Aggregate(ParallelQuery, Func, QueryAggregationOptions) | return (normal) of argument 2 -> return (normal) | true | | System.Linq.ParallelEnumerable.All(ParallelQuery, Func) | element of argument 0 -> parameter 0 of argument 1 | true | | System.Linq.ParallelEnumerable.Any(ParallelQuery, Func) | element of argument 0 -> parameter 0 of argument 1 | true | | System.Linq.ParallelEnumerable.AsEnumerable(ParallelQuery) | element of argument 0 -> element of return (normal) | true | @@ -2000,6 +1964,7 @@ | System.Net.Security.SslStream.ReadAsync(Byte[], int, int, CancellationToken) | argument -1 -> return (out parameter 0) | false | | System.Net.Security.SslStream.Write(Byte[], int, int) | argument 0 -> argument -1 | false | | System.Net.Security.SslStream.WriteAsync(Byte[], int, int, CancellationToken) | argument 0 -> argument -1 | false | +| System.Net.WebUtility.HtmlEncode(ReadOnlySpan, ref ValueStringBuilder) | argument 0 -> return (normal) | false | | System.Net.WebUtility.HtmlEncode(string) | argument 0 -> return (normal) | false | | System.Net.WebUtility.HtmlEncode(string, TextWriter) | argument 0 -> return (normal) | false | | System.Net.WebUtility.UrlEncode(string) | argument 0 -> return (normal) | false | @@ -2211,54 +2176,83 @@ | System.Text.RegularExpressions.MatchCollection.get_Item(int) | element of argument -1 -> return (normal) | true | | System.Text.RegularExpressions.MatchCollection.set_Item(int, Match) | argument 1 -> element of argument -1 | true | | System.Text.RegularExpressions.MatchCollection.set_Item(int, object) | argument 1 -> element of argument -1 | true | +| System.Text.StringBuilder.Append(Char[]) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(Char[]) | element of argument 0 -> element of argument -1 | true | +| System.Text.StringBuilder.Append(Char[], int, int) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(Char[], int, int) | element of argument 0 -> element of argument -1 | true | +| System.Text.StringBuilder.Append(ReadOnlyMemory) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(ReadOnlySpan) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(StringBuilder) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(StringBuilder, int, int) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(bool) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(byte) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(char) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(char*, int) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(char, int) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(decimal) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(double) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(float) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(int) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(long) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.Append(object) | argument 0 -> element of argument -1 | true | -| System.Text.StringBuilder.Append(object) | argument 0 -> element of return (normal) | true | +| System.Text.StringBuilder.Append(object) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(sbyte) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(short) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.Append(string) | argument 0 -> element of argument -1 | true | -| System.Text.StringBuilder.Append(string) | argument 0 -> element of return (normal) | true | +| System.Text.StringBuilder.Append(string) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.Append(string, int, int) | argument 0 -> element of argument -1 | true | -| System.Text.StringBuilder.Append(string, int, int) | argument 0 -> element of return (normal) | true | +| System.Text.StringBuilder.Append(string, int, int) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(uint) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(ulong) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.Append(ushort) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object) | argument 1 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object) | argument 1 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object) | argument 2 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object) | argument 2 -> element of return (normal) | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument 1 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument 1 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument 2 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument 2 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument 3 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument 3 -> element of return (normal) | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 1 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 1 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 2 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 2 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 3 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 3 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 4 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument 4 -> element of return (normal) | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, object, object, object) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.AppendFormat(IFormatProvider, string, params Object[]) | argument 1 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, params Object[]) | argument 1 -> element of return (normal) | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, params Object[]) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.AppendFormat(IFormatProvider, string, params Object[]) | element of argument 2 -> element of argument -1 | true | | System.Text.StringBuilder.AppendFormat(string, object) | argument 0 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(string, object) | argument 0 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(string, object) | argument 1 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(string, object) | argument 1 -> element of return (normal) | true | +| System.Text.StringBuilder.AppendFormat(string, object) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.AppendFormat(string, object, object) | argument 0 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(string, object, object) | argument 0 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(string, object, object) | argument 1 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(string, object, object) | argument 1 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(string, object, object) | argument 2 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(string, object, object) | argument 2 -> element of return (normal) | true | +| System.Text.StringBuilder.AppendFormat(string, object, object) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 0 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 0 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 1 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 1 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 2 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 2 -> element of return (normal) | true | | System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 3 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument 3 -> element of return (normal) | true | +| System.Text.StringBuilder.AppendFormat(string, object, object, object) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.AppendFormat(string, params Object[]) | argument 0 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendFormat(string, params Object[]) | argument 0 -> element of return (normal) | true | +| System.Text.StringBuilder.AppendFormat(string, params Object[]) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.AppendFormat(string, params Object[]) | element of argument 1 -> element of argument -1 | true | +| System.Text.StringBuilder.AppendJoin(char, params Object[]) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.AppendJoin(char, params Object[]) | element of argument 1 -> element of argument -1 | true | +| System.Text.StringBuilder.AppendJoin(char, params String[]) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.AppendJoin(char, params String[]) | element of argument 1 -> element of argument -1 | true | +| System.Text.StringBuilder.AppendJoin(string, params Object[]) | argument 0 -> element of argument -1 | true | +| System.Text.StringBuilder.AppendJoin(string, params Object[]) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.AppendJoin(string, params Object[]) | element of argument 1 -> element of argument -1 | true | +| System.Text.StringBuilder.AppendJoin(string, params String[]) | argument 0 -> element of argument -1 | true | +| System.Text.StringBuilder.AppendJoin(string, params String[]) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.AppendJoin(string, params String[]) | element of argument 1 -> element of argument -1 | true | +| System.Text.StringBuilder.AppendJoin(char, IEnumerable) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.AppendJoin(char, IEnumerable) | element of argument 1 -> element of argument -1 | true | +| System.Text.StringBuilder.AppendJoin(string, IEnumerable) | argument 0 -> element of argument -1 | true | +| System.Text.StringBuilder.AppendJoin(string, IEnumerable) | argument -1 -> return (normal) | true | +| System.Text.StringBuilder.AppendJoin(string, IEnumerable) | element of argument 1 -> element of argument -1 | true | +| System.Text.StringBuilder.AppendLine() | argument -1 -> return (normal) | true | | System.Text.StringBuilder.AppendLine(string) | argument 0 -> element of argument -1 | true | -| System.Text.StringBuilder.AppendLine(string) | argument 0 -> element of return (normal) | true | +| System.Text.StringBuilder.AppendLine(string) | argument -1 -> return (normal) | true | | System.Text.StringBuilder.StringBuilder(string) | argument 0 -> element of return (normal) | true | | System.Text.StringBuilder.StringBuilder(string, int) | argument 0 -> element of return (normal) | true | | System.Text.StringBuilder.StringBuilder(string, int, int, int) | argument 0 -> element of return (normal) | true | @@ -2276,6 +2270,7 @@ | System.Threading.Tasks.Task.ContinueWith(Action, object, CancellationToken, TaskContinuationOptions, TaskScheduler) | argument 1 -> parameter 1 of argument 0 | true | | System.Threading.Tasks.Task.ContinueWith(Action, object, TaskContinuationOptions) | argument 1 -> parameter 1 of argument 0 | true | | System.Threading.Tasks.Task.ContinueWith(Action, object, TaskScheduler) | argument 1 -> parameter 1 of argument 0 | true | +| System.Threading.Tasks.Task.ContinueWith(Action, object, TaskScheduler, CancellationToken, TaskContinuationOptions) | argument 1 -> parameter 1 of argument 0 | true | | System.Threading.Tasks.Task.ContinueWith(Func, object) | argument 1 -> parameter 1 of argument 0 | true | | System.Threading.Tasks.Task.ContinueWith(Func, object) | return (normal) of argument 0 -> property Result of return (normal) | true | | System.Threading.Tasks.Task.ContinueWith(Func, object, CancellationToken) | argument 1 -> parameter 1 of argument 0 | true | @@ -2286,11 +2281,14 @@ | System.Threading.Tasks.Task.ContinueWith(Func, object, TaskContinuationOptions) | return (normal) of argument 0 -> property Result of return (normal) | true | | System.Threading.Tasks.Task.ContinueWith(Func, object, TaskScheduler) | argument 1 -> parameter 1 of argument 0 | true | | System.Threading.Tasks.Task.ContinueWith(Func, object, TaskScheduler) | return (normal) of argument 0 -> property Result of return (normal) | true | +| System.Threading.Tasks.Task.ContinueWith(Func, object, TaskScheduler, CancellationToken, TaskContinuationOptions) | argument 1 -> parameter 1 of argument 0 | true | +| System.Threading.Tasks.Task.ContinueWith(Func, object, TaskScheduler, CancellationToken, TaskContinuationOptions) | return (normal) of argument 0 -> property Result of return (normal) | true | | System.Threading.Tasks.Task.ContinueWith(Func) | return (normal) of argument 0 -> property Result of return (normal) | true | | System.Threading.Tasks.Task.ContinueWith(Func, CancellationToken) | return (normal) of argument 0 -> property Result of return (normal) | true | | System.Threading.Tasks.Task.ContinueWith(Func, CancellationToken, TaskContinuationOptions, TaskScheduler) | return (normal) of argument 0 -> property Result of return (normal) | true | | System.Threading.Tasks.Task.ContinueWith(Func, TaskContinuationOptions) | return (normal) of argument 0 -> property Result of return (normal) | true | | System.Threading.Tasks.Task.ContinueWith(Func, TaskScheduler) | return (normal) of argument 0 -> property Result of return (normal) | true | +| System.Threading.Tasks.Task.ContinueWith(Func, TaskScheduler, CancellationToken, TaskContinuationOptions) | return (normal) of argument 0 -> property Result of return (normal) | true | | System.Threading.Tasks.Task.FromResult(TResult) | argument 0 -> property Result of return (normal) | true | | System.Threading.Tasks.Task.Run(Func) | return (normal) of argument 0 -> property Result of return (normal) | true | | System.Threading.Tasks.Task.Run(Func, CancellationToken) | return (normal) of argument 0 -> property Result of return (normal) | true | @@ -2317,11 +2315,14 @@ | System.Threading.Tasks.Task<>.ContinueWith(Action, Object>, object, TaskContinuationOptions) | argument -1 -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Action, Object>, object, TaskScheduler) | argument 1 -> parameter 1 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Action, Object>, object, TaskScheduler) | argument -1 -> parameter 0 of argument 0 | true | +| System.Threading.Tasks.Task<>.ContinueWith(Action, Object>, object, TaskScheduler, CancellationToken, TaskContinuationOptions) | argument 1 -> parameter 1 of argument 0 | true | +| System.Threading.Tasks.Task<>.ContinueWith(Action, Object>, object, TaskScheduler, CancellationToken, TaskContinuationOptions) | argument -1 -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Action>) | argument -1 -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Action>, CancellationToken) | argument -1 -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Action>, CancellationToken, TaskContinuationOptions, TaskScheduler) | argument -1 -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Action>, TaskContinuationOptions) | argument -1 -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Action>, TaskScheduler) | argument -1 -> parameter 0 of argument 0 | true | +| System.Threading.Tasks.Task<>.ContinueWith(Action>, TaskScheduler, CancellationToken, TaskContinuationOptions) | argument -1 -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object) | argument 1 -> parameter 1 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object) | argument -1 -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object) | return (normal) of argument 0 -> property Result of return (normal) | true | @@ -2337,6 +2338,9 @@ | System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, TaskScheduler) | argument 1 -> parameter 1 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, TaskScheduler) | argument -1 -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, TaskScheduler) | return (normal) of argument 0 -> property Result of return (normal) | true | +| System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, TaskScheduler, CancellationToken, TaskContinuationOptions) | argument 1 -> parameter 1 of argument 0 | true | +| System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, TaskScheduler, CancellationToken, TaskContinuationOptions) | argument -1 -> parameter 0 of argument 0 | true | +| System.Threading.Tasks.Task<>.ContinueWith(Func, Object, TNewResult>, object, TaskScheduler, CancellationToken, TaskContinuationOptions) | return (normal) of argument 0 -> property Result of return (normal) | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>) | argument -1 -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>) | return (normal) of argument 0 -> property Result of return (normal) | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>, CancellationToken) | argument -1 -> parameter 0 of argument 0 | true | @@ -2347,6 +2351,8 @@ | System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>, TaskContinuationOptions) | return (normal) of argument 0 -> property Result of return (normal) | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>, TaskScheduler) | argument -1 -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>, TaskScheduler) | return (normal) of argument 0 -> property Result of return (normal) | true | +| System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>, TaskScheduler, CancellationToken, TaskContinuationOptions) | argument -1 -> parameter 0 of argument 0 | true | +| System.Threading.Tasks.Task<>.ContinueWith(Func, TNewResult>, TaskScheduler, CancellationToken, TaskContinuationOptions) | return (normal) of argument 0 -> property Result of return (normal) | true | | System.Threading.Tasks.Task<>.GetAwaiter() | argument -1 -> field m_task of return (normal) | true | | System.Threading.Tasks.Task<>.Task(Func, object) | argument 1 -> parameter 0 of argument 0 | true | | System.Threading.Tasks.Task<>.Task(Func, object) | return (normal) of argument 0 -> property Result of return (normal) | true | @@ -2359,6 +2365,7 @@ | System.Threading.Tasks.Task<>.Task(Func) | return (normal) of argument 0 -> property Result of return (normal) | true | | System.Threading.Tasks.Task<>.Task(Func, CancellationToken) | return (normal) of argument 0 -> property Result of return (normal) | true | | System.Threading.Tasks.Task<>.Task(Func, CancellationToken, TaskCreationOptions) | return (normal) of argument 0 -> property Result of return (normal) | true | +| System.Threading.Tasks.Task<>.Task(Func, Task, CancellationToken, TaskCreationOptions, InternalTaskOptions, TaskScheduler) | return (normal) of argument 0 -> property Result of return (normal) | true | | System.Threading.Tasks.Task<>.Task(Func, TaskCreationOptions) | return (normal) of argument 0 -> property Result of return (normal) | true | | System.Threading.Tasks.Task<>.get_Result() | argument -1 -> return (normal) | false | | System.Threading.Tasks.TaskFactory.ContinueWhenAll(Task[], Func) | argument 0 -> parameter 0 of argument 1 | true | @@ -2686,6 +2693,41 @@ | System.Uri.get_OriginalString() | argument -1 -> return (normal) | false | | System.Uri.get_PathAndQuery() | argument -1 -> return (normal) | false | | System.Uri.get_Query() | argument -1 -> return (normal) | false | +| System.ValueTuple.Create(T1, T2, T3, T4, T5, T6, T7, T8) | argument 0 -> field Item1 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5, T6, T7, T8) | argument 1 -> field Item2 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5, T6, T7, T8) | argument 2 -> field Item3 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5, T6, T7, T8) | argument 3 -> field Item4 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5, T6, T7, T8) | argument 4 -> field Item5 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5, T6, T7, T8) | argument 5 -> field Item6 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5, T6, T7, T8) | argument 6 -> field Item7 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5, T6, T7) | argument 0 -> field Item1 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5, T6, T7) | argument 1 -> field Item2 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5, T6, T7) | argument 2 -> field Item3 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5, T6, T7) | argument 3 -> field Item4 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5, T6, T7) | argument 4 -> field Item5 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5, T6, T7) | argument 5 -> field Item6 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5, T6, T7) | argument 6 -> field Item7 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5, T6) | argument 0 -> field Item1 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5, T6) | argument 1 -> field Item2 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5, T6) | argument 2 -> field Item3 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5, T6) | argument 3 -> field Item4 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5, T6) | argument 4 -> field Item5 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5, T6) | argument 5 -> field Item6 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5) | argument 0 -> field Item1 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5) | argument 1 -> field Item2 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5) | argument 2 -> field Item3 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5) | argument 3 -> field Item4 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4, T5) | argument 4 -> field Item5 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4) | argument 0 -> field Item1 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4) | argument 1 -> field Item2 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4) | argument 2 -> field Item3 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3, T4) | argument 3 -> field Item4 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3) | argument 0 -> field Item1 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3) | argument 1 -> field Item2 of return (normal) | true | +| System.ValueTuple.Create(T1, T2, T3) | argument 2 -> field Item3 of return (normal) | true | +| System.ValueTuple.Create(T1, T2) | argument 0 -> field Item1 of return (normal) | true | +| System.ValueTuple.Create(T1, T2) | argument 1 -> field Item2 of return (normal) | true | +| System.ValueTuple.Create(T1) | argument 0 -> field Item1 of return (normal) | true | | System.ValueTuple<,,,,,,,>.ValueTuple(T1, T2, T3, T4, T5, T6, T7, TRest) | argument 0 -> field Item1 of return (normal) | true | | System.ValueTuple<,,,,,,,>.ValueTuple(T1, T2, T3, T4, T5, T6, T7, TRest) | argument 1 -> field Item2 of return (normal) | true | | System.ValueTuple<,,,,,,,>.ValueTuple(T1, T2, T3, T4, T5, T6, T7, TRest) | argument 2 -> field Item3 of return (normal) | true | @@ -2700,6 +2742,62 @@ | System.ValueTuple<,,,,,,,>.get_Item(int) | field Item5 of argument -1 -> return (normal) | true | | System.ValueTuple<,,,,,,,>.get_Item(int) | field Item6 of argument -1 -> return (normal) | true | | System.ValueTuple<,,,,,,,>.get_Item(int) | field Item7 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,,,,>.ValueTuple(T1, T2, T3, T4, T5, T6, T7) | argument 0 -> field Item1 of return (normal) | true | +| System.ValueTuple<,,,,,,>.ValueTuple(T1, T2, T3, T4, T5, T6, T7) | argument 1 -> field Item2 of return (normal) | true | +| System.ValueTuple<,,,,,,>.ValueTuple(T1, T2, T3, T4, T5, T6, T7) | argument 2 -> field Item3 of return (normal) | true | +| System.ValueTuple<,,,,,,>.ValueTuple(T1, T2, T3, T4, T5, T6, T7) | argument 3 -> field Item4 of return (normal) | true | +| System.ValueTuple<,,,,,,>.ValueTuple(T1, T2, T3, T4, T5, T6, T7) | argument 4 -> field Item5 of return (normal) | true | +| System.ValueTuple<,,,,,,>.ValueTuple(T1, T2, T3, T4, T5, T6, T7) | argument 5 -> field Item6 of return (normal) | true | +| System.ValueTuple<,,,,,,>.ValueTuple(T1, T2, T3, T4, T5, T6, T7) | argument 6 -> field Item7 of return (normal) | true | +| System.ValueTuple<,,,,,,>.get_Item(int) | field Item1 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,,,,>.get_Item(int) | field Item2 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,,,,>.get_Item(int) | field Item3 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,,,,>.get_Item(int) | field Item4 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,,,,>.get_Item(int) | field Item5 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,,,,>.get_Item(int) | field Item6 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,,,,>.get_Item(int) | field Item7 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,,,>.ValueTuple(T1, T2, T3, T4, T5, T6) | argument 0 -> field Item1 of return (normal) | true | +| System.ValueTuple<,,,,,>.ValueTuple(T1, T2, T3, T4, T5, T6) | argument 1 -> field Item2 of return (normal) | true | +| System.ValueTuple<,,,,,>.ValueTuple(T1, T2, T3, T4, T5, T6) | argument 2 -> field Item3 of return (normal) | true | +| System.ValueTuple<,,,,,>.ValueTuple(T1, T2, T3, T4, T5, T6) | argument 3 -> field Item4 of return (normal) | true | +| System.ValueTuple<,,,,,>.ValueTuple(T1, T2, T3, T4, T5, T6) | argument 4 -> field Item5 of return (normal) | true | +| System.ValueTuple<,,,,,>.ValueTuple(T1, T2, T3, T4, T5, T6) | argument 5 -> field Item6 of return (normal) | true | +| System.ValueTuple<,,,,,>.get_Item(int) | field Item1 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,,,>.get_Item(int) | field Item2 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,,,>.get_Item(int) | field Item3 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,,,>.get_Item(int) | field Item4 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,,,>.get_Item(int) | field Item5 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,,,>.get_Item(int) | field Item6 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,,>.ValueTuple(T1, T2, T3, T4, T5) | argument 0 -> field Item1 of return (normal) | true | +| System.ValueTuple<,,,,>.ValueTuple(T1, T2, T3, T4, T5) | argument 1 -> field Item2 of return (normal) | true | +| System.ValueTuple<,,,,>.ValueTuple(T1, T2, T3, T4, T5) | argument 2 -> field Item3 of return (normal) | true | +| System.ValueTuple<,,,,>.ValueTuple(T1, T2, T3, T4, T5) | argument 3 -> field Item4 of return (normal) | true | +| System.ValueTuple<,,,,>.ValueTuple(T1, T2, T3, T4, T5) | argument 4 -> field Item5 of return (normal) | true | +| System.ValueTuple<,,,,>.get_Item(int) | field Item1 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,,>.get_Item(int) | field Item2 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,,>.get_Item(int) | field Item3 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,,>.get_Item(int) | field Item4 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,,>.get_Item(int) | field Item5 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,>.ValueTuple(T1, T2, T3, T4) | argument 0 -> field Item1 of return (normal) | true | +| System.ValueTuple<,,,>.ValueTuple(T1, T2, T3, T4) | argument 1 -> field Item2 of return (normal) | true | +| System.ValueTuple<,,,>.ValueTuple(T1, T2, T3, T4) | argument 2 -> field Item3 of return (normal) | true | +| System.ValueTuple<,,,>.ValueTuple(T1, T2, T3, T4) | argument 3 -> field Item4 of return (normal) | true | +| System.ValueTuple<,,,>.get_Item(int) | field Item1 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,>.get_Item(int) | field Item2 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,>.get_Item(int) | field Item3 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,,>.get_Item(int) | field Item4 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,>.ValueTuple(T1, T2, T3) | argument 0 -> field Item1 of return (normal) | true | +| System.ValueTuple<,,>.ValueTuple(T1, T2, T3) | argument 1 -> field Item2 of return (normal) | true | +| System.ValueTuple<,,>.ValueTuple(T1, T2, T3) | argument 2 -> field Item3 of return (normal) | true | +| System.ValueTuple<,,>.get_Item(int) | field Item1 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,>.get_Item(int) | field Item2 of argument -1 -> return (normal) | true | +| System.ValueTuple<,,>.get_Item(int) | field Item3 of argument -1 -> return (normal) | true | +| System.ValueTuple<,>.ValueTuple(T1, T2) | argument 0 -> field Item1 of return (normal) | true | +| System.ValueTuple<,>.ValueTuple(T1, T2) | argument 1 -> field Item2 of return (normal) | true | +| System.ValueTuple<,>.get_Item(int) | field Item1 of argument -1 -> return (normal) | true | +| System.ValueTuple<,>.get_Item(int) | field Item2 of argument -1 -> return (normal) | true | +| System.ValueTuple<>.ValueTuple(T1) | argument 0 -> field Item1 of return (normal) | true | +| System.ValueTuple<>.get_Item(int) | field Item1 of argument -1 -> return (normal) | true | | System.Web.HttpCookie.get_Value() | argument -1 -> return (normal) | false | | System.Web.HttpCookie.get_Values() | argument -1 -> return (normal) | false | | System.Web.HttpServerUtility.UrlEncode(string) | argument 0 -> return (normal) | false | diff --git a/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.expected b/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.expected index e6c0dd9beda..f8d274d364d 100644 --- a/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.expected +++ b/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.expected @@ -310,6 +310,7 @@ | LocalDataFlow.cs:234:13:234:42 | SSA def(sink36) | LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | | LocalDataFlow.cs:234:22:234:42 | object creation of type StringBuilder | LocalDataFlow.cs:234:13:234:42 | SSA def(sink36) | | LocalDataFlow.cs:235:9:235:14 | [post] access to local variable sink36 | LocalDataFlow.cs:236:15:236:20 | access to local variable sink36 | +| LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | LocalDataFlow.cs:235:9:235:33 | call to method AppendLine | | LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | LocalDataFlow.cs:236:15:236:20 | access to local variable sink36 | | LocalDataFlow.cs:239:13:239:51 | SSA def(nonSink10) | LocalDataFlow.cs:240:15:240:23 | access to local variable nonSink10 | | LocalDataFlow.cs:239:25:239:51 | object creation of type StringBuilder | LocalDataFlow.cs:239:13:239:51 | SSA def(nonSink10) | @@ -322,6 +323,7 @@ | LocalDataFlow.cs:242:15:242:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | | LocalDataFlow.cs:242:15:242:22 | access to local variable nonSink0 | LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | | LocalDataFlow.cs:243:9:243:17 | [post] access to local variable nonSink10 | LocalDataFlow.cs:244:15:244:23 | access to local variable nonSink10 | +| LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | LocalDataFlow.cs:243:9:243:38 | call to method AppendLine | | LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | LocalDataFlow.cs:244:15:244:23 | access to local variable nonSink10 | | LocalDataFlow.cs:247:13:247:52 | SSA def(taintedDataContract) | LocalDataFlow.cs:248:22:248:40 | access to local variable taintedDataContract | | LocalDataFlow.cs:247:13:247:52 | SSA qualifier def(taintedDataContract.AList) | LocalDataFlow.cs:250:22:250:46 | access to property AList | diff --git a/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.ql b/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.ql index f1fc98c459b..8f820cfea48 100644 --- a/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.ql +++ b/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.ql @@ -1,5 +1,5 @@ import csharp from DataFlow::Node pred, DataFlow::Node succ -where DataFlow::localFlowStep(pred, succ) +where not pred.asExpr().fromLibrary() and DataFlow::localFlowStep(pred, succ) select pred, succ diff --git a/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected b/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected index e20fecfbe55..b5a6e1ba929 100644 --- a/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected +++ b/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected @@ -400,9 +400,9 @@ | LocalDataFlow.cs:234:22:234:42 | object creation of type StringBuilder | LocalDataFlow.cs:234:13:234:42 | SSA def(sink36) | | LocalDataFlow.cs:234:40:234:41 | "" | LocalDataFlow.cs:234:22:234:42 | object creation of type StringBuilder | | LocalDataFlow.cs:235:9:235:14 | [post] access to local variable sink36 | LocalDataFlow.cs:236:15:236:20 | access to local variable sink36 | +| LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | LocalDataFlow.cs:235:9:235:33 | call to method AppendLine | | LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | LocalDataFlow.cs:236:15:236:20 | access to local variable sink36 | | LocalDataFlow.cs:235:27:235:32 | access to local variable sink35 | LocalDataFlow.cs:235:9:235:14 | [post] access to local variable sink36 | -| LocalDataFlow.cs:235:27:235:32 | access to local variable sink35 | LocalDataFlow.cs:235:9:235:33 | call to method AppendLine | | LocalDataFlow.cs:239:13:239:51 | SSA def(nonSink10) | LocalDataFlow.cs:240:15:240:23 | access to local variable nonSink10 | | LocalDataFlow.cs:239:25:239:51 | object creation of type StringBuilder | LocalDataFlow.cs:239:13:239:51 | SSA def(nonSink10) | | LocalDataFlow.cs:239:43:239:50 | access to local variable nonSink0 | LocalDataFlow.cs:239:25:239:51 | object creation of type StringBuilder | @@ -416,9 +416,9 @@ | LocalDataFlow.cs:242:15:242:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | | LocalDataFlow.cs:242:15:242:22 | access to local variable nonSink0 | LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | | LocalDataFlow.cs:243:9:243:17 | [post] access to local variable nonSink10 | LocalDataFlow.cs:244:15:244:23 | access to local variable nonSink10 | +| LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | LocalDataFlow.cs:243:9:243:38 | call to method AppendLine | | LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | LocalDataFlow.cs:244:15:244:23 | access to local variable nonSink10 | | LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | LocalDataFlow.cs:243:9:243:17 | [post] access to local variable nonSink10 | -| LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | LocalDataFlow.cs:243:9:243:38 | call to method AppendLine | | LocalDataFlow.cs:247:13:247:52 | SSA def(taintedDataContract) | LocalDataFlow.cs:248:22:248:40 | access to local variable taintedDataContract | | LocalDataFlow.cs:247:13:247:52 | SSA qualifier def(taintedDataContract.AList) | LocalDataFlow.cs:250:22:250:46 | access to property AList | | LocalDataFlow.cs:247:13:247:52 | SSA qualifier def(taintedDataContract.AString) | LocalDataFlow.cs:248:22:248:48 | access to property AString | diff --git a/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.ql b/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.ql index 1866acfcda8..ffbdcd4dddf 100644 --- a/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.ql +++ b/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.ql @@ -1,5 +1,7 @@ import csharp from DataFlow::Node pred, DataFlow::Node succ -where TaintTracking::localTaintStep(pred, succ) +where + not pred.asExpr().fromLibrary() and + TaintTracking::localTaintStep(pred, succ) select pred, succ diff --git a/csharp/ql/test/library-tests/dataflow/modulusanalysis/ModulusAnalysis.ql b/csharp/ql/test/library-tests/dataflow/modulusanalysis/ModulusAnalysis.ql index b07608ea009..02ffbc535ce 100644 --- a/csharp/ql/test/library-tests/dataflow/modulusanalysis/ModulusAnalysis.ql +++ b/csharp/ql/test/library-tests/dataflow/modulusanalysis/ModulusAnalysis.ql @@ -4,5 +4,7 @@ import semmle.code.csharp.dataflow.ModulusAnalysis import semmle.code.csharp.dataflow.Bound from ControlFlow::Nodes::ExprNode e, Bound b, int delta, int mod -where exprModulus(e, b, delta, mod) +where + not e.getExpr().fromLibrary() and + exprModulus(e, b, delta, mod) select e, b.toString(), delta, mod diff --git a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.ql b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.ql index 5427cdc631d..650d0f6bed4 100644 --- a/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.ql +++ b/csharp/ql/test/library-tests/dataflow/signanalysis/SignAnalysis.ql @@ -18,4 +18,5 @@ string getASignString(ControlFlow::Nodes::ExprNode e) { } from ControlFlow::Nodes::ExprNode e +where not e.getExpr().fromLibrary() select e, strictconcat(string s | s = getASignString(e) | s, " ") diff --git a/csharp/ql/test/library-tests/dataflow/tuples/DataFlowStep.ql b/csharp/ql/test/library-tests/dataflow/tuples/DataFlowStep.ql index f1fc98c459b..8f820cfea48 100644 --- a/csharp/ql/test/library-tests/dataflow/tuples/DataFlowStep.ql +++ b/csharp/ql/test/library-tests/dataflow/tuples/DataFlowStep.ql @@ -1,5 +1,5 @@ import csharp from DataFlow::Node pred, DataFlow::Node succ -where DataFlow::localFlowStep(pred, succ) +where not pred.asExpr().fromLibrary() and DataFlow::localFlowStep(pred, succ) select pred, succ diff --git a/csharp/ql/test/library-tests/diagnostics/A.cs b/csharp/ql/test/library-tests/diagnostics/A.cs new file mode 100644 index 00000000000..21a5b7f5261 --- /dev/null +++ b/csharp/ql/test/library-tests/diagnostics/A.cs @@ -0,0 +1,4 @@ +public class A +{ + public void M() { } +} \ No newline at end of file diff --git a/csharp/ql/test/library-tests/diagnostics/DiagnosticExtractorErrors.expected b/csharp/ql/test/library-tests/diagnostics/DiagnosticExtractorErrors.expected new file mode 100644 index 00000000000..e4ae5ec1ef2 --- /dev/null +++ b/csharp/ql/test/library-tests/diagnostics/DiagnosticExtractorErrors.expected @@ -0,0 +1 @@ +| Unexpected C# extractor error in Program.cs: Unable to resolve target for call. (Compilation error?) | 2 | diff --git a/csharp/ql/test/library-tests/diagnostics/DiagnosticExtractorErrors.qlref b/csharp/ql/test/library-tests/diagnostics/DiagnosticExtractorErrors.qlref new file mode 100644 index 00000000000..7068705cc1b --- /dev/null +++ b/csharp/ql/test/library-tests/diagnostics/DiagnosticExtractorErrors.qlref @@ -0,0 +1 @@ +Diagnostics/DiagnosticExtractionErrors.ql diff --git a/csharp/ql/test/library-tests/diagnostics/DiagnosticNoExtractorErrors.expected b/csharp/ql/test/library-tests/diagnostics/DiagnosticNoExtractorErrors.expected new file mode 100644 index 00000000000..17e4dc54b7d --- /dev/null +++ b/csharp/ql/test/library-tests/diagnostics/DiagnosticNoExtractorErrors.expected @@ -0,0 +1 @@ +| A.cs:0:0:0:0 | A.cs | | diff --git a/csharp/ql/test/library-tests/diagnostics/DiagnosticNoExtractorErrors.qlref b/csharp/ql/test/library-tests/diagnostics/DiagnosticNoExtractorErrors.qlref new file mode 100644 index 00000000000..7994e050699 --- /dev/null +++ b/csharp/ql/test/library-tests/diagnostics/DiagnosticNoExtractorErrors.qlref @@ -0,0 +1 @@ +Diagnostics/DiagnosticNoExtractionErrors.ql diff --git a/csharp/ql/test/library-tests/diagnostics/Program.cs b/csharp/ql/test/library-tests/diagnostics/Program.cs new file mode 100644 index 00000000000..7f0a7e57d1c --- /dev/null +++ b/csharp/ql/test/library-tests/diagnostics/Program.cs @@ -0,0 +1,17 @@ +// semmle-extractor-options: --standalone + +using System; + +class Class +{ + static void Main(string[] args) + { + int z = GetParamLength(__arglist(1, 2)); + } + + public static int GetParamLength(__arglist) + { + ArgIterator iterator = new ArgIterator(__arglist); + return iterator.GetRemainingCount(); + } +} diff --git a/csharp/ql/test/library-tests/expressions/ConstructorInitializers.expected b/csharp/ql/test/library-tests/expressions/ConstructorInitializers.expected new file mode 100644 index 00000000000..0bc1597c0dd --- /dev/null +++ b/csharp/ql/test/library-tests/expressions/ConstructorInitializers.expected @@ -0,0 +1,8 @@ +| expressions.cs:56:9:56:13 | Class | expressions.cs:56:19:56:22 | call to constructor Class | expressions.cs:58:19:58:23 | Class | +| expressions.cs:58:19:58:23 | Class | expressions.cs:58:19:58:23 | call to constructor Object | file://:0:0:0:0 | Object | +| expressions.cs:132:13:132:18 | Nested | expressions.cs:132:13:132:18 | call to constructor Class | expressions.cs:56:9:56:13 | Class | +| expressions.cs:133:13:133:18 | Nested | expressions.cs:133:29:133:32 | call to constructor Class | expressions.cs:58:19:58:23 | Class | +| expressions.cs:249:16:249:26 | LoginDialog | expressions.cs:249:16:249:26 | call to constructor Object | file://:0:0:0:0 | Object | +| expressions.cs:270:16:270:24 | IntVector | expressions.cs:270:16:270:24 | call to constructor Object | file://:0:0:0:0 | Object | +| expressions.cs:310:16:310:20 | Digit | expressions.cs:310:16:310:20 | call to constructor ValueType | file://:0:0:0:0 | ValueType | +| expressions.cs:480:20:480:22 | Num | expressions.cs:480:20:480:22 | call to constructor Object | file://:0:0:0:0 | Object | diff --git a/csharp/ql/test/library-tests/expressions/ConstructorInitializers.ql b/csharp/ql/test/library-tests/expressions/ConstructorInitializers.ql new file mode 100644 index 00000000000..d5799127ea0 --- /dev/null +++ b/csharp/ql/test/library-tests/expressions/ConstructorInitializers.ql @@ -0,0 +1,21 @@ +import csharp + +private class ConstructorInitializerTarget extends Constructor { + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + if this.fromSource() + then this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + else ( + filepath = "" and + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 + ) + } +} + +from Constructor c, ConstructorInitializer i, ConstructorInitializerTarget target +where c.getInitializer() = i and target = i.getTarget() +select c, i, target diff --git a/csharp/ql/test/library-tests/expressions/PrintAst.expected b/csharp/ql/test/library-tests/expressions/PrintAst.expected index 5c23d3eb03f..1af5eb1b856 100644 --- a/csharp/ql/test/library-tests/expressions/PrintAst.expected +++ b/csharp/ql/test/library-tests/expressions/PrintAst.expected @@ -835,7 +835,14 @@ expressions.cs: # 129| 21: [Class] Nested #-----| 3: (Base types) # 129| 0: [TypeMention] Class -# 133| 4: [InstanceConstructor] Nested +# 131| 4: [StaticConstructor] Nested +# 131| 4: [BlockStmt] {...} +# 132| 5: [InstanceConstructor] Nested +#-----| 2: (Parameters) +# 132| 0: [Parameter] b +# 132| -1: [TypeMention] bool +# 132| 4: [BlockStmt] {...} +# 133| 6: [InstanceConstructor] Nested #-----| 2: (Parameters) # 133| 0: [Parameter] i # 133| -1: [TypeMention] int @@ -844,7 +851,7 @@ expressions.cs: # 133| 0: [ParameterAccess] access to parameter i # 133| 1: [IntLiteral] 1 # 133| 4: [BlockStmt] {...} -# 135| 5: [Method] OtherAccesses +# 135| 7: [Method] OtherAccesses # 135| -1: [TypeMention] Void # 136| 4: [BlockStmt] {...} # 137| 0: [ExprStmt] ...; diff --git a/csharp/ql/test/library-tests/expressions/expressions.cs b/csharp/ql/test/library-tests/expressions/expressions.cs index 5d995e10985..242db973baa 100644 --- a/csharp/ql/test/library-tests/expressions/expressions.cs +++ b/csharp/ql/test/library-tests/expressions/expressions.cs @@ -128,8 +128,8 @@ namespace Expressions class Nested : Class { - - + static Nested() { } + Nested(bool b) { } Nested(int i) : base(i + 1) { } void OtherAccesses() diff --git a/csharp/ql/test/library-tests/exprorstmtparent/Parameter.ql b/csharp/ql/test/library-tests/exprorstmtparent/Parameter.ql index 3c12d5b870f..33f6eeb2b9d 100644 --- a/csharp/ql/test/library-tests/exprorstmtparent/Parameter.ql +++ b/csharp/ql/test/library-tests/exprorstmtparent/Parameter.ql @@ -1,4 +1,5 @@ import csharp from Parameter p +where p.fromSource() select p, p.getDefaultValue() diff --git a/csharp/ql/test/library-tests/extension-method-call/ExtensionMethodCalls.expected b/csharp/ql/test/library-tests/extension-method-call/ExtensionMethodCalls.expected new file mode 100644 index 00000000000..f88833695ac --- /dev/null +++ b/csharp/ql/test/library-tests/extension-method-call/ExtensionMethodCalls.expected @@ -0,0 +1,42 @@ +methodCallTargets +| methods.cs:14:60:14:73 | call to method Ext3 | methods.cs:14:28:14:34 | Ext3 | Ext3(T, int) | +| methods.cs:16:60:16:74 | call to method Ext4 | methods.cs:16:28:16:34 | Ext4 | Ext4(T, int) | +| methods.cs:23:13:23:22 | call to method Ext0 | methods.cs:8:28:8:34 | Ext0 | Ext0(string, int) | +| methods.cs:24:13:24:27 | call to method Ext0 | methods.cs:8:28:8:34 | Ext0 | Ext0(string, int) | +| methods.cs:25:13:25:30 | call to method Ext0 | methods.cs:8:28:8:34 | Ext0 | Ext0(string, double) | +| methods.cs:26:13:26:33 | call to method Ext0 | methods.cs:8:28:8:34 | Ext0 | Ext0(string, object) | +| methods.cs:27:13:27:34 | call to method Ext0 | methods.cs:8:28:8:34 | Ext0 | Ext0(string, int) | +| methods.cs:28:13:28:39 | call to method Ext0 | methods.cs:8:28:8:34 | Ext0 | Ext0(string, int) | +| methods.cs:29:13:29:42 | call to method Ext0 | methods.cs:8:28:8:34 | Ext0 | Ext0(string, double) | +| methods.cs:30:13:30:45 | call to method Ext0 | methods.cs:8:28:8:34 | Ext0 | Ext0(string, object) | +| methods.cs:32:13:32:22 | call to method Ext1 | methods.cs:10:28:10:31 | Ext1 | Ext1(string, int) | +| methods.cs:33:13:33:34 | call to method Ext1 | methods.cs:10:28:10:31 | Ext1 | Ext1(string, int) | +| methods.cs:35:13:35:21 | call to method Ext2 | methods.cs:12:28:12:34 | Ext2 | Ext2(int, int) | +| methods.cs:36:13:36:26 | call to method Ext2 | methods.cs:12:28:12:34 | Ext2 | Ext2(int, int) | +| methods.cs:37:13:37:22 | call to method Ext2 | methods.cs:12:28:12:34 | Ext2 | Ext2(string, int) | +| methods.cs:38:13:38:30 | call to method Ext2 | methods.cs:12:28:12:34 | Ext2 | Ext2(string, int) | +| methods.cs:39:13:39:30 | call to method Ext2 | methods.cs:12:28:12:34 | Ext2 | Ext2(object, int) | +| methods.cs:40:13:40:33 | call to method Ext2 | methods.cs:12:28:12:34 | Ext2 | Ext2(int, int) | +| methods.cs:41:13:41:38 | call to method Ext2 | methods.cs:12:28:12:34 | Ext2 | Ext2(int, int) | +| methods.cs:42:13:42:34 | call to method Ext2 | methods.cs:12:28:12:34 | Ext2 | Ext2(string, int) | +| methods.cs:43:13:43:42 | call to method Ext2 | methods.cs:12:28:12:34 | Ext2 | Ext2(string, int) | +| methods.cs:44:13:44:42 | call to method Ext2 | methods.cs:12:28:12:34 | Ext2 | Ext2(object, int) | +genericMethodCallTargets +| methods.cs:23:13:23:22 | call to method Ext0 | methods.cs:8:28:8:34 | Ext0 | Ext0(string, int) | methods.cs:8:28:8:34 | Ext0 | Ext0(string, T) | +| methods.cs:24:13:24:27 | call to method Ext0 | methods.cs:8:28:8:34 | Ext0 | Ext0(string, int) | methods.cs:8:28:8:34 | Ext0 | Ext0(string, T) | +| methods.cs:25:13:25:30 | call to method Ext0 | methods.cs:8:28:8:34 | Ext0 | Ext0(string, double) | methods.cs:8:28:8:34 | Ext0 | Ext0(string, T) | +| methods.cs:26:13:26:33 | call to method Ext0 | methods.cs:8:28:8:34 | Ext0 | Ext0(string, object) | methods.cs:8:28:8:34 | Ext0 | Ext0(string, T) | +| methods.cs:27:13:27:34 | call to method Ext0 | methods.cs:8:28:8:34 | Ext0 | Ext0(string, int) | methods.cs:8:28:8:34 | Ext0 | Ext0(string, T) | +| methods.cs:28:13:28:39 | call to method Ext0 | methods.cs:8:28:8:34 | Ext0 | Ext0(string, int) | methods.cs:8:28:8:34 | Ext0 | Ext0(string, T) | +| methods.cs:29:13:29:42 | call to method Ext0 | methods.cs:8:28:8:34 | Ext0 | Ext0(string, double) | methods.cs:8:28:8:34 | Ext0 | Ext0(string, T) | +| methods.cs:30:13:30:45 | call to method Ext0 | methods.cs:8:28:8:34 | Ext0 | Ext0(string, object) | methods.cs:8:28:8:34 | Ext0 | Ext0(string, T) | +| methods.cs:35:13:35:21 | call to method Ext2 | methods.cs:12:28:12:34 | Ext2 | Ext2(int, int) | methods.cs:12:28:12:34 | Ext2 | Ext2(T, int) | +| methods.cs:36:13:36:26 | call to method Ext2 | methods.cs:12:28:12:34 | Ext2 | Ext2(int, int) | methods.cs:12:28:12:34 | Ext2 | Ext2(T, int) | +| methods.cs:37:13:37:22 | call to method Ext2 | methods.cs:12:28:12:34 | Ext2 | Ext2(string, int) | methods.cs:12:28:12:34 | Ext2 | Ext2(T, int) | +| methods.cs:38:13:38:30 | call to method Ext2 | methods.cs:12:28:12:34 | Ext2 | Ext2(string, int) | methods.cs:12:28:12:34 | Ext2 | Ext2(T, int) | +| methods.cs:39:13:39:30 | call to method Ext2 | methods.cs:12:28:12:34 | Ext2 | Ext2(object, int) | methods.cs:12:28:12:34 | Ext2 | Ext2(T, int) | +| methods.cs:40:13:40:33 | call to method Ext2 | methods.cs:12:28:12:34 | Ext2 | Ext2(int, int) | methods.cs:12:28:12:34 | Ext2 | Ext2(T, int) | +| methods.cs:41:13:41:38 | call to method Ext2 | methods.cs:12:28:12:34 | Ext2 | Ext2(int, int) | methods.cs:12:28:12:34 | Ext2 | Ext2(T, int) | +| methods.cs:42:13:42:34 | call to method Ext2 | methods.cs:12:28:12:34 | Ext2 | Ext2(string, int) | methods.cs:12:28:12:34 | Ext2 | Ext2(T, int) | +| methods.cs:43:13:43:42 | call to method Ext2 | methods.cs:12:28:12:34 | Ext2 | Ext2(string, int) | methods.cs:12:28:12:34 | Ext2 | Ext2(T, int) | +| methods.cs:44:13:44:42 | call to method Ext2 | methods.cs:12:28:12:34 | Ext2 | Ext2(object, int) | methods.cs:12:28:12:34 | Ext2 | Ext2(T, int) | diff --git a/csharp/ql/test/library-tests/extension-method-call/ExtensionMethodCalls.ql b/csharp/ql/test/library-tests/extension-method-call/ExtensionMethodCalls.ql new file mode 100644 index 00000000000..afdbe77fce8 --- /dev/null +++ b/csharp/ql/test/library-tests/extension-method-call/ExtensionMethodCalls.ql @@ -0,0 +1,14 @@ +import csharp + +query predicate methodCallTargets(MethodCall mc, Method m, string sig) { + m = mc.getTarget() and sig = m.toStringWithTypes() +} + +query predicate genericMethodCallTargets( + MethodCall mc, ConstructedMethod cm, string sig1, UnboundGenericMethod ugm, string sig2 +) { + cm = mc.getTarget() and + sig1 = cm.toStringWithTypes() and + ugm = cm.getUnboundGeneric() and + sig2 = ugm.toStringWithTypes() +} diff --git a/csharp/ql/test/library-tests/extension-method-call/methods.cs b/csharp/ql/test/library-tests/extension-method-call/methods.cs new file mode 100644 index 00000000000..e587a645d68 --- /dev/null +++ b/csharp/ql/test/library-tests/extension-method-call/methods.cs @@ -0,0 +1,47 @@ +using System; + +namespace Test +{ + + public static class Extensions + { + public static void Ext0(this string self, T arg) { } + + public static void Ext1(this string self, int arg) { } + + public static void Ext2(this T self, int arg) { } + + public static void Ext3(this T self, int arg) { self.Ext3(arg); } + + public static void Ext4(this T self, int arg) { Ext4(self, arg); } + } + + public class Program + { + public static void M() + { + "".Ext0(1); + "".Ext0(1); + "".Ext0(1); + "".Ext0(null); + Extensions.Ext0("", 1); + Extensions.Ext0("", 1); + Extensions.Ext0("", 1); + Extensions.Ext0("", null); + + "".Ext1(1); + Extensions.Ext1("", 1); + + 1.Ext2(1); + 1.Ext2(1); + "".Ext2(1); + "".Ext2(1); + "".Ext2(1); + Extensions.Ext2(1, 1); + Extensions.Ext2(1, 1); + Extensions.Ext2("", 1); + Extensions.Ext2("", 1); + Extensions.Ext2("", 1); + } + } +} diff --git a/csharp/ql/test/library-tests/goto/Goto1.ql b/csharp/ql/test/library-tests/goto/Goto1.ql index e7ee0b3ff8a..97dd1fa2af0 100644 --- a/csharp/ql/test/library-tests/goto/Goto1.ql +++ b/csharp/ql/test/library-tests/goto/Goto1.ql @@ -1,6 +1,7 @@ import csharp query predicate edges(ControlFlow::Node node, ControlFlow::Node successor, string attr, string val) { + not node.getElement().fromLibrary() and exists(ControlFlow::SuccessorType t | successor = node.getASuccessorByType(t) | attr = "semmle.label" and val = t.toString() diff --git a/csharp/ql/test/library-tests/methods/ExtensionMethods.expected b/csharp/ql/test/library-tests/methods/ExtensionMethods.expected index d7593f8f223..795014ea078 100644 --- a/csharp/ql/test/library-tests/methods/ExtensionMethods.expected +++ b/csharp/ql/test/library-tests/methods/ExtensionMethods.expected @@ -1,12 +1,12 @@ -| methods.cs:118:44:118:55 | call to method ToInt32 | 0 | methods.cs:118:52:118:54 | "0" | -| methods.cs:127:34:127:52 | call to method Slice | 0 | methods.cs:127:34:127:40 | access to local variable strings | -| methods.cs:127:34:127:52 | call to method Slice | 1 | methods.cs:127:48:127:48 | 1 | -| methods.cs:127:34:127:52 | call to method Slice | 2 | methods.cs:127:51:127:51 | 2 | -| methods.cs:129:42:129:52 | call to method ToInt32 | 0 | methods.cs:129:42:129:42 | access to local variable s | -| methods.cs:132:13:132:34 | call to method ToInt32 | 0 | methods.cs:132:32:132:33 | "" | -| methods.cs:132:13:132:34 | call to method ToInt32 | -1 | methods.cs:132:13:132:22 | access to type Extensions | -| methods.cs:134:13:134:49 | call to method ToBool | 0 | methods.cs:134:31:134:36 | "true" | -| methods.cs:134:13:134:49 | call to method ToBool | 1 | methods.cs:134:39:134:48 | delegate creation of type Func | -| methods.cs:134:13:134:49 | call to method ToBool | -1 | methods.cs:134:13:134:22 | access to type Extensions | -| methods.cs:180:20:180:39 | call to method SkipTwo | 0 | methods.cs:180:20:180:23 | access to parameter list | -| methods.cs:180:20:180:39 | call to method SkipTwo | 1 | methods.cs:180:38:180:38 | access to parameter i | +| methods.cs:119:44:119:55 | call to method ToInt32 | 0 | methods.cs:119:52:119:54 | "0" | +| methods.cs:128:34:128:52 | call to method Slice | 0 | methods.cs:128:34:128:40 | access to local variable strings | +| methods.cs:128:34:128:52 | call to method Slice | 1 | methods.cs:128:48:128:48 | 1 | +| methods.cs:128:34:128:52 | call to method Slice | 2 | methods.cs:128:51:128:51 | 2 | +| methods.cs:130:42:130:52 | call to method ToInt32 | 0 | methods.cs:130:42:130:42 | access to local variable s | +| methods.cs:133:13:133:34 | call to method ToInt32 | 0 | methods.cs:133:32:133:33 | "" | +| methods.cs:133:13:133:34 | call to method ToInt32 | -1 | methods.cs:133:13:133:22 | access to type Extensions | +| methods.cs:135:13:135:49 | call to method ToBool | 0 | methods.cs:135:31:135:36 | "true" | +| methods.cs:135:13:135:49 | call to method ToBool | 1 | methods.cs:135:39:135:48 | delegate creation of type Func | +| methods.cs:135:13:135:49 | call to method ToBool | -1 | methods.cs:135:13:135:22 | access to type Extensions | +| methods.cs:181:20:181:39 | call to method SkipTwo | 0 | methods.cs:181:20:181:23 | access to parameter list | +| methods.cs:181:20:181:39 | call to method SkipTwo | 1 | methods.cs:181:38:181:38 | access to parameter i | diff --git a/csharp/ql/test/library-tests/methods/Methods1.expected b/csharp/ql/test/library-tests/methods/Methods1.expected index be14a801185..054f358330f 100644 --- a/csharp/ql/test/library-tests/methods/Methods1.expected +++ b/csharp/ql/test/library-tests/methods/Methods1.expected @@ -1 +1 @@ -| methods.cs:9:21:9:24 | Swap | +| methods.cs:10:21:10:24 | Swap | diff --git a/csharp/ql/test/library-tests/methods/Methods1.ql b/csharp/ql/test/library-tests/methods/Methods1.ql index fb8c2d4cdf9..09560396bd2 100644 --- a/csharp/ql/test/library-tests/methods/Methods1.ql +++ b/csharp/ql/test/library-tests/methods/Methods1.ql @@ -7,5 +7,6 @@ import csharp from Method s where s.hasName("Swap") and - s.isStatic() + s.isStatic() and + s.fromSource() select s diff --git a/csharp/ql/test/library-tests/methods/Methods2.expected b/csharp/ql/test/library-tests/methods/Methods2.expected index b9fad0aa394..d0dc1559fa6 100644 --- a/csharp/ql/test/library-tests/methods/Methods2.expected +++ b/csharp/ql/test/library-tests/methods/Methods2.expected @@ -1 +1 @@ -| methods.cs:28:28:28:33 | Divide | +| methods.cs:29:28:29:33 | Divide | diff --git a/csharp/ql/test/library-tests/methods/Methods3.expected b/csharp/ql/test/library-tests/methods/Methods3.expected index 8c726a4b6ff..1fb5f1dd0ba 100644 --- a/csharp/ql/test/library-tests/methods/Methods3.expected +++ b/csharp/ql/test/library-tests/methods/Methods3.expected @@ -1 +1 @@ -| methods.cs:49:11:49:25 | TestOverloading | +| methods.cs:50:11:50:25 | TestOverloading | diff --git a/csharp/ql/test/library-tests/methods/Methods4.expected b/csharp/ql/test/library-tests/methods/Methods4.expected index f709606a238..6eaa7230538 100644 --- a/csharp/ql/test/library-tests/methods/Methods4.expected +++ b/csharp/ql/test/library-tests/methods/Methods4.expected @@ -1 +1 @@ -| methods.cs:49:11:49:25 | TestOverloading | methods.cs:57:21:57:21 | F | Object | +| methods.cs:50:11:50:25 | TestOverloading | methods.cs:58:21:58:21 | F | Object | diff --git a/csharp/ql/test/library-tests/methods/Methods5.expected b/csharp/ql/test/library-tests/methods/Methods5.expected index 4f7e31d5bee..01beaa41f6e 100644 --- a/csharp/ql/test/library-tests/methods/Methods5.expected +++ b/csharp/ql/test/library-tests/methods/Methods5.expected @@ -1,22 +1,28 @@ -| methods.cs:16:14:16:17 | Main | methods.cs:9:21:9:24 | Swap | methods.cs:6:18:6:24 | TestRef | -| methods.cs:16:14:16:17 | Main | methods.cs:46:28:46:36 | WriteLine | methods.cs:6:18:6:24 | TestRef | -| methods.cs:34:14:34:17 | Main | methods.cs:28:28:28:33 | Divide | methods.cs:25:18:25:24 | TestOut | -| methods.cs:34:14:34:17 | Main | methods.cs:46:28:46:36 | WriteLine | methods.cs:25:18:25:24 | TestOut | -| methods.cs:52:21:52:21 | F | methods.cs:46:28:46:36 | WriteLine | methods.cs:49:11:49:25 | TestOverloading | -| methods.cs:57:21:57:21 | F | methods.cs:46:28:46:36 | WriteLine | methods.cs:49:11:49:25 | TestOverloading | -| methods.cs:62:21:62:21 | F | methods.cs:46:28:46:36 | WriteLine | methods.cs:49:11:49:25 | TestOverloading | -| methods.cs:67:21:67:21 | F | methods.cs:46:28:46:36 | WriteLine | methods.cs:49:11:49:25 | TestOverloading | -| methods.cs:72:21:72:24 | F | methods.cs:46:28:46:36 | WriteLine | methods.cs:49:11:49:25 | TestOverloading | -| methods.cs:77:21:77:21 | F | methods.cs:46:28:46:36 | WriteLine | methods.cs:49:11:49:25 | TestOverloading | -| methods.cs:82:14:82:17 | Main | methods.cs:52:21:52:21 | F | methods.cs:49:11:49:25 | TestOverloading | -| methods.cs:82:14:82:17 | Main | methods.cs:57:21:57:21 | F | methods.cs:49:11:49:25 | TestOverloading | -| methods.cs:82:14:82:17 | Main | methods.cs:62:21:62:21 | F | methods.cs:49:11:49:25 | TestOverloading | -| methods.cs:82:14:82:17 | Main | methods.cs:67:21:67:21 | F | methods.cs:49:11:49:25 | TestOverloading | -| methods.cs:82:14:82:17 | Main | methods.cs:72:21:72:24 | F | methods.cs:49:11:49:25 | TestOverloading | -| methods.cs:82:14:82:17 | Main | methods.cs:72:21:72:24 | F | methods.cs:49:11:49:25 | TestOverloading | -| methods.cs:82:14:82:17 | Main | methods.cs:77:21:77:21 | F | methods.cs:49:11:49:25 | TestOverloading | -| methods.cs:118:27:118:37 | CallToInt32 | methods.cs:99:27:99:33 | ToInt32 | methods.cs:96:25:96:34 | Extensions | -| methods.cs:124:21:124:24 | Main | methods.cs:99:27:99:33 | ToInt32 | methods.cs:121:18:121:31 | TestExtensions | -| methods.cs:124:21:124:24 | Main | methods.cs:104:28:104:33 | ToBool | methods.cs:121:18:121:31 | TestExtensions | -| methods.cs:124:21:124:24 | Main | methods.cs:109:27:109:34 | Slice | methods.cs:121:18:121:31 | TestExtensions | -| methods.cs:178:67:178:76 | SkipTwoInt | methods.cs:173:65:173:74 | SkipTwo | methods.cs:166:18:166:47 | TestDefaultExtensionParameters | +| methods.cs:17:14:17:17 | Main | methods.cs:10:21:10:24 | Swap | methods.cs:7:18:7:24 | TestRef | +| methods.cs:17:14:17:17 | Main | methods.cs:47:28:47:36 | WriteLine | methods.cs:7:18:7:24 | TestRef | +| methods.cs:35:14:35:17 | Main | methods.cs:29:28:29:33 | Divide | methods.cs:26:18:26:24 | TestOut | +| methods.cs:35:14:35:17 | Main | methods.cs:47:28:47:36 | WriteLine | methods.cs:26:18:26:24 | TestOut | +| methods.cs:53:21:53:21 | F | methods.cs:47:28:47:36 | WriteLine | methods.cs:50:11:50:25 | TestOverloading | +| methods.cs:58:21:58:21 | F | methods.cs:47:28:47:36 | WriteLine | methods.cs:50:11:50:25 | TestOverloading | +| methods.cs:63:21:63:21 | F | methods.cs:47:28:47:36 | WriteLine | methods.cs:50:11:50:25 | TestOverloading | +| methods.cs:68:21:68:21 | F | methods.cs:47:28:47:36 | WriteLine | methods.cs:50:11:50:25 | TestOverloading | +| methods.cs:73:21:73:24 | F | methods.cs:47:28:47:36 | WriteLine | methods.cs:50:11:50:25 | TestOverloading | +| methods.cs:78:21:78:21 | F | methods.cs:47:28:47:36 | WriteLine | methods.cs:50:11:50:25 | TestOverloading | +| methods.cs:83:14:83:17 | Main | methods.cs:53:21:53:21 | F | methods.cs:50:11:50:25 | TestOverloading | +| methods.cs:83:14:83:17 | Main | methods.cs:58:21:58:21 | F | methods.cs:50:11:50:25 | TestOverloading | +| methods.cs:83:14:83:17 | Main | methods.cs:63:21:63:21 | F | methods.cs:50:11:50:25 | TestOverloading | +| methods.cs:83:14:83:17 | Main | methods.cs:68:21:68:21 | F | methods.cs:50:11:50:25 | TestOverloading | +| methods.cs:83:14:83:17 | Main | methods.cs:73:21:73:24 | F | methods.cs:50:11:50:25 | TestOverloading | +| methods.cs:83:14:83:17 | Main | methods.cs:73:21:73:24 | F | methods.cs:50:11:50:25 | TestOverloading | +| methods.cs:83:14:83:17 | Main | methods.cs:78:21:78:21 | F | methods.cs:50:11:50:25 | TestOverloading | +| methods.cs:119:27:119:37 | CallToInt32 | methods.cs:100:27:100:33 | ToInt32 | methods.cs:97:25:97:34 | Extensions | +| methods.cs:125:21:125:24 | Main | methods.cs:100:27:100:33 | ToInt32 | methods.cs:122:18:122:31 | TestExtensions | +| methods.cs:125:21:125:24 | Main | methods.cs:105:28:105:33 | ToBool | methods.cs:122:18:122:31 | TestExtensions | +| methods.cs:125:21:125:24 | Main | methods.cs:110:27:110:34 | Slice | methods.cs:122:18:122:31 | TestExtensions | +| methods.cs:179:67:179:76 | SkipTwoInt | methods.cs:174:65:174:74 | SkipTwo | methods.cs:167:18:167:47 | TestDefaultExtensionParameters | +| methods.cs:190:21:190:25 | Calls | methods.cs:187:21:187:21 | M | methods.cs:185:18:185:40 | TestCollidingMethods<> | +| methods.cs:190:21:190:25 | Calls | methods.cs:188:21:188:21 | M | methods.cs:185:18:185:40 | TestCollidingMethods<> | +| methods.cs:190:21:190:25 | Calls | methods.cs:188:21:188:21 | M | methods.cs:185:18:185:40 | TestCollidingMethods<> | +| methods.cs:203:20:203:25 | Nested | methods.cs:202:20:202:25 | Nested | methods.cs:200:22:200:27 | Nested | +| methods.cs:203:20:203:25 | Nested | methods.cs:202:20:202:25 | Nested | methods.cs:200:22:200:27 | Nested | +| methods.cs:203:20:203:25 | Nested | methods.cs:203:20:203:25 | Nested | methods.cs:200:22:200:27 | Nested | diff --git a/csharp/ql/test/library-tests/methods/Parameters1.expected b/csharp/ql/test/library-tests/methods/Parameters1.expected index 9d9948f38dd..e4e643616f6 100644 --- a/csharp/ql/test/library-tests/methods/Parameters1.expected +++ b/csharp/ql/test/library-tests/methods/Parameters1.expected @@ -1,2 +1,2 @@ -| methods.cs:9:21:9:24 | Swap | methods.cs:9:34:9:34 | x | -| methods.cs:9:21:9:24 | Swap | methods.cs:9:45:9:45 | y | +| methods.cs:10:21:10:24 | Swap | methods.cs:10:34:10:34 | x | +| methods.cs:10:21:10:24 | Swap | methods.cs:10:45:10:45 | y | diff --git a/csharp/ql/test/library-tests/methods/Parameters2.expected b/csharp/ql/test/library-tests/methods/Parameters2.expected index 645af10244f..30a7ca05274 100644 --- a/csharp/ql/test/library-tests/methods/Parameters2.expected +++ b/csharp/ql/test/library-tests/methods/Parameters2.expected @@ -1,4 +1,4 @@ -| methods.cs:28:28:28:33 | Divide | methods.cs:28:39:28:39 | x | -| methods.cs:28:28:28:33 | Divide | methods.cs:28:46:28:46 | y | -| methods.cs:28:28:28:33 | Divide | methods.cs:28:57:28:62 | result | -| methods.cs:28:28:28:33 | Divide | methods.cs:28:73:28:81 | remainder | +| methods.cs:29:28:29:33 | Divide | methods.cs:29:39:29:39 | x | +| methods.cs:29:28:29:33 | Divide | methods.cs:29:46:29:46 | y | +| methods.cs:29:28:29:33 | Divide | methods.cs:29:57:29:62 | result | +| methods.cs:29:28:29:33 | Divide | methods.cs:29:73:29:81 | remainder | diff --git a/csharp/ql/test/library-tests/methods/Parameters3.expected b/csharp/ql/test/library-tests/methods/Parameters3.expected index 68936353065..ae1c5c02e80 100644 --- a/csharp/ql/test/library-tests/methods/Parameters3.expected +++ b/csharp/ql/test/library-tests/methods/Parameters3.expected @@ -1,2 +1,2 @@ -| methods.cs:45:28:45:32 | Write | Object[] | -| methods.cs:45:28:45:32 | Write | String | +| methods.cs:46:28:46:32 | Write | Object[] | +| methods.cs:46:28:46:32 | Write | String | diff --git a/csharp/ql/test/library-tests/methods/Parameters5.expected b/csharp/ql/test/library-tests/methods/Parameters5.expected index 823329fe908..1fd039430b1 100644 --- a/csharp/ql/test/library-tests/methods/Parameters5.expected +++ b/csharp/ql/test/library-tests/methods/Parameters5.expected @@ -1,7 +1,7 @@ -| methods.cs:145:40:145:40 | c | methods.cs:145:44:145:44 | 1 | 1 | -| methods.cs:145:51:145:51 | d | methods.cs:145:55:145:55 | 2 | 2 | -| methods.cs:145:65:145:65 | e | methods.cs:145:69:145:77 | ... + ... | ab | -| methods.cs:153:45:153:45 | x | methods.cs:153:49:153:53 | "abc" | abc | -| methods.cs:153:63:153:63 | y | methods.cs:153:67:153:78 | object creation of type Double | 0 | -| methods.cs:159:36:159:36 | y | methods.cs:159:40:159:40 | 0 | 0 | -| methods.cs:159:36:159:36 | y | methods.cs:159:40:159:40 | 0 | 0 | +| methods.cs:146:40:146:40 | c | methods.cs:146:44:146:44 | 1 | 1 | +| methods.cs:146:51:146:51 | d | methods.cs:146:55:146:55 | 2 | 2 | +| methods.cs:146:65:146:65 | e | methods.cs:146:69:146:77 | ... + ... | ab | +| methods.cs:154:45:154:45 | x | methods.cs:154:49:154:53 | "abc" | abc | +| methods.cs:154:63:154:63 | y | methods.cs:154:67:154:78 | object creation of type Double | 0 | +| methods.cs:160:36:160:36 | y | methods.cs:160:40:160:40 | 0 | 0 | +| methods.cs:160:36:160:36 | y | methods.cs:160:40:160:40 | 0 | 0 | diff --git a/csharp/ql/test/library-tests/methods/Parameters6.expected b/csharp/ql/test/library-tests/methods/Parameters6.expected index 25c2a9e1e9f..c431492acd1 100644 --- a/csharp/ql/test/library-tests/methods/Parameters6.expected +++ b/csharp/ql/test/library-tests/methods/Parameters6.expected @@ -1,2 +1,2 @@ -| methods.cs:157:40:157:40 | b | methods.cs:157:44:157:45 | 12 | 12 | -| methods.cs:157:55:157:55 | c | methods.cs:157:59:157:70 | object creation of type Double | 0 | +| methods.cs:158:40:158:40 | b | methods.cs:158:44:158:45 | 12 | 12 | +| methods.cs:158:55:158:55 | c | methods.cs:158:59:158:70 | object creation of type Double | 0 | diff --git a/csharp/ql/test/library-tests/methods/Parameters7.expected b/csharp/ql/test/library-tests/methods/Parameters7.expected index 372d22c8dfd..bbc0f36bae0 100644 --- a/csharp/ql/test/library-tests/methods/Parameters7.expected +++ b/csharp/ql/test/library-tests/methods/Parameters7.expected @@ -1 +1 @@ -| methods.cs:162:13:162:15 | value | +| methods.cs:163:13:163:15 | value | diff --git a/csharp/ql/test/library-tests/methods/Parameters8.expected b/csharp/ql/test/library-tests/methods/Parameters8.expected index d6bebaf61cc..21ec8aeae13 100644 --- a/csharp/ql/test/library-tests/methods/Parameters8.expected +++ b/csharp/ql/test/library-tests/methods/Parameters8.expected @@ -1,43 +1,54 @@ -| methods.cs:9:21:9:24 | Swap | methods.cs:9:34:9:34 | x | -| methods.cs:9:21:9:24 | Swap | methods.cs:9:45:9:45 | y | -| methods.cs:28:28:28:33 | Divide | methods.cs:28:39:28:39 | x | -| methods.cs:28:28:28:33 | Divide | methods.cs:28:46:28:46 | y | -| methods.cs:28:28:28:33 | Divide | methods.cs:28:57:28:62 | result | -| methods.cs:28:28:28:33 | Divide | methods.cs:28:73:28:81 | remainder | -| methods.cs:45:28:45:32 | Write | methods.cs:45:41:45:43 | fmt | -| methods.cs:45:28:45:32 | Write | methods.cs:45:62:45:65 | args | -| methods.cs:46:28:46:36 | WriteLine | methods.cs:46:45:46:47 | fmt | -| methods.cs:46:28:46:36 | WriteLine | methods.cs:46:66:46:69 | args | -| methods.cs:57:21:57:21 | F | methods.cs:57:30:57:30 | x | -| methods.cs:62:21:62:21 | F | methods.cs:62:27:62:27 | x | -| methods.cs:67:21:67:21 | F | methods.cs:67:30:67:30 | x | -| methods.cs:72:21:72:24 | F | methods.cs:72:28:72:28 | x | -| methods.cs:72:21:72:24 | F | methods.cs:72:28:72:28 | x | -| methods.cs:72:21:72:24 | F | methods.cs:72:28:72:28 | x | -| methods.cs:77:21:77:21 | F | methods.cs:77:30:77:30 | x | -| methods.cs:77:21:77:21 | F | methods.cs:77:40:77:40 | y | -| methods.cs:99:27:99:33 | ToInt32 | methods.cs:99:35:99:47 | s | -| methods.cs:99:27:99:33 | ToInt32 | methods.cs:99:47:99:47 | s | -| methods.cs:104:28:104:33 | ToBool | methods.cs:104:47:104:47 | s | -| methods.cs:104:28:104:33 | ToBool | methods.cs:104:69:104:69 | f | -| methods.cs:109:27:109:34 | Slice | methods.cs:109:36:109:50 | source | -| methods.cs:109:27:109:34 | Slice | methods.cs:109:45:109:50 | source | -| methods.cs:109:27:109:34 | Slice | methods.cs:109:57:109:61 | index | -| methods.cs:109:27:109:34 | Slice | methods.cs:109:57:109:61 | index | -| methods.cs:109:27:109:34 | Slice | methods.cs:109:68:109:72 | count | -| methods.cs:109:27:109:34 | Slice | methods.cs:109:68:109:72 | count | -| methods.cs:141:14:141:20 | Method1 | methods.cs:141:26:141:26 | x | -| methods.cs:141:14:141:20 | Method1 | methods.cs:141:33:141:33 | y | -| methods.cs:145:14:145:20 | Method2 | methods.cs:145:26:145:26 | a | -| methods.cs:145:14:145:20 | Method2 | methods.cs:145:33:145:33 | b | -| methods.cs:145:14:145:20 | Method2 | methods.cs:145:40:145:40 | c | -| methods.cs:145:14:145:20 | Method2 | methods.cs:145:51:145:51 | d | -| methods.cs:145:14:145:20 | Method2 | methods.cs:145:65:145:65 | e | -| methods.cs:168:27:168:30 | Plus | methods.cs:168:41:168:44 | left | -| methods.cs:168:27:168:30 | Plus | methods.cs:168:51:168:55 | right | -| methods.cs:173:65:173:74 | SkipTwo | methods.cs:173:76:173:126 | list | -| methods.cs:173:65:173:74 | SkipTwo | methods.cs:173:123:173:126 | list | -| methods.cs:173:65:173:74 | SkipTwo | methods.cs:173:133:173:133 | i | -| methods.cs:173:65:173:74 | SkipTwo | methods.cs:173:133:173:133 | i | -| methods.cs:178:67:178:76 | SkipTwoInt | methods.cs:178:127:178:130 | list | -| methods.cs:178:67:178:76 | SkipTwoInt | methods.cs:178:137:178:137 | i | +| methods.cs:10:21:10:24 | Swap | methods.cs:10:34:10:34 | x | +| methods.cs:10:21:10:24 | Swap | methods.cs:10:45:10:45 | y | +| methods.cs:29:28:29:33 | Divide | methods.cs:29:39:29:39 | x | +| methods.cs:29:28:29:33 | Divide | methods.cs:29:46:29:46 | y | +| methods.cs:29:28:29:33 | Divide | methods.cs:29:57:29:62 | result | +| methods.cs:29:28:29:33 | Divide | methods.cs:29:73:29:81 | remainder | +| methods.cs:46:28:46:32 | Write | methods.cs:46:41:46:43 | fmt | +| methods.cs:46:28:46:32 | Write | methods.cs:46:62:46:65 | args | +| methods.cs:47:28:47:36 | WriteLine | methods.cs:47:45:47:47 | fmt | +| methods.cs:47:28:47:36 | WriteLine | methods.cs:47:66:47:69 | args | +| methods.cs:58:21:58:21 | F | methods.cs:58:30:58:30 | x | +| methods.cs:63:21:63:21 | F | methods.cs:63:27:63:27 | x | +| methods.cs:68:21:68:21 | F | methods.cs:68:30:68:30 | x | +| methods.cs:73:21:73:24 | F | methods.cs:73:28:73:28 | x | +| methods.cs:73:21:73:24 | F | methods.cs:73:28:73:28 | x | +| methods.cs:73:21:73:24 | F | methods.cs:73:28:73:28 | x | +| methods.cs:78:21:78:21 | F | methods.cs:78:30:78:30 | x | +| methods.cs:78:21:78:21 | F | methods.cs:78:40:78:40 | y | +| methods.cs:100:27:100:33 | ToInt32 | methods.cs:100:47:100:47 | s | +| methods.cs:105:28:105:33 | ToBool | methods.cs:105:47:105:47 | s | +| methods.cs:105:28:105:33 | ToBool | methods.cs:105:69:105:69 | f | +| methods.cs:110:27:110:34 | Slice | methods.cs:110:45:110:50 | source | +| methods.cs:110:27:110:34 | Slice | methods.cs:110:45:110:50 | source | +| methods.cs:110:27:110:34 | Slice | methods.cs:110:57:110:61 | index | +| methods.cs:110:27:110:34 | Slice | methods.cs:110:57:110:61 | index | +| methods.cs:110:27:110:34 | Slice | methods.cs:110:68:110:72 | count | +| methods.cs:110:27:110:34 | Slice | methods.cs:110:68:110:72 | count | +| methods.cs:142:14:142:20 | Method1 | methods.cs:142:26:142:26 | x | +| methods.cs:142:14:142:20 | Method1 | methods.cs:142:33:142:33 | y | +| methods.cs:146:14:146:20 | Method2 | methods.cs:146:26:146:26 | a | +| methods.cs:146:14:146:20 | Method2 | methods.cs:146:33:146:33 | b | +| methods.cs:146:14:146:20 | Method2 | methods.cs:146:40:146:40 | c | +| methods.cs:146:14:146:20 | Method2 | methods.cs:146:51:146:51 | d | +| methods.cs:146:14:146:20 | Method2 | methods.cs:146:65:146:65 | e | +| methods.cs:169:27:169:30 | Plus | methods.cs:169:41:169:44 | left | +| methods.cs:169:27:169:30 | Plus | methods.cs:169:51:169:55 | right | +| methods.cs:174:65:174:74 | SkipTwo | methods.cs:174:123:174:126 | list | +| methods.cs:174:65:174:74 | SkipTwo | methods.cs:174:123:174:126 | list | +| methods.cs:174:65:174:74 | SkipTwo | methods.cs:174:133:174:133 | i | +| methods.cs:174:65:174:74 | SkipTwo | methods.cs:174:133:174:133 | i | +| methods.cs:179:67:179:76 | SkipTwoInt | methods.cs:179:127:179:130 | list | +| methods.cs:179:67:179:76 | SkipTwoInt | methods.cs:179:137:179:137 | i | +| methods.cs:187:21:187:21 | M | methods.cs:187:25:187:26 | p1 | +| methods.cs:187:21:187:21 | M | methods.cs:187:25:187:26 | p1 | +| methods.cs:187:21:187:21 | M | methods.cs:187:25:187:26 | p1 | +| methods.cs:187:21:187:21 | M | methods.cs:187:33:187:34 | p2 | +| methods.cs:187:21:187:21 | M | methods.cs:187:33:187:34 | p2 | +| methods.cs:187:21:187:21 | M | methods.cs:187:33:187:34 | p2 | +| methods.cs:188:21:188:21 | M | methods.cs:188:27:188:28 | p1 | +| methods.cs:188:21:188:21 | M | methods.cs:188:27:188:28 | p1 | +| methods.cs:188:21:188:21 | M | methods.cs:188:27:188:28 | p1 | +| methods.cs:188:21:188:21 | M | methods.cs:188:35:188:36 | p2 | +| methods.cs:188:21:188:21 | M | methods.cs:188:35:188:36 | p2 | +| methods.cs:188:21:188:21 | M | methods.cs:188:35:188:36 | p2 | diff --git a/csharp/ql/test/library-tests/methods/Parameters9.expected b/csharp/ql/test/library-tests/methods/Parameters9.expected index 2af714b9abc..ee0a6ef0e53 100644 --- a/csharp/ql/test/library-tests/methods/Parameters9.expected +++ b/csharp/ql/test/library-tests/methods/Parameters9.expected @@ -1,7 +1,7 @@ -| methods.cs:145:40:145:40 | c | methods.cs:145:44:145:44 | 1 | Method2(int, int, int, int, string) | -| methods.cs:145:51:145:51 | d | methods.cs:145:55:145:55 | 2 | Method2(int, int, int, int, string) | -| methods.cs:145:65:145:65 | e | methods.cs:145:69:145:77 | ... + ... | Method2(int, int, int, int, string) | -| methods.cs:168:51:168:55 | right | methods.cs:168:59:168:59 | 0 | Plus(int, int) | -| methods.cs:173:133:173:133 | i | methods.cs:173:137:173:137 | 1 | SkipTwo(IEnumerable, int) | -| methods.cs:173:133:173:133 | i | methods.cs:173:137:173:137 | 1 | SkipTwo(IEnumerable, int) | -| methods.cs:178:137:178:137 | i | methods.cs:178:141:178:141 | 1 | SkipTwoInt(IEnumerable, int) | +| methods.cs:146:40:146:40 | c | methods.cs:146:44:146:44 | 1 | Method2(int, int, int, int, string) | +| methods.cs:146:51:146:51 | d | methods.cs:146:55:146:55 | 2 | Method2(int, int, int, int, string) | +| methods.cs:146:65:146:65 | e | methods.cs:146:69:146:77 | ... + ... | Method2(int, int, int, int, string) | +| methods.cs:169:51:169:55 | right | methods.cs:169:59:169:59 | 0 | Plus(int, int) | +| methods.cs:174:133:174:133 | i | methods.cs:174:137:174:137 | 1 | SkipTwo(IEnumerable, int) | +| methods.cs:174:133:174:133 | i | methods.cs:174:137:174:137 | 1 | SkipTwo(IEnumerable, int) | +| methods.cs:179:137:179:137 | i | methods.cs:179:141:179:141 | 1 | SkipTwoInt(IEnumerable, int) | diff --git a/csharp/ql/test/library-tests/methods/PrintAst.expected b/csharp/ql/test/library-tests/methods/PrintAst.expected index a41e0362094..a2220c63ea0 100644 --- a/csharp/ql/test/library-tests/methods/PrintAst.expected +++ b/csharp/ql/test/library-tests/methods/PrintAst.expected @@ -1,53 +1,44 @@ methods.cs: -# 3| [NamespaceDeclaration] namespace ... { ... } -# 6| 1: [Class] TestRef -# 9| 5: [Method] Swap -# 9| -1: [TypeMention] Void +# 4| [NamespaceDeclaration] namespace ... { ... } +# 7| 1: [Class] TestRef +# 10| 5: [Method] Swap +# 10| -1: [TypeMention] Void #-----| 2: (Parameters) -# 9| 0: [Parameter] x -# 9| -1: [TypeMention] int -# 9| 1: [Parameter] y -# 9| -1: [TypeMention] int -# 10| 4: [BlockStmt] {...} -# 11| 0: [LocalVariableDeclStmt] ... ...; -# 11| 0: [LocalVariableDeclAndInitExpr] Int32 temp = ... -# 11| -1: [TypeMention] int -# 11| 0: [LocalVariableAccess] access to local variable temp -# 11| 1: [ParameterAccess] access to parameter x -# 12| 1: [ExprStmt] ...; -# 12| 0: [AssignExpr] ... = ... -# 12| 0: [ParameterAccess] access to parameter x -# 12| 1: [ParameterAccess] access to parameter y -# 13| 2: [ExprStmt] ...; +# 10| 0: [Parameter] x +# 10| -1: [TypeMention] int +# 10| 1: [Parameter] y +# 10| -1: [TypeMention] int +# 11| 4: [BlockStmt] {...} +# 12| 0: [LocalVariableDeclStmt] ... ...; +# 12| 0: [LocalVariableDeclAndInitExpr] Int32 temp = ... +# 12| -1: [TypeMention] int +# 12| 0: [LocalVariableAccess] access to local variable temp +# 12| 1: [ParameterAccess] access to parameter x +# 13| 1: [ExprStmt] ...; # 13| 0: [AssignExpr] ... = ... -# 13| 0: [ParameterAccess] access to parameter y -# 13| 1: [LocalVariableAccess] access to local variable temp -# 16| 6: [Method] Main -# 16| -1: [TypeMention] Void -# 17| 4: [BlockStmt] {...} -# 18| 0: [LocalVariableDeclStmt] ... ...; -# 18| 0: [LocalVariableDeclAndInitExpr] Int32 i = ... -# 18| -1: [TypeMention] int -# 18| 0: [LocalVariableAccess] access to local variable i -# 18| 1: [IntLiteral] 1 -# 18| 1: [LocalVariableDeclAndInitExpr] Int32 j = ... -# 18| -1: [TypeMention] int -# 18| 0: [LocalVariableAccess] access to local variable j -# 18| 1: [IntLiteral] 2 -# 19| 1: [ExprStmt] ...; -# 19| 0: [MethodCall] call to method Swap +# 13| 0: [ParameterAccess] access to parameter x +# 13| 1: [ParameterAccess] access to parameter y +# 14| 2: [ExprStmt] ...; +# 14| 0: [AssignExpr] ... = ... +# 14| 0: [ParameterAccess] access to parameter y +# 14| 1: [LocalVariableAccess] access to local variable temp +# 17| 6: [Method] Main +# 17| -1: [TypeMention] Void +# 18| 4: [BlockStmt] {...} +# 19| 0: [LocalVariableDeclStmt] ... ...; +# 19| 0: [LocalVariableDeclAndInitExpr] Int32 i = ... +# 19| -1: [TypeMention] int # 19| 0: [LocalVariableAccess] access to local variable i -# 19| 1: [LocalVariableAccess] access to local variable j -# 20| 2: [ExprStmt] ...; -# 20| 0: [MethodCall] call to method WriteLine -# 20| -1: [TypeAccess] access to type Console -# 20| 0: [TypeMention] Console -# 20| 0: [StringLiteral] "{0} {1}" -# 20| 1: [CastExpr] (...) ... -# 20| 1: [LocalVariableAccess] access to local variable i -# 20| 2: [CastExpr] (...) ... -# 20| 1: [LocalVariableAccess] access to local variable j -# 21| 3: [ExprStmt] ...; +# 19| 1: [IntLiteral] 1 +# 19| 1: [LocalVariableDeclAndInitExpr] Int32 j = ... +# 19| -1: [TypeMention] int +# 19| 0: [LocalVariableAccess] access to local variable j +# 19| 1: [IntLiteral] 2 +# 20| 1: [ExprStmt] ...; +# 20| 0: [MethodCall] call to method Swap +# 20| 0: [LocalVariableAccess] access to local variable i +# 20| 1: [LocalVariableAccess] access to local variable j +# 21| 2: [ExprStmt] ...; # 21| 0: [MethodCall] call to method WriteLine # 21| -1: [TypeAccess] access to type Console # 21| 0: [TypeMention] Console @@ -56,65 +47,65 @@ methods.cs: # 21| 1: [LocalVariableAccess] access to local variable i # 21| 2: [CastExpr] (...) ... # 21| 1: [LocalVariableAccess] access to local variable j -# 25| 2: [Class] TestOut -# 28| 5: [Method] Divide -# 28| -1: [TypeMention] Void +# 22| 3: [ExprStmt] ...; +# 22| 0: [MethodCall] call to method WriteLine +# 22| -1: [TypeAccess] access to type Console +# 22| 0: [TypeMention] Console +# 22| 0: [StringLiteral] "{0} {1}" +# 22| 1: [CastExpr] (...) ... +# 22| 1: [LocalVariableAccess] access to local variable i +# 22| 2: [CastExpr] (...) ... +# 22| 1: [LocalVariableAccess] access to local variable j +# 26| 2: [Class] TestOut +# 29| 5: [Method] Divide +# 29| -1: [TypeMention] Void #-----| 2: (Parameters) -# 28| 0: [Parameter] x -# 28| -1: [TypeMention] int -# 28| 1: [Parameter] y -# 28| -1: [TypeMention] int -# 28| 2: [Parameter] result -# 28| -1: [TypeMention] int -# 28| 3: [Parameter] remainder -# 28| -1: [TypeMention] int -# 29| 4: [BlockStmt] {...} -# 30| 0: [ExprStmt] ...; -# 30| 0: [AssignExpr] ... = ... -# 30| 0: [ParameterAccess] access to parameter result -# 30| 1: [DivExpr] ... / ... -# 30| 0: [ParameterAccess] access to parameter x -# 30| 1: [ParameterAccess] access to parameter y -# 31| 1: [ExprStmt] ...; +# 29| 0: [Parameter] x +# 29| -1: [TypeMention] int +# 29| 1: [Parameter] y +# 29| -1: [TypeMention] int +# 29| 2: [Parameter] result +# 29| -1: [TypeMention] int +# 29| 3: [Parameter] remainder +# 29| -1: [TypeMention] int +# 30| 4: [BlockStmt] {...} +# 31| 0: [ExprStmt] ...; # 31| 0: [AssignExpr] ... = ... -# 31| 0: [ParameterAccess] access to parameter remainder -# 31| 1: [RemExpr] ... % ... +# 31| 0: [ParameterAccess] access to parameter result +# 31| 1: [DivExpr] ... / ... # 31| 0: [ParameterAccess] access to parameter x # 31| 1: [ParameterAccess] access to parameter y -# 34| 6: [Method] Main -# 34| -1: [TypeMention] Void -# 35| 4: [BlockStmt] {...} -# 36| 0: [LocalVariableDeclStmt] ... ...; -# 36| 0: [LocalVariableDeclExpr] Int32 res -# 36| 0: [TypeMention] int -# 36| 1: [LocalVariableDeclExpr] Int32 rem -# 36| 0: [TypeMention] int -# 37| 1: [ExprStmt] ...; -# 37| 0: [MethodCall] call to method Divide -# 37| 0: [IntLiteral] 10 -# 37| 1: [IntLiteral] 3 -# 37| 2: [LocalVariableAccess] access to local variable res -# 37| 3: [LocalVariableAccess] access to local variable rem -# 38| 2: [ExprStmt] ...; -# 38| 0: [MethodCall] call to method WriteLine -# 38| -1: [TypeAccess] access to type Console -# 38| 0: [TypeMention] Console -# 38| 0: [StringLiteral] "{0} {1}" -# 38| 1: [CastExpr] (...) ... -# 38| 1: [LocalVariableAccess] access to local variable res -# 38| 2: [CastExpr] (...) ... -# 38| 1: [LocalVariableAccess] access to local variable rem -# 42| 3: [Class] Console -# 45| 5: [Method] Write -# 45| -1: [TypeMention] Void -#-----| 2: (Parameters) -# 45| 0: [Parameter] fmt -# 45| -1: [TypeMention] string -# 45| 1: [Parameter] args -# 45| -1: [TypeMention] Object[] -# 45| 1: [TypeMention] object -# 45| 4: [BlockStmt] {...} -# 46| 6: [Method] WriteLine +# 32| 1: [ExprStmt] ...; +# 32| 0: [AssignExpr] ... = ... +# 32| 0: [ParameterAccess] access to parameter remainder +# 32| 1: [RemExpr] ... % ... +# 32| 0: [ParameterAccess] access to parameter x +# 32| 1: [ParameterAccess] access to parameter y +# 35| 6: [Method] Main +# 35| -1: [TypeMention] Void +# 36| 4: [BlockStmt] {...} +# 37| 0: [LocalVariableDeclStmt] ... ...; +# 37| 0: [LocalVariableDeclExpr] Int32 res +# 37| 0: [TypeMention] int +# 37| 1: [LocalVariableDeclExpr] Int32 rem +# 37| 0: [TypeMention] int +# 38| 1: [ExprStmt] ...; +# 38| 0: [MethodCall] call to method Divide +# 38| 0: [IntLiteral] 10 +# 38| 1: [IntLiteral] 3 +# 38| 2: [LocalVariableAccess] access to local variable res +# 38| 3: [LocalVariableAccess] access to local variable rem +# 39| 2: [ExprStmt] ...; +# 39| 0: [MethodCall] call to method WriteLine +# 39| -1: [TypeAccess] access to type Console +# 39| 0: [TypeMention] Console +# 39| 0: [StringLiteral] "{0} {1}" +# 39| 1: [CastExpr] (...) ... +# 39| 1: [LocalVariableAccess] access to local variable res +# 39| 2: [CastExpr] (...) ... +# 39| 1: [LocalVariableAccess] access to local variable rem +# 43| 3: [Class] Console +# 46| 5: [Method] Write # 46| -1: [TypeMention] Void #-----| 2: (Parameters) # 46| 0: [Parameter] fmt @@ -123,354 +114,452 @@ methods.cs: # 46| -1: [TypeMention] Object[] # 46| 1: [TypeMention] object # 46| 4: [BlockStmt] {...} -# 49| 4: [Class] TestOverloading -# 52| 5: [Method] F -# 52| -1: [TypeMention] Void -# 53| 4: [BlockStmt] {...} -# 54| 0: [ExprStmt] ...; -# 54| 0: [MethodCall] call to method WriteLine -# 54| -1: [TypeAccess] access to type Console -# 54| 0: [TypeMention] Console -# 54| 0: [StringLiteral] "F()" -# 57| 6: [Method] F -# 57| -1: [TypeMention] Void +# 47| 6: [Method] WriteLine +# 47| -1: [TypeMention] Void #-----| 2: (Parameters) -# 57| 0: [Parameter] x -# 57| -1: [TypeMention] object -# 58| 4: [BlockStmt] {...} -# 59| 0: [ExprStmt] ...; -# 59| 0: [MethodCall] call to method WriteLine -# 59| -1: [TypeAccess] access to type Console -# 59| 0: [TypeMention] Console -# 59| 0: [StringLiteral] "F(object)" -# 62| 7: [Method] F -# 62| -1: [TypeMention] Void +# 47| 0: [Parameter] fmt +# 47| -1: [TypeMention] string +# 47| 1: [Parameter] args +# 47| -1: [TypeMention] Object[] +# 47| 1: [TypeMention] object +# 47| 4: [BlockStmt] {...} +# 50| 4: [Class] TestOverloading +# 53| 5: [Method] F +# 53| -1: [TypeMention] Void +# 54| 4: [BlockStmt] {...} +# 55| 0: [ExprStmt] ...; +# 55| 0: [MethodCall] call to method WriteLine +# 55| -1: [TypeAccess] access to type Console +# 55| 0: [TypeMention] Console +# 55| 0: [StringLiteral] "F()" +# 58| 6: [Method] F +# 58| -1: [TypeMention] Void #-----| 2: (Parameters) -# 62| 0: [Parameter] x -# 62| -1: [TypeMention] int -# 63| 4: [BlockStmt] {...} -# 64| 0: [ExprStmt] ...; -# 64| 0: [MethodCall] call to method WriteLine -# 64| -1: [TypeAccess] access to type Console -# 64| 0: [TypeMention] Console -# 64| 0: [StringLiteral] "F(int)" -# 67| 8: [Method] F -# 67| -1: [TypeMention] Void +# 58| 0: [Parameter] x +# 58| -1: [TypeMention] object +# 59| 4: [BlockStmt] {...} +# 60| 0: [ExprStmt] ...; +# 60| 0: [MethodCall] call to method WriteLine +# 60| -1: [TypeAccess] access to type Console +# 60| 0: [TypeMention] Console +# 60| 0: [StringLiteral] "F(object)" +# 63| 7: [Method] F +# 63| -1: [TypeMention] Void #-----| 2: (Parameters) -# 67| 0: [Parameter] x -# 67| -1: [TypeMention] double -# 68| 4: [BlockStmt] {...} -# 69| 0: [ExprStmt] ...; -# 69| 0: [MethodCall] call to method WriteLine -# 69| -1: [TypeAccess] access to type Console -# 69| 0: [TypeMention] Console -# 69| 0: [StringLiteral] "F(double)" -# 72| 9: [Method] F -# 72| -1: [TypeMention] Void +# 63| 0: [Parameter] x +# 63| -1: [TypeMention] int +# 64| 4: [BlockStmt] {...} +# 65| 0: [ExprStmt] ...; +# 65| 0: [MethodCall] call to method WriteLine +# 65| -1: [TypeAccess] access to type Console +# 65| 0: [TypeMention] Console +# 65| 0: [StringLiteral] "F(int)" +# 68| 8: [Method] F +# 68| -1: [TypeMention] Void +#-----| 2: (Parameters) +# 68| 0: [Parameter] x +# 68| -1: [TypeMention] double +# 69| 4: [BlockStmt] {...} +# 70| 0: [ExprStmt] ...; +# 70| 0: [MethodCall] call to method WriteLine +# 70| -1: [TypeAccess] access to type Console +# 70| 0: [TypeMention] Console +# 70| 0: [StringLiteral] "F(double)" +# 73| 9: [Method] F +# 73| -1: [TypeMention] Void #-----| 1: (Type parameters) -# 72| 0: [TypeParameter] T +# 73| 0: [TypeParameter] T #-----| 2: (Parameters) -# 72| 0: [Parameter] x -# 72| -1: [TypeMention] T -# 73| 4: [BlockStmt] {...} -# 74| 0: [ExprStmt] ...; -# 74| 0: [MethodCall] call to method WriteLine -# 74| -1: [TypeAccess] access to type Console -# 74| 0: [TypeMention] Console -# 74| 0: [StringLiteral] "F(T)" -# 77| 12: [Method] F -# 77| -1: [TypeMention] Void +# 73| 0: [Parameter] x +# 73| -1: [TypeMention] T +# 74| 4: [BlockStmt] {...} +# 75| 0: [ExprStmt] ...; +# 75| 0: [MethodCall] call to method WriteLine +# 75| -1: [TypeAccess] access to type Console +# 75| 0: [TypeMention] Console +# 75| 0: [StringLiteral] "F(T)" +# 78| 12: [Method] F +# 78| -1: [TypeMention] Void #-----| 2: (Parameters) -# 77| 0: [Parameter] x -# 77| -1: [TypeMention] double -# 77| 1: [Parameter] y -# 77| -1: [TypeMention] double -# 78| 4: [BlockStmt] {...} -# 79| 0: [ExprStmt] ...; -# 79| 0: [MethodCall] call to method WriteLine -# 79| -1: [TypeAccess] access to type Console -# 79| 0: [TypeMention] Console -# 79| 0: [StringLiteral] "F(double, double)" -# 82| 13: [Method] Main -# 82| -1: [TypeMention] Void -# 83| 4: [BlockStmt] {...} -# 84| 0: [ExprStmt] ...; -# 84| 0: [MethodCall] call to method F -# 85| 1: [ExprStmt] ...; +# 78| 0: [Parameter] x +# 78| -1: [TypeMention] double +# 78| 1: [Parameter] y +# 78| -1: [TypeMention] double +# 79| 4: [BlockStmt] {...} +# 80| 0: [ExprStmt] ...; +# 80| 0: [MethodCall] call to method WriteLine +# 80| -1: [TypeAccess] access to type Console +# 80| 0: [TypeMention] Console +# 80| 0: [StringLiteral] "F(double, double)" +# 83| 13: [Method] Main +# 83| -1: [TypeMention] Void +# 84| 4: [BlockStmt] {...} +# 85| 0: [ExprStmt] ...; # 85| 0: [MethodCall] call to method F -# 85| 0: [IntLiteral] 1 -# 86| 2: [ExprStmt] ...; +# 86| 1: [ExprStmt] ...; # 86| 0: [MethodCall] call to method F -# 86| 0: [DoubleLiteral] 1 -# 87| 3: [ExprStmt] ...; +# 86| 0: [IntLiteral] 1 +# 87| 2: [ExprStmt] ...; # 87| 0: [MethodCall] call to method F -# 87| 0: [StringLiteral] "abc" -# 88| 4: [ExprStmt] ...; +# 87| 0: [DoubleLiteral] 1 +# 88| 3: [ExprStmt] ...; # 88| 0: [MethodCall] call to method F -# 88| 0: [CastExpr] (...) ... -# 88| 0: [TypeAccess] access to type Double -# 88| 0: [TypeMention] double -# 88| 1: [IntLiteral] 1 -# 89| 5: [ExprStmt] ...; +# 88| 0: [StringLiteral] "abc" +# 89| 4: [ExprStmt] ...; # 89| 0: [MethodCall] call to method F # 89| 0: [CastExpr] (...) ... -# 89| 0: [TypeAccess] access to type Object -# 89| 0: [TypeMention] object +# 89| 0: [TypeAccess] access to type Double +# 89| 0: [TypeMention] double # 89| 1: [IntLiteral] 1 -# 90| 6: [ExprStmt] ...; +# 90| 5: [ExprStmt] ...; # 90| 0: [MethodCall] call to method F -# 90| 0: [IntLiteral] 1 -# 91| 7: [ExprStmt] ...; +# 90| 0: [CastExpr] (...) ... +# 90| 0: [TypeAccess] access to type Object +# 90| 0: [TypeMention] object +# 90| 1: [IntLiteral] 1 +# 91| 6: [ExprStmt] ...; # 91| 0: [MethodCall] call to method F -# 91| 0: [CastExpr] (...) ... -# 91| 1: [IntLiteral] 1 -# 91| 1: [CastExpr] (...) ... -# 91| 1: [IntLiteral] 1 -# 96| 5: [Class] Extensions -# 99| 4: [ExtensionMethod] ToInt32 -# 99| -1: [TypeMention] int +# 91| 0: [IntLiteral] 1 +# 92| 7: [ExprStmt] ...; +# 92| 0: [MethodCall] call to method F +# 92| 0: [CastExpr] (...) ... +# 92| 1: [IntLiteral] 1 +# 92| 1: [CastExpr] (...) ... +# 92| 1: [IntLiteral] 1 +# 97| 5: [Class] Extensions +# 100| 4: [ExtensionMethod] ToInt32 +# 100| -1: [TypeMention] int #-----| 2: (Parameters) -# 99| 0: [Parameter] s -# 99| -1: [TypeMention] string -# 100| 4: [BlockStmt] {...} -# 101| 0: [ReturnStmt] return ...; -# 101| 0: [MethodCall] call to method Parse -# 101| -1: [TypeAccess] access to type Int32 -# 101| 0: [TypeMention] int -# 101| 0: [ParameterAccess] access to parameter s -# 104| 5: [ExtensionMethod] ToBool -# 104| -1: [TypeMention] bool +# 100| 0: [Parameter] s +# 100| -1: [TypeMention] string +# 101| 4: [BlockStmt] {...} +# 102| 0: [ReturnStmt] return ...; +# 102| 0: [MethodCall] call to method Parse +# 102| -1: [TypeAccess] access to type Int32 +# 102| 0: [TypeMention] int +# 102| 0: [ParameterAccess] access to parameter s +# 105| 5: [ExtensionMethod] ToBool +# 105| -1: [TypeMention] bool #-----| 2: (Parameters) -# 104| 0: [Parameter] s -# 104| -1: [TypeMention] string -# 104| 1: [Parameter] f -# 104| -1: [TypeMention] Func -# 104| 1: [TypeMention] string -# 104| 2: [TypeMention] bool -# 105| 4: [BlockStmt] {...} -# 106| 0: [ReturnStmt] return ...; -# 106| 0: [DelegateCall] delegate call -# 106| -1: [ParameterAccess] access to parameter f -# 106| 0: [ParameterAccess] access to parameter s -# 109| 6: [ExtensionMethod] Slice -# 109| -1: [TypeMention] T[] -# 109| 1: [TypeMention] T +# 105| 0: [Parameter] s +# 105| -1: [TypeMention] string +# 105| 1: [Parameter] f +# 105| -1: [TypeMention] Func +# 105| 1: [TypeMention] string +# 105| 2: [TypeMention] bool +# 106| 4: [BlockStmt] {...} +# 107| 0: [ReturnStmt] return ...; +# 107| 0: [DelegateCall] delegate call +# 107| -1: [ParameterAccess] access to parameter f +# 107| 0: [ParameterAccess] access to parameter s +# 110| 6: [ExtensionMethod] Slice +# 110| -1: [TypeMention] T[] +# 110| 1: [TypeMention] T #-----| 1: (Type parameters) -# 109| 0: [TypeParameter] T +# 110| 0: [TypeParameter] T #-----| 2: (Parameters) -# 109| 0: [Parameter] source -# 109| -1: [TypeMention] T[] -# 109| 1: [TypeMention] T -# 109| 1: [Parameter] index -# 109| -1: [TypeMention] int -# 109| 2: [Parameter] count -# 109| -1: [TypeMention] int -# 110| 4: [BlockStmt] {...} -# 111| 0: [IfStmt] if (...) ... -# 111| 0: [LogicalOrExpr] ... || ... -# 111| 0: [LogicalOrExpr] ... || ... -# 111| 0: [LTExpr] ... < ... -# 111| 0: [ParameterAccess] access to parameter index -# 111| 1: [IntLiteral] 0 -# 111| 1: [LTExpr] ... < ... -# 111| 0: [ParameterAccess] access to parameter count -# 111| 1: [IntLiteral] 0 -# 111| 1: [LTExpr] ... < ... -# 111| 0: [SubExpr] ... - ... -# 111| 0: [PropertyCall] access to property Length -# 111| -1: [ParameterAccess] access to parameter source -# 111| 1: [ParameterAccess] access to parameter index -# 111| 1: [ParameterAccess] access to parameter count -# 112| 1: [ThrowStmt] throw ...; -# 112| 0: [ObjectCreation] object creation of type ArgumentException -# 112| 0: [TypeMention] ArgumentException -# 113| 1: [LocalVariableDeclStmt] ... ...; -# 113| 0: [LocalVariableDeclAndInitExpr] T[] result = ... -# 113| -1: [TypeMention] T[] -# 113| 1: [TypeMention] T -# 113| 0: [LocalVariableAccess] access to local variable result -# 113| 1: [ArrayCreation] array creation of type T[] -# 113| -1: [TypeMention] T[] -# 113| 1: [TypeMention] T -# 113| 0: [ParameterAccess] access to parameter count -# 114| 2: [ExprStmt] ...; -# 114| 0: [MethodCall] call to method Copy -# 114| -1: [TypeAccess] access to type Array -# 114| 0: [TypeMention] Array -# 114| 0: [ParameterAccess] access to parameter source -# 114| 1: [ParameterAccess] access to parameter index -# 114| 2: [LocalVariableAccess] access to local variable result -# 114| 3: [IntLiteral] 0 -# 114| 4: [ParameterAccess] access to parameter count -# 115| 3: [ReturnStmt] return ...; -# 115| 0: [LocalVariableAccess] access to local variable result -# 118| 8: [Method] CallToInt32 -# 118| -1: [TypeMention] int -# 118| 4: [MethodCall] call to method ToInt32 -# 118| 0: [StringLiteral] "0" -# 121| 6: [Class] TestExtensions -# 124| 4: [Method] Main -# 124| -1: [TypeMention] Void -# 125| 4: [BlockStmt] {...} -# 126| 0: [LocalVariableDeclStmt] ... ...; -# 126| 0: [LocalVariableDeclAndInitExpr] String[] strings = ... -# 126| -1: [TypeMention] String[] -# 126| 1: [TypeMention] string -# 126| 0: [LocalVariableAccess] access to local variable strings -# 126| 1: [ArrayCreation] array creation of type String[] -# 126| -1: [ArrayInitializer] { ..., ... } -# 126| 0: [StringLiteral] "1" -# 126| 1: [StringLiteral] "22" -# 126| 2: [StringLiteral] "333" -# 126| 3: [StringLiteral] "4444" -# 127| 1: [ForeachStmt] foreach (... ... in ...) ... -# 127| 0: [LocalVariableDeclExpr] String s -# 127| 0: [TypeMention] string -# 127| 1: [MethodCall] call to method Slice -# 127| -1: [LocalVariableAccess] access to local variable strings -# 127| 0: [IntLiteral] 1 -# 127| 1: [IntLiteral] 2 -# 128| 2: [BlockStmt] {...} -# 129| 0: [ExprStmt] ...; -# 129| 0: [MethodCall] call to method WriteLine -# 129| -1: [TypeAccess] access to type Console -# 129| 0: [TypeMention] Console -# 129| 0: [MethodCall] call to method ToInt32 -# 129| -1: [LocalVariableAccess] access to local variable s -# 132| 2: [ExprStmt] ...; -# 132| 0: [MethodCall] call to method ToInt32 -# 132| -1: [TypeAccess] access to type Extensions -# 132| 0: [TypeMention] Extensions -# 132| 0: [StringLiteral] "" -# 134| 3: [ExprStmt] ...; -# 134| 0: [MethodCall] call to method ToBool -# 134| -1: [TypeAccess] access to type Extensions -# 134| 0: [TypeMention] Extensions -# 134| 0: [StringLiteral] "true" -# 134| 1: [ImplicitDelegateCreation] delegate creation of type Func -# 134| 0: [MethodAccess] access to method Parse -# 134| -1: [TypeAccess] access to type Boolean -# 134| 0: [TypeMention] bool -# 139| 7: [Class] TestDefaultParameters -# 141| 4: [Method] Method1 -# 141| -1: [TypeMention] Void +# 110| 0: [Parameter] source +# 110| -1: [TypeMention] T[] +# 110| 1: [TypeMention] T +# 110| 1: [Parameter] index +# 110| -1: [TypeMention] int +# 110| 2: [Parameter] count +# 110| -1: [TypeMention] int +# 111| 4: [BlockStmt] {...} +# 112| 0: [IfStmt] if (...) ... +# 112| 0: [LogicalOrExpr] ... || ... +# 112| 0: [LogicalOrExpr] ... || ... +# 112| 0: [LTExpr] ... < ... +# 112| 0: [ParameterAccess] access to parameter index +# 112| 1: [IntLiteral] 0 +# 112| 1: [LTExpr] ... < ... +# 112| 0: [ParameterAccess] access to parameter count +# 112| 1: [IntLiteral] 0 +# 112| 1: [LTExpr] ... < ... +# 112| 0: [SubExpr] ... - ... +# 112| 0: [PropertyCall] access to property Length +# 112| -1: [ParameterAccess] access to parameter source +# 112| 1: [ParameterAccess] access to parameter index +# 112| 1: [ParameterAccess] access to parameter count +# 113| 1: [ThrowStmt] throw ...; +# 113| 0: [ObjectCreation] object creation of type ArgumentException +# 113| 0: [TypeMention] ArgumentException +# 114| 1: [LocalVariableDeclStmt] ... ...; +# 114| 0: [LocalVariableDeclAndInitExpr] T[] result = ... +# 114| -1: [TypeMention] T[] +# 114| 1: [TypeMention] T +# 114| 0: [LocalVariableAccess] access to local variable result +# 114| 1: [ArrayCreation] array creation of type T[] +# 114| -1: [TypeMention] T[] +# 114| 1: [TypeMention] T +# 114| 0: [ParameterAccess] access to parameter count +# 115| 2: [ExprStmt] ...; +# 115| 0: [MethodCall] call to method Copy +# 115| -1: [TypeAccess] access to type Array +# 115| 0: [TypeMention] Array +# 115| 0: [ParameterAccess] access to parameter source +# 115| 1: [ParameterAccess] access to parameter index +# 115| 2: [LocalVariableAccess] access to local variable result +# 115| 3: [IntLiteral] 0 +# 115| 4: [ParameterAccess] access to parameter count +# 116| 3: [ReturnStmt] return ...; +# 116| 0: [LocalVariableAccess] access to local variable result +# 119| 8: [Method] CallToInt32 +# 119| -1: [TypeMention] int +# 119| 4: [MethodCall] call to method ToInt32 +# 119| 0: [StringLiteral] "0" +# 122| 6: [Class] TestExtensions +# 125| 4: [Method] Main +# 125| -1: [TypeMention] Void +# 126| 4: [BlockStmt] {...} +# 127| 0: [LocalVariableDeclStmt] ... ...; +# 127| 0: [LocalVariableDeclAndInitExpr] String[] strings = ... +# 127| -1: [TypeMention] String[] +# 127| 1: [TypeMention] string +# 127| 0: [LocalVariableAccess] access to local variable strings +# 127| 1: [ArrayCreation] array creation of type String[] +# 127| -1: [ArrayInitializer] { ..., ... } +# 127| 0: [StringLiteral] "1" +# 127| 1: [StringLiteral] "22" +# 127| 2: [StringLiteral] "333" +# 127| 3: [StringLiteral] "4444" +# 128| 1: [ForeachStmt] foreach (... ... in ...) ... +# 128| 0: [LocalVariableDeclExpr] String s +# 128| 0: [TypeMention] string +# 128| 1: [MethodCall] call to method Slice +# 128| -1: [LocalVariableAccess] access to local variable strings +# 128| 0: [IntLiteral] 1 +# 128| 1: [IntLiteral] 2 +# 129| 2: [BlockStmt] {...} +# 130| 0: [ExprStmt] ...; +# 130| 0: [MethodCall] call to method WriteLine +# 130| -1: [TypeAccess] access to type Console +# 130| 0: [TypeMention] Console +# 130| 0: [MethodCall] call to method ToInt32 +# 130| -1: [LocalVariableAccess] access to local variable s +# 133| 2: [ExprStmt] ...; +# 133| 0: [MethodCall] call to method ToInt32 +# 133| -1: [TypeAccess] access to type Extensions +# 133| 0: [TypeMention] Extensions +# 133| 0: [StringLiteral] "" +# 135| 3: [ExprStmt] ...; +# 135| 0: [MethodCall] call to method ToBool +# 135| -1: [TypeAccess] access to type Extensions +# 135| 0: [TypeMention] Extensions +# 135| 0: [StringLiteral] "true" +# 135| 1: [ImplicitDelegateCreation] delegate creation of type Func +# 135| 0: [MethodAccess] access to method Parse +# 135| -1: [TypeAccess] access to type Boolean +# 135| 0: [TypeMention] bool +# 140| 7: [Class] TestDefaultParameters +# 142| 4: [Method] Method1 +# 142| -1: [TypeMention] Void #-----| 2: (Parameters) -# 141| 0: [Parameter] x -# 141| -1: [TypeMention] int -# 141| 1: [Parameter] y -# 141| -1: [TypeMention] int -# 142| 4: [BlockStmt] {...} -# 145| 5: [Method] Method2 -# 145| -1: [TypeMention] Void +# 142| 0: [Parameter] x +# 142| -1: [TypeMention] int +# 142| 1: [Parameter] y +# 142| -1: [TypeMention] int +# 143| 4: [BlockStmt] {...} +# 146| 5: [Method] Method2 +# 146| -1: [TypeMention] Void #-----| 2: (Parameters) -# 145| 0: [Parameter] a -# 145| -1: [TypeMention] int -# 145| 1: [Parameter] b -# 145| -1: [TypeMention] int -# 145| 2: [Parameter] c -# 145| -1: [TypeMention] int -# 145| 1: [IntLiteral] 1 -# 145| 3: [Parameter] d -# 145| -1: [TypeMention] int -# 145| 1: [IntLiteral] 2 -# 145| 4: [Parameter] e -# 145| -1: [TypeMention] string -# 145| 1: [AddExpr] ... + ... -# 145| 0: [StringLiteral] "a" -# 145| 1: [StringLiteral] "b" -# 146| 4: [BlockStmt] {...} -# 149| 6: [InstanceConstructor] TestDefaultParameters +# 146| 0: [Parameter] a +# 146| -1: [TypeMention] int +# 146| 1: [Parameter] b +# 146| -1: [TypeMention] int +# 146| 2: [Parameter] c +# 146| -1: [TypeMention] int +# 146| 1: [IntLiteral] 1 +# 146| 3: [Parameter] d +# 146| -1: [TypeMention] int +# 146| 1: [IntLiteral] 2 +# 146| 4: [Parameter] e +# 146| -1: [TypeMention] string +# 146| 1: [AddExpr] ... + ... +# 146| 0: [StringLiteral] "a" +# 146| 1: [StringLiteral] "b" +# 147| 4: [BlockStmt] {...} +# 150| 6: [InstanceConstructor] TestDefaultParameters #-----| 2: (Parameters) -# 149| 0: [Parameter] x -# 149| -1: [TypeMention] int -# 150| 4: [BlockStmt] {...} -# 153| 7: [InstanceConstructor] TestDefaultParameters +# 150| 0: [Parameter] x +# 150| -1: [TypeMention] int +# 151| 4: [BlockStmt] {...} +# 154| 7: [InstanceConstructor] TestDefaultParameters #-----| 2: (Parameters) -# 153| 0: [Parameter] x -# 153| -1: [TypeMention] string -# 153| 1: [StringLiteral] "abc" -# 153| 1: [Parameter] y -# 153| -1: [TypeMention] double -# 153| 1: [ObjectCreation] object creation of type Double -# 153| 0: [TypeMention] double -# 154| 4: [BlockStmt] {...} -# 157| 8: [DelegateType] Del +# 154| 0: [Parameter] x +# 154| -1: [TypeMention] string +# 154| 1: [StringLiteral] "abc" +# 154| 1: [Parameter] y +# 154| -1: [TypeMention] double +# 154| 1: [ObjectCreation] object creation of type Double +# 154| 0: [TypeMention] double +# 155| 4: [BlockStmt] {...} +# 158| 8: [DelegateType] Del #-----| 2: (Parameters) -# 157| 0: [Parameter] a -# 157| -1: [TypeMention] string -# 157| 1: [Parameter] b -# 157| -1: [TypeMention] int -# 157| 1: [IntLiteral] 12 -# 157| 2: [Parameter] c -# 157| -1: [TypeMention] double -# 157| 1: [ObjectCreation] object creation of type Double -# 157| 0: [TypeMention] double -# 159| 9: [Indexer] Item -# 159| -1: [TypeMention] int +# 158| 0: [Parameter] a +# 158| -1: [TypeMention] string +# 158| 1: [Parameter] b +# 158| -1: [TypeMention] int +# 158| 1: [IntLiteral] 12 +# 158| 2: [Parameter] c +# 158| -1: [TypeMention] double +# 158| 1: [ObjectCreation] object creation of type Double +# 158| 0: [TypeMention] double +# 160| 9: [Indexer] Item +# 160| -1: [TypeMention] int #-----| 1: (Parameters) -# 159| 0: [Parameter] x -# 159| -1: [TypeMention] int -# 159| 1: [Parameter] y -# 159| -1: [TypeMention] int -# 159| 1: [IntLiteral] 0 -# 161| 3: [Getter] get_Item +# 160| 0: [Parameter] x +# 160| -1: [TypeMention] int +# 160| 1: [Parameter] y +# 160| -1: [TypeMention] int +# 160| 1: [IntLiteral] 0 +# 162| 3: [Getter] get_Item #-----| 2: (Parameters) -# 159| 0: [Parameter] x -# 159| 1: [Parameter] y -# 159| 1: [IntLiteral] 0 -# 161| 4: [BlockStmt] {...} -# 161| 0: [ReturnStmt] return ...; -# 161| 0: [AddExpr] ... + ... -# 161| 0: [ParameterAccess] access to parameter x -# 161| 1: [ParameterAccess] access to parameter y -# 162| 4: [Setter] set_Item -#-----| 2: (Parameters) -# 159| 0: [Parameter] x -# 159| 1: [Parameter] y -# 159| 1: [IntLiteral] 0 -# 162| 2: [Parameter] value +# 160| 0: [Parameter] x +# 160| 1: [Parameter] y +# 160| 1: [IntLiteral] 0 # 162| 4: [BlockStmt] {...} -# 166| 8: [Class] TestDefaultExtensionParameters -# 168| 4: [ExtensionMethod] Plus -# 168| -1: [TypeMention] int +# 162| 0: [ReturnStmt] return ...; +# 162| 0: [AddExpr] ... + ... +# 162| 0: [ParameterAccess] access to parameter x +# 162| 1: [ParameterAccess] access to parameter y +# 163| 4: [Setter] set_Item +#-----| 2: (Parameters) +# 160| 0: [Parameter] x +# 160| 1: [Parameter] y +# 160| 1: [IntLiteral] 0 +# 163| 2: [Parameter] value +# 163| 4: [BlockStmt] {...} +# 167| 8: [Class] TestDefaultExtensionParameters +# 169| 4: [ExtensionMethod] Plus +# 169| -1: [TypeMention] int #-----| 2: (Parameters) -# 168| 0: [Parameter] left -# 168| -1: [TypeMention] int -# 168| 1: [Parameter] right -# 168| -1: [TypeMention] int -# 168| 1: [IntLiteral] 0 -# 169| 4: [BlockStmt] {...} -# 170| 0: [ReturnStmt] return ...; -# 170| 0: [AddExpr] ... + ... -# 170| 0: [ParameterAccess] access to parameter left -# 170| 1: [ParameterAccess] access to parameter right -# 173| 5: [ExtensionMethod] SkipTwo -# 173| -1: [TypeMention] IEnumerable -# 173| 1: [TypeMention] T +# 169| 0: [Parameter] left +# 169| -1: [TypeMention] int +# 169| 1: [Parameter] right +# 169| -1: [TypeMention] int +# 169| 1: [IntLiteral] 0 +# 170| 4: [BlockStmt] {...} +# 171| 0: [ReturnStmt] return ...; +# 171| 0: [AddExpr] ... + ... +# 171| 0: [ParameterAccess] access to parameter left +# 171| 1: [ParameterAccess] access to parameter right +# 174| 5: [ExtensionMethod] SkipTwo +# 174| -1: [TypeMention] IEnumerable +# 174| 1: [TypeMention] T #-----| 1: (Type parameters) -# 173| 0: [TypeParameter] T +# 174| 0: [TypeParameter] T #-----| 2: (Parameters) -# 173| 0: [Parameter] list -# 173| -1: [TypeMention] IEnumerable -# 173| 1: [TypeMention] T -# 173| 1: [Parameter] i -# 173| -1: [TypeMention] int -# 173| 1: [IntLiteral] 1 -# 174| 4: [BlockStmt] {...} -# 175| 0: [ReturnStmt] return ...; -# 175| 0: [ParameterAccess] access to parameter list -# 178| 7: [ExtensionMethod] SkipTwoInt -# 178| -1: [TypeMention] IEnumerable -# 178| 1: [TypeMention] int +# 174| 0: [Parameter] list +# 174| -1: [TypeMention] IEnumerable +# 174| 1: [TypeMention] T +# 174| 1: [Parameter] i +# 174| -1: [TypeMention] int +# 174| 1: [IntLiteral] 1 +# 175| 4: [BlockStmt] {...} +# 176| 0: [ReturnStmt] return ...; +# 176| 0: [ParameterAccess] access to parameter list +# 179| 7: [ExtensionMethod] SkipTwoInt +# 179| -1: [TypeMention] IEnumerable +# 179| 1: [TypeMention] int #-----| 2: (Parameters) -# 178| 0: [Parameter] list -# 178| -1: [TypeMention] IEnumerable -# 178| 1: [TypeMention] int -# 178| 1: [Parameter] i -# 178| -1: [TypeMention] int -# 178| 1: [IntLiteral] 1 -# 179| 4: [BlockStmt] {...} -# 180| 0: [ReturnStmt] return ...; -# 180| 0: [MethodCall] call to method SkipTwo -# 180| -1: [ParameterAccess] access to parameter list -# 180| 0: [ParameterAccess] access to parameter i +# 179| 0: [Parameter] list +# 179| -1: [TypeMention] IEnumerable +# 179| 1: [TypeMention] int +# 179| 1: [Parameter] i +# 179| -1: [TypeMention] int +# 179| 1: [IntLiteral] 1 +# 180| 4: [BlockStmt] {...} +# 181| 0: [ReturnStmt] return ...; +# 181| 0: [MethodCall] call to method SkipTwo +# 181| -1: [ParameterAccess] access to parameter list +# 181| 0: [ParameterAccess] access to parameter i +# 185| 9: [Class] TestCollidingMethods<> +#-----| 1: (Type parameters) +# 185| 0: [TypeParameter] T +# 187| 5: [Method] M +# 187| -1: [TypeMention] Void +#-----| 2: (Parameters) +# 187| 0: [Parameter] p1 +# 187| -1: [TypeMention] T +# 187| 1: [Parameter] p2 +# 187| -1: [TypeMention] int +# 187| 4: [BlockStmt] {...} +# 188| 6: [Method] M +# 188| -1: [TypeMention] Void +#-----| 2: (Parameters) +# 188| 0: [Parameter] p1 +# 188| -1: [TypeMention] int +# 188| 1: [Parameter] p2 +# 188| -1: [TypeMention] int +# 188| 4: [BlockStmt] {...} +# 190| 7: [Method] Calls +# 190| -1: [TypeMention] Void +# 191| 4: [BlockStmt] {...} +# 192| 0: [LocalVariableDeclStmt] ... ...; +# 192| 0: [LocalVariableDeclAndInitExpr] TestCollidingMethods x = ... +# 192| -1: [TypeMention] TestCollidingMethods +# 192| 0: [LocalVariableAccess] access to local variable x +# 192| 1: [ObjectCreation] object creation of type TestCollidingMethods +# 192| 0: [TypeMention] TestCollidingMethods +# 192| 1: [TypeMention] int +# 193| 1: [ExprStmt] ...; +# 193| 0: [MethodCall] call to method M +# 193| -1: [LocalVariableAccess] access to local variable x +# 193| 0: [IntLiteral] 1 +# 193| 1: [IntLiteral] 1 +# 195| 2: [LocalVariableDeclStmt] ... ...; +# 195| 0: [LocalVariableDeclAndInitExpr] TestCollidingMethods y = ... +# 195| -1: [TypeMention] TestCollidingMethods +# 195| 0: [LocalVariableAccess] access to local variable y +# 195| 1: [ObjectCreation] object creation of type TestCollidingMethods +# 195| 0: [TypeMention] TestCollidingMethods +# 195| 1: [TypeMention] double +# 196| 3: [ExprStmt] ...; +# 196| 0: [MethodCall] call to method M +# 196| -1: [LocalVariableAccess] access to local variable y +# 196| 0: [DoubleLiteral] 1 +# 196| 1: [IntLiteral] 1 +# 197| 4: [ExprStmt] ...; +# 197| 0: [MethodCall] call to method M +# 197| -1: [LocalVariableAccess] access to local variable y +# 197| 0: [IntLiteral] 1 +# 197| 1: [IntLiteral] 1 +# 200| 8: [Class] Nested +# 202| 4: [InstanceConstructor] Nested +#-----| 2: (Parameters) +# 202| 0: [Parameter] p1 +# 202| -1: [TypeMention] int +# 202| 4: [BlockStmt] {...} +# 203| 5: [InstanceConstructor] Nested +#-----| 2: (Parameters) +# 203| 0: [Parameter] p1 +# 203| -1: [TypeMention] T +# 204| 4: [BlockStmt] {...} +# 205| 0: [LocalVariableDeclStmt] ... ...; +# 205| 0: [LocalVariableDeclAndInitExpr] Nested x = ... +# 205| -1: [TypeMention] Nested +# 205| 0: [LocalVariableAccess] access to local variable x +# 205| 1: [ObjectCreation] object creation of type Nested +# 205| -1: [TypeMention] Nested +# 205| 1: [TypeMention] TestCollidingMethods +# 205| 1: [TypeMention] int +# 205| 0: [IntLiteral] 1 +# 206| 1: [LocalVariableDeclStmt] ... ...; +# 206| 0: [LocalVariableDeclAndInitExpr] Nested y = ... +# 206| -1: [TypeMention] Nested +# 206| 0: [LocalVariableAccess] access to local variable y +# 206| 1: [ObjectCreation] object creation of type Nested +# 206| -1: [TypeMention] Nested +# 206| 1: [TypeMention] TestCollidingMethods +# 206| 1: [TypeMention] double +# 206| 0: [DoubleLiteral] 1 +# 207| 2: [LocalVariableDeclStmt] ... ...; +# 207| 0: [LocalVariableDeclAndInitExpr] Nested z = ... +# 207| -1: [TypeMention] Nested +# 207| 0: [LocalVariableAccess] access to local variable z +# 207| 1: [ObjectCreation] object creation of type Nested +# 207| -1: [TypeMention] Nested +# 207| 1: [TypeMention] TestCollidingMethods +# 207| 1: [TypeMention] double +# 207| 0: [IntLiteral] 1 diff --git a/csharp/ql/test/library-tests/methods/methods.cs b/csharp/ql/test/library-tests/methods/methods.cs index f67676cad46..72e9b0409c0 100644 --- a/csharp/ql/test/library-tests/methods/methods.cs +++ b/csharp/ql/test/library-tests/methods/methods.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace Methods { @@ -180,4 +181,31 @@ namespace Methods return list.SkipTwo(i); } } + + public class TestCollidingMethods + { + public void M(T p1, int p2) { } + public void M(int p1, int p2) { } + + public void Calls() + { + var x = new TestCollidingMethods(); + x.M(1, 1); + + var y = new TestCollidingMethods(); + y.M(1.0, 1); + y.M(1, 1); + } + + public class Nested + { + public Nested(int p1) { } + public Nested(T p1) + { + var x = new TestCollidingMethods.Nested(1); + var y = new TestCollidingMethods.Nested(1.0); + var z = new TestCollidingMethods.Nested(1); + } + } + } } diff --git a/csharp/ql/test/library-tests/parameters/Parameters.cs b/csharp/ql/test/library-tests/parameters/Parameters.cs new file mode 100644 index 00000000000..b7cc3b001a9 --- /dev/null +++ b/csharp/ql/test/library-tests/parameters/Parameters.cs @@ -0,0 +1,17 @@ +public class Parameters +{ + public void M1(int a, object b, string c) => throw null; + public void M2(int a, object b = null, string c = "default string") => throw null; + public void M3(int a = 1, object b = null, string c = "null") => throw null; + public void M4(int a = default, object b = default) => throw null; + public void M5(int a = new int(), object b = default) => throw null; + public void M6(MyStruct s1, MyStruct s2 = default(MyStruct), MyStruct s3 = new MyStruct()) => throw null; + public void M7(MyEnum e1, MyEnum e2 = default(MyEnum), MyEnum e3 = new MyEnum(), MyEnum e4 = MyEnum.A, MyEnum e5 = (MyEnum)5) => throw null; + + public void M8(T t = default) => throw null; + public void M9(T t = default) where T : struct => throw null; + public void M10(T t = default) where T : class => throw null; + + public struct MyStruct { } + public enum MyEnum { A = 1, B = 2 } +} \ No newline at end of file diff --git a/csharp/ql/test/library-tests/parameters/Parameters.cs_ b/csharp/ql/test/library-tests/parameters/Parameters.cs_ new file mode 100644 index 00000000000..062c4b98b18 --- /dev/null +++ b/csharp/ql/test/library-tests/parameters/Parameters.cs_ @@ -0,0 +1,17 @@ +public class ParametersDll +{ + public void M1(int a, object b, string c) => throw null; + public void M2(int a, object b = null, string c = "default string") => throw null; + public void M3(int a = 1, object b = null, string c = "null") => throw null; + public void M4(int a = default, object b = default) => throw null; + public void M5(int a = new int(), object b = default) => throw null; + public void M6(MyStruct s1, MyStruct s2 = default(MyStruct), MyStruct s3 = new MyStruct()) => throw null; + public void M7(MyEnum e1, MyEnum e2 = default(MyEnum), MyEnum e3 = new MyEnum(), MyEnum e4 = MyEnum.A, MyEnum e5 = (MyEnum)5) => throw null; + + public void M8(T t = default) => throw null; + public void M9(T t = default) where T : struct => throw null; + public void M10(T t = default) where T : class => throw null; + + public struct MyStruct { } + public enum MyEnum { A = 1, B = 2 } +} \ No newline at end of file diff --git a/csharp/ql/test/library-tests/parameters/Parameters.dll b/csharp/ql/test/library-tests/parameters/Parameters.dll new file mode 100644 index 00000000000..cd48ebef015 Binary files /dev/null and b/csharp/ql/test/library-tests/parameters/Parameters.dll differ diff --git a/csharp/ql/test/library-tests/parameters/Parameters.expected b/csharp/ql/test/library-tests/parameters/Parameters.expected new file mode 100644 index 00000000000..864464167b9 --- /dev/null +++ b/csharp/ql/test/library-tests/parameters/Parameters.expected @@ -0,0 +1,50 @@ +noDefaultValue +| Parameters.cs:3:17:3:18 | M1 | Parameters.cs:3:24:3:24 | a | 0 | +| Parameters.cs:3:17:3:18 | M1 | Parameters.cs:3:34:3:34 | b | 1 | +| Parameters.cs:3:17:3:18 | M1 | Parameters.cs:3:44:3:44 | c | 2 | +| Parameters.cs:4:17:4:18 | M2 | Parameters.cs:4:24:4:24 | a | 0 | +| Parameters.cs:8:17:8:18 | M6 | Parameters.cs:8:29:8:30 | s1 | 0 | +| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:27:9:28 | e1 | 0 | +| Parameters.dll:0:0:0:0 | M1 | Parameters.dll:0:0:0:0 | a | 0 | +| Parameters.dll:0:0:0:0 | M1 | Parameters.dll:0:0:0:0 | b | 1 | +| Parameters.dll:0:0:0:0 | M1 | Parameters.dll:0:0:0:0 | c | 2 | +| Parameters.dll:0:0:0:0 | M2 | Parameters.dll:0:0:0:0 | a | 0 | +| Parameters.dll:0:0:0:0 | M6 | Parameters.dll:0:0:0:0 | s1 | 0 | +| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e1 | 0 | +withDefaultValue +| Parameters.cs:4:17:4:18 | M2 | Parameters.cs:4:34:4:34 | b | 1 | Parameters.cs:4:38:4:41 | null | null | +| Parameters.cs:4:17:4:18 | M2 | Parameters.cs:4:51:4:51 | c | 2 | Parameters.cs:4:55:4:70 | "default string" | default string | +| Parameters.cs:5:17:5:18 | M3 | Parameters.cs:5:24:5:24 | a | 0 | Parameters.cs:5:28:5:28 | 1 | 1 | +| Parameters.cs:5:17:5:18 | M3 | Parameters.cs:5:38:5:38 | b | 1 | Parameters.cs:5:42:5:45 | null | null | +| Parameters.cs:5:17:5:18 | M3 | Parameters.cs:5:55:5:55 | c | 2 | Parameters.cs:5:59:5:64 | "null" | null | +| Parameters.cs:6:17:6:18 | M4 | Parameters.cs:6:24:6:24 | a | 0 | Parameters.cs:6:28:6:34 | (...) ... | 0 | +| Parameters.cs:6:17:6:18 | M4 | Parameters.cs:6:44:6:44 | b | 1 | Parameters.cs:6:48:6:54 | default | null | +| Parameters.cs:7:17:7:18 | M5 | Parameters.cs:7:24:7:24 | a | 0 | Parameters.cs:7:28:7:36 | object creation of type Int32 | 0 | +| Parameters.cs:7:17:7:18 | M5 | Parameters.cs:7:46:7:46 | b | 1 | Parameters.cs:7:50:7:56 | default | null | +| Parameters.cs:8:17:8:18 | M6 | Parameters.cs:8:42:8:43 | s2 | 1 | Parameters.cs:8:47:8:63 | default(...) | - | +| Parameters.cs:8:17:8:18 | M6 | Parameters.cs:8:75:8:76 | s3 | 2 | Parameters.cs:8:80:8:93 | object creation of type MyStruct | - | +| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:38:9:39 | e2 | 1 | Parameters.cs:9:43:9:57 | default(...) | 0 | +| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:67:9:68 | e3 | 2 | Parameters.cs:9:72:9:83 | object creation of type MyEnum | 0 | +| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:93:9:94 | e4 | 3 | Parameters.cs:9:98:9:105 | access to constant A | 1 | +| Parameters.cs:9:17:9:18 | M7 | Parameters.cs:9:115:9:116 | e5 | 4 | Parameters.cs:9:120:9:128 | (...) ... | 5 | +| Parameters.cs:11:17:11:21 | M8 | Parameters.cs:11:25:11:25 | t | 0 | Parameters.cs:11:29:11:35 | (...) ... | - | +| Parameters.cs:12:17:12:21 | M9 | Parameters.cs:12:25:12:25 | t | 0 | Parameters.cs:12:29:12:35 | (...) ... | - | +| Parameters.cs:13:17:13:22 | M10 | Parameters.cs:13:26:13:26 | t | 0 | Parameters.cs:13:30:13:36 | (...) ... | null | +| Parameters.dll:0:0:0:0 | M2 | Parameters.dll:0:0:0:0 | b | 1 | Parameters.dll:0:0:0:0 | default | null | +| Parameters.dll:0:0:0:0 | M2 | Parameters.dll:0:0:0:0 | c | 2 | Parameters.dll:0:0:0:0 | "default string" | default string | +| Parameters.dll:0:0:0:0 | M3 | Parameters.dll:0:0:0:0 | a | 0 | Parameters.dll:0:0:0:0 | 1 | 1 | +| Parameters.dll:0:0:0:0 | M3 | Parameters.dll:0:0:0:0 | b | 1 | Parameters.dll:0:0:0:0 | default | null | +| Parameters.dll:0:0:0:0 | M3 | Parameters.dll:0:0:0:0 | c | 2 | Parameters.dll:0:0:0:0 | "null" | null | +| Parameters.dll:0:0:0:0 | M4 | Parameters.dll:0:0:0:0 | a | 0 | Parameters.dll:0:0:0:0 | 0 | 0 | +| Parameters.dll:0:0:0:0 | M4 | Parameters.dll:0:0:0:0 | b | 1 | Parameters.dll:0:0:0:0 | default | null | +| Parameters.dll:0:0:0:0 | M5 | Parameters.dll:0:0:0:0 | a | 0 | Parameters.dll:0:0:0:0 | 0 | 0 | +| Parameters.dll:0:0:0:0 | M5 | Parameters.dll:0:0:0:0 | b | 1 | Parameters.dll:0:0:0:0 | default | null | +| Parameters.dll:0:0:0:0 | M6 | Parameters.dll:0:0:0:0 | s2 | 1 | Parameters.dll:0:0:0:0 | default | - | +| Parameters.dll:0:0:0:0 | M6 | Parameters.dll:0:0:0:0 | s3 | 2 | Parameters.dll:0:0:0:0 | default | - | +| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e2 | 1 | Parameters.dll:0:0:0:0 | (...) ... | 0 | +| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e3 | 2 | Parameters.dll:0:0:0:0 | (...) ... | 0 | +| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e4 | 3 | Parameters.dll:0:0:0:0 | (...) ... | 1 | +| Parameters.dll:0:0:0:0 | M7 | Parameters.dll:0:0:0:0 | e5 | 4 | Parameters.dll:0:0:0:0 | (...) ... | 5 | +| Parameters.dll:0:0:0:0 | M8 | Parameters.dll:0:0:0:0 | t | 0 | Parameters.dll:0:0:0:0 | default | - | +| Parameters.dll:0:0:0:0 | M9 | Parameters.dll:0:0:0:0 | t | 0 | Parameters.dll:0:0:0:0 | default | - | +| Parameters.dll:0:0:0:0 | M10 | Parameters.dll:0:0:0:0 | t | 0 | Parameters.dll:0:0:0:0 | default | null | diff --git a/csharp/ql/test/library-tests/parameters/Parameters.ql b/csharp/ql/test/library-tests/parameters/Parameters.ql new file mode 100644 index 00000000000..dca5c2d9006 --- /dev/null +++ b/csharp/ql/test/library-tests/parameters/Parameters.ql @@ -0,0 +1,19 @@ +import csharp + +private predicate fromTestLocation(Element e) { + e.fromSource() or e.getFile().getStem() = "Parameters" +} + +query predicate noDefaultValue(Parameterizable container, Parameter p, int i) { + fromTestLocation(container) and + not p.hasDefaultValue() and + container.getParameter(i) = p +} + +query predicate withDefaultValue(Parameterizable container, Parameter p, int i, Expr e, string value) { + fromTestLocation(container) and + p.hasDefaultValue() and + container.getParameter(i) = p and + p.getDefaultValue() = e and + if exists(e.getValue()) then value = e.getValue() else value = "-" +} diff --git a/csharp/ql/test/library-tests/regressions/{}.cs b/csharp/ql/test/library-tests/regressions/{}.cs new file mode 100644 index 00000000000..266f8ccf4b1 --- /dev/null +++ b/csharp/ql/test/library-tests/regressions/{}.cs @@ -0,0 +1 @@ +class CheckFileNameEscaping { } \ No newline at end of file diff --git a/csharp/ql/test/library-tests/standalone/controlflow/cfg.ql b/csharp/ql/test/library-tests/standalone/controlflow/cfg.ql index 88a0a00c632..4936dc9b4cd 100644 --- a/csharp/ql/test/library-tests/standalone/controlflow/cfg.ql +++ b/csharp/ql/test/library-tests/standalone/controlflow/cfg.ql @@ -17,4 +17,6 @@ class UnknownLocalVariableDeclExpr extends LocalVariableDeclAndInitExpr { override string toString() { result = "(unknown type) " + this.getName() } } -query predicate edges(ControlFlow::Node n1, ControlFlow::Node n2) { n2 = n1.getASuccessor() } +query predicate edges(ControlFlow::Node n1, ControlFlow::Node n2) { + not n1.getElement().fromLibrary() and n2 = n1.getASuccessor() +} diff --git a/csharp/ql/test/library-tests/standalone/errorrecovery/DiagnosticsAndErrors.expected b/csharp/ql/test/library-tests/standalone/errorrecovery/DiagnosticsAndErrors.expected index ff6d7e236a4..917d625c10c 100644 --- a/csharp/ql/test/library-tests/standalone/errorrecovery/DiagnosticsAndErrors.expected +++ b/csharp/ql/test/library-tests/standalone/errorrecovery/DiagnosticsAndErrors.expected @@ -1,2 +1,13 @@ compilationMessages extractorMessages +| errors.cs:8:1:8:22 | Namespace not found | +| errors.cs:24:31:24:32 | Failed to determine type | +| errors.cs:24:31:24:40 | Failed to determine type | +| errors.cs:24:31:24:40 | Unable to resolve target for call. (Compilation error?) | +| errors.cs:24:38:24:39 | Failed to determine type | +| errors.cs:57:20:57:20 | Failed to determine type | +| errors.cs:93:45:93:45 | Failed to determine type | +| errors.cs:93:45:93:45 | Failed to resolve name | +| errors.cs:94:45:94:45 | Failed to determine type | +| errors.cs:94:45:94:45 | Failed to resolve name | +| file://:0:0:0:0 | Extracting default argument value 'object RecordNumber = default' instead of 'object RecordNumber = -1'. The latter is not supported in C#. | diff --git a/csharp/ql/test/library-tests/tuples/tuple.cs b/csharp/ql/test/library-tests/tuples/tuple.cs new file mode 100644 index 00000000000..7f6719d127a --- /dev/null +++ b/csharp/ql/test/library-tests/tuples/tuple.cs @@ -0,0 +1,21 @@ +using System; + +public class Program +{ + public static void Main() + { + var x = (1, 2); + Console.WriteLine(x.GetType()); + x = new ValueTuple(1, 2); + Console.WriteLine(x.GetType()); + + var y = (1, 2, 3, 4, 5, 6, 7); + Console.WriteLine(y.GetType()); + + var z = (1, 2, 3, 4, 5, 6, 7, 8); + Console.WriteLine(z.GetType()); + + var w = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + Console.WriteLine(w.GetType()); + } +} \ No newline at end of file diff --git a/csharp/ql/test/library-tests/tuples/tuples.expected b/csharp/ql/test/library-tests/tuples/tuples.expected new file mode 100644 index 00000000000..d5ae6b741c2 --- /dev/null +++ b/csharp/ql/test/library-tests/tuples/tuples.expected @@ -0,0 +1,91 @@ +members1 +members2 +| tuple.cs:7:17:7:22 | (Int32,Int32) | ValueTuple | CompareTo((int, int)) | +| tuple.cs:7:17:7:22 | (Int32,Int32) | ValueTuple | CompareTo(object) | +| tuple.cs:7:17:7:22 | (Int32,Int32) | ValueTuple | CompareTo(object, IComparer) | +| tuple.cs:7:17:7:22 | (Int32,Int32) | ValueTuple | Equals((int, int)) | +| tuple.cs:7:17:7:22 | (Int32,Int32) | ValueTuple | Equals(object) | +| tuple.cs:7:17:7:22 | (Int32,Int32) | ValueTuple | Equals(object, IEqualityComparer) | +| tuple.cs:7:17:7:22 | (Int32,Int32) | ValueTuple | GetHashCode() | +| tuple.cs:7:17:7:22 | (Int32,Int32) | ValueTuple | GetHashCode(IEqualityComparer) | +| tuple.cs:7:17:7:22 | (Int32,Int32) | ValueTuple | GetHashCodeCore(IEqualityComparer) | +| tuple.cs:7:17:7:22 | (Int32,Int32) | ValueTuple | Item1 | +| tuple.cs:7:17:7:22 | (Int32,Int32) | ValueTuple | Item2 | +| tuple.cs:7:17:7:22 | (Int32,Int32) | ValueTuple | Item[int] | +| tuple.cs:7:17:7:22 | (Int32,Int32) | ValueTuple | Length | +| tuple.cs:7:17:7:22 | (Int32,Int32) | ValueTuple | ToString() | +| tuple.cs:7:17:7:22 | (Int32,Int32) | ValueTuple | ToStringEnd() | +| tuple.cs:7:17:7:22 | (Int32,Int32) | ValueTuple | ValueTuple() | +| tuple.cs:7:17:7:22 | (Int32,Int32) | ValueTuple | ValueTuple(int, int) | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | CompareTo((int, int, int, int, int, int, int)) | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | CompareTo(object) | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | CompareTo(object, IComparer) | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Equals((int, int, int, int, int, int, int)) | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Equals(object) | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Equals(object, IEqualityComparer) | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | GetHashCode() | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | GetHashCode(IEqualityComparer) | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | GetHashCodeCore(IEqualityComparer) | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item1 | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item2 | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item3 | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item4 | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item5 | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item6 | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item7 | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item[int] | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Length | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | ToString() | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | ToStringEnd() | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | ValueTuple() | +| tuple.cs:12:17:12:37 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | ValueTuple(int, int, int, int, int, int, int) | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | CompareTo((int, int, int, int, int, int, int, int)) | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | CompareTo(object) | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | CompareTo(object, IComparer) | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Equals((int, int, int, int, int, int, int, int)) | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Equals(object) | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Equals(object, IEqualityComparer) | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | GetHashCode() | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | GetHashCode(IEqualityComparer) | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | GetHashCodeCore(IEqualityComparer) | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item1 | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item2 | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item3 | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item4 | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item5 | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item6 | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item7 | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item8 | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item[int] | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Length | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Rest | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | ToString() | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | ToStringEnd() | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | ValueTuple() | +| tuple.cs:15:17:15:40 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | ValueTuple(int, int, int, int, int, int, int, (int)) | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | CompareTo((int, int, int, int, int, int, int, int, int, int)) | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | CompareTo(object) | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | CompareTo(object, IComparer) | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Equals((int, int, int, int, int, int, int, int, int, int)) | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Equals(object) | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Equals(object, IEqualityComparer) | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | GetHashCode() | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | GetHashCode(IEqualityComparer) | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | GetHashCodeCore(IEqualityComparer) | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item1 | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item2 | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item3 | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item4 | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item5 | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item6 | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item7 | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item8 | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item9 | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item10 | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Item[int] | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Length | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | Rest | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | ToString() | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | ToStringEnd() | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | ValueTuple() | +| tuple.cs:18:17:18:47 | (Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32,Int32) | ValueTuple | ValueTuple(int, int, int, int, int, int, int, (int, int, int)) | diff --git a/csharp/ql/test/library-tests/tuples/tuples.ql b/csharp/ql/test/library-tests/tuples/tuples.ql new file mode 100644 index 00000000000..29e26815d1a --- /dev/null +++ b/csharp/ql/test/library-tests/tuples/tuples.ql @@ -0,0 +1,12 @@ +import csharp + +query predicate members1(TupleType t, string m) { + t.fromSource() and + m = t.getAMember().toStringWithTypes() +} + +query predicate members2(TupleType t, string s, string m) { + t.fromSource() and + s = t.getUnderlyingType().toStringWithTypes() and + m = t.getUnderlyingType().getAMember().toStringWithTypes() +} diff --git a/csharp/ql/test/library-tests/unsafe/unsafe4.ql b/csharp/ql/test/library-tests/unsafe/unsafe4.ql index 8aa38fc8fbb..c086e0c00a5 100644 --- a/csharp/ql/test/library-tests/unsafe/unsafe4.ql +++ b/csharp/ql/test/library-tests/unsafe/unsafe4.ql @@ -1,3 +1,3 @@ import csharp -select any(Modifiable m | m.isUnsafe()) +select any(Modifiable m | m.isUnsafe() and m.fromSource()) diff --git a/csharp/ql/test/query-tests/Metrics/Summaries/LinesOfCode.expected b/csharp/ql/test/query-tests/Metrics/Summaries/LinesOfCode.expected new file mode 100644 index 00000000000..b5a514b9ffa --- /dev/null +++ b/csharp/ql/test/query-tests/Metrics/Summaries/LinesOfCode.expected @@ -0,0 +1 @@ +| 4 | diff --git a/csharp/ql/test/query-tests/Metrics/Summaries/LinesOfCode.qlref b/csharp/ql/test/query-tests/Metrics/Summaries/LinesOfCode.qlref new file mode 100644 index 00000000000..8c18065043f --- /dev/null +++ b/csharp/ql/test/query-tests/Metrics/Summaries/LinesOfCode.qlref @@ -0,0 +1 @@ +Metrics/Summaries/LinesOfCode.ql \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Metrics/Summaries/file1.cs b/csharp/ql/test/query-tests/Metrics/Summaries/file1.cs new file mode 100644 index 00000000000..0c12535ee09 --- /dev/null +++ b/csharp/ql/test/query-tests/Metrics/Summaries/file1.cs @@ -0,0 +1,14 @@ + +class C1 +{ + /* + int M() + { + return 0; + } + */ + + // int M() => 0; + + int M() => 0; // Comment +} diff --git a/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjection.cs b/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjection.cs index 09a01281f79..f3593096e1e 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjection.cs +++ b/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjection.cs @@ -1,4 +1,4 @@ -// semmle-extractor-options: /r:System.ComponentModel.Primitives.dll /r:System.ComponentModel.TypeConverter.dll /r:System.Data.Common.dll ${testdir}/../../../resources/stubs/EntityFramework.cs ${testdir}/../../../resources/stubs/System.Data.cs ${testdir}/../../../resources/stubs/System.Windows.cs +// semmle-extractor-options: /r:System.ComponentModel.Primitives.dll /r:System.ComponentModel.TypeConverter.dll /r:System.Data.Common.dll ${testdir}/../../../resources/stubs/EntityFramework.cs ${testdir}/../../../resources/stubs/System.Data.cs ${testdir}/../../../resources/stubs/System.Windows.cs ${testdir}/../../../resources/stubs/Dapper.cs /r:System.Linq.Expressions.dll using System; diff --git a/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjection.expected b/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjection.expected index 61dffee741f..2ad651f8c0e 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjection.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjection.expected @@ -5,6 +5,13 @@ edges | SqlInjection.cs:73:33:73:52 | access to property Text : String | SqlInjection.cs:74:56:74:61 | access to local variable query1 | | SqlInjection.cs:73:33:73:52 | access to property Text : String | SqlInjection.cs:75:55:75:60 | access to local variable query1 | | SqlInjection.cs:87:21:87:29 | access to property Text : String | SqlInjection.cs:88:50:88:55 | access to local variable query1 | +| SqlInjectionDapper.cs:20:86:20:94 | access to property Text : String | SqlInjectionDapper.cs:21:55:21:59 | access to local variable query | +| SqlInjectionDapper.cs:29:86:29:94 | access to property Text : String | SqlInjectionDapper.cs:30:66:30:70 | access to local variable query | +| SqlInjectionDapper.cs:38:86:38:94 | access to property Text : String | SqlInjectionDapper.cs:39:63:39:67 | access to local variable query | +| SqlInjectionDapper.cs:47:86:47:94 | access to property Text : String | SqlInjectionDapper.cs:49:47:49:51 | access to local variable query | +| SqlInjectionDapper.cs:57:86:57:94 | access to property Text : String | SqlInjectionDapper.cs:58:42:58:46 | access to local variable query | +| SqlInjectionDapper.cs:66:86:66:94 | access to property Text : String | SqlInjectionDapper.cs:67:42:67:46 | access to local variable query | +| SqlInjectionDapper.cs:75:86:75:94 | access to property Text : String | SqlInjectionDapper.cs:77:52:77:56 | access to local variable query | nodes | SqlInjection.cs:38:21:38:35 | access to field categoryTextBox : TextBox | semmle.label | access to field categoryTextBox : TextBox | | SqlInjection.cs:38:21:38:40 | access to property Text : String | semmle.label | access to property Text : String | @@ -15,8 +22,29 @@ nodes | SqlInjection.cs:75:55:75:60 | access to local variable query1 | semmle.label | access to local variable query1 | | SqlInjection.cs:87:21:87:29 | access to property Text : String | semmle.label | access to property Text : String | | SqlInjection.cs:88:50:88:55 | access to local variable query1 | semmle.label | access to local variable query1 | +| SqlInjectionDapper.cs:20:86:20:94 | access to property Text : String | semmle.label | access to property Text : String | +| SqlInjectionDapper.cs:21:55:21:59 | access to local variable query | semmle.label | access to local variable query | +| SqlInjectionDapper.cs:29:86:29:94 | access to property Text : String | semmle.label | access to property Text : String | +| SqlInjectionDapper.cs:30:66:30:70 | access to local variable query | semmle.label | access to local variable query | +| SqlInjectionDapper.cs:38:86:38:94 | access to property Text : String | semmle.label | access to property Text : String | +| SqlInjectionDapper.cs:39:63:39:67 | access to local variable query | semmle.label | access to local variable query | +| SqlInjectionDapper.cs:47:86:47:94 | access to property Text : String | semmle.label | access to property Text : String | +| SqlInjectionDapper.cs:49:47:49:51 | access to local variable query | semmle.label | access to local variable query | +| SqlInjectionDapper.cs:57:86:57:94 | access to property Text : String | semmle.label | access to property Text : String | +| SqlInjectionDapper.cs:58:42:58:46 | access to local variable query | semmle.label | access to local variable query | +| SqlInjectionDapper.cs:66:86:66:94 | access to property Text : String | semmle.label | access to property Text : String | +| SqlInjectionDapper.cs:67:42:67:46 | access to local variable query | semmle.label | access to local variable query | +| SqlInjectionDapper.cs:75:86:75:94 | access to property Text : String | semmle.label | access to property Text : String | +| SqlInjectionDapper.cs:77:52:77:56 | access to local variable query | semmle.label | access to local variable query | #select | SqlInjection.cs:39:50:39:55 | access to local variable query1 | SqlInjection.cs:38:21:38:35 | access to field categoryTextBox : TextBox | SqlInjection.cs:39:50:39:55 | access to local variable query1 | Query might include code from $@. | SqlInjection.cs:38:21:38:35 | access to field categoryTextBox : TextBox | this ASP.NET user input | | SqlInjection.cs:74:56:74:61 | access to local variable query1 | SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | SqlInjection.cs:74:56:74:61 | access to local variable query1 | Query might include code from $@. | SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | this ASP.NET user input | | SqlInjection.cs:75:55:75:60 | access to local variable query1 | SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | SqlInjection.cs:75:55:75:60 | access to local variable query1 | Query might include code from $@. | SqlInjection.cs:73:33:73:47 | access to field categoryTextBox : TextBox | this ASP.NET user input | | SqlInjection.cs:88:50:88:55 | access to local variable query1 | SqlInjection.cs:87:21:87:29 | access to property Text : String | SqlInjection.cs:88:50:88:55 | access to local variable query1 | Query might include code from $@. | SqlInjection.cs:87:21:87:29 | access to property Text : String | this TextBox text | +| SqlInjectionDapper.cs:21:55:21:59 | access to local variable query | SqlInjectionDapper.cs:20:86:20:94 | access to property Text : String | SqlInjectionDapper.cs:21:55:21:59 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:20:86:20:94 | access to property Text : String | this TextBox text | +| SqlInjectionDapper.cs:30:66:30:70 | access to local variable query | SqlInjectionDapper.cs:29:86:29:94 | access to property Text : String | SqlInjectionDapper.cs:30:66:30:70 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:29:86:29:94 | access to property Text : String | this TextBox text | +| SqlInjectionDapper.cs:39:63:39:67 | access to local variable query | SqlInjectionDapper.cs:38:86:38:94 | access to property Text : String | SqlInjectionDapper.cs:39:63:39:67 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:38:86:38:94 | access to property Text : String | this TextBox text | +| SqlInjectionDapper.cs:49:47:49:51 | access to local variable query | SqlInjectionDapper.cs:47:86:47:94 | access to property Text : String | SqlInjectionDapper.cs:49:47:49:51 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:47:86:47:94 | access to property Text : String | this TextBox text | +| SqlInjectionDapper.cs:58:42:58:46 | access to local variable query | SqlInjectionDapper.cs:57:86:57:94 | access to property Text : String | SqlInjectionDapper.cs:58:42:58:46 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:57:86:57:94 | access to property Text : String | this TextBox text | +| SqlInjectionDapper.cs:67:42:67:46 | access to local variable query | SqlInjectionDapper.cs:66:86:66:94 | access to property Text : String | SqlInjectionDapper.cs:67:42:67:46 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:66:86:66:94 | access to property Text : String | this TextBox text | +| SqlInjectionDapper.cs:77:52:77:56 | access to local variable query | SqlInjectionDapper.cs:75:86:75:94 | access to property Text : String | SqlInjectionDapper.cs:77:52:77:56 | access to local variable query | Query might include code from $@. | SqlInjectionDapper.cs:75:86:75:94 | access to property Text : String | this TextBox text | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjectionDapper.cs b/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjectionDapper.cs new file mode 100644 index 00000000000..ec54c70ddeb --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-089/SqlInjectionDapper.cs @@ -0,0 +1,95 @@ +using System; + +namespace Test +{ + using System.Data; + using System.Data.Entity; + using System.Data.SqlClient; + using System.Web.UI.WebControls; + using System.Threading.Tasks; + using Dapper; + + class SqlInjectionDapper + { + string connectionString; + + public void Bad01() + { + using (var connection = new SqlConnection(connectionString)) + { + var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE"; + var result = connection.Query(query); + } + } + + public async Task Bad02() + { + using (var connection = new SqlConnection(connectionString)) + { + var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE"; + var result = await connection.QueryAsync(query); + } + } + + public async Task Bad03() + { + using (var connection = new SqlConnection(connectionString)) + { + var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE"; + var result = await connection.QueryFirstAsync(query); + } + } + + public async Task Bad04() + { + using (var connection = new SqlConnection(connectionString)) + { + var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE"; + + await connection.ExecuteAsync(query); + } + } + + public void Bad05() + { + using (var connection = new SqlConnection(connectionString)) + { + var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE"; + connection.ExecuteScalar(query); + } + } + + public void Bad06() + { + using (var connection = new SqlConnection(connectionString)) + { + var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE"; + connection.ExecuteReader(query); + } + } + + public async Task Bad07() + { + using (var connection = new SqlConnection(connectionString)) + { + var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE"; + + var comDef = new CommandDefinition(query); + var result = await connection.QueryFirstAsync(comDef); + } + } + + public async Task Ok07() + { + using (var connection = new SqlConnection(connectionString)) + { + var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" + box1.Text + "' ORDER BY PRICE"; + + var comDef = new CommandDefinition(query); + // no call to any query method + } + } + + System.Windows.Forms.TextBox box1; + } +} diff --git a/csharp/ql/test/query-tests/Security Features/CWE-134/ConsoleUncontrolledFormatString.cs b/csharp/ql/test/query-tests/Security Features/CWE-134/ConsoleUncontrolledFormatString.cs new file mode 100644 index 00000000000..c9d4440cf78 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-134/ConsoleUncontrolledFormatString.cs @@ -0,0 +1,13 @@ +using System; +using System; + +public class Program +{ + public static void Main() + { + var format = Console.ReadLine(); + + // BAD: Uncontrolled format string. + var x = string.Format(format, 1, 2); + } +} diff --git a/csharp/ql/test/query-tests/Security Features/CWE-134/UncontrolledFormatString.expected b/csharp/ql/test/query-tests/Security Features/CWE-134/UncontrolledFormatString.expected index bc87c96b194..4923cd34e70 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-134/UncontrolledFormatString.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-134/UncontrolledFormatString.expected @@ -1,8 +1,11 @@ edges +| ConsoleUncontrolledFormatString.cs:8:22:8:39 | call to method ReadLine : String | ConsoleUncontrolledFormatString.cs:11:31:11:36 | access to local variable format | | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString : NameValueCollection | UncontrolledFormatString.cs:14:23:14:26 | access to local variable path | | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString : NameValueCollection | UncontrolledFormatString.cs:17:46:17:49 | access to local variable path | | UncontrolledFormatStringBad.cs:9:25:9:47 | access to property QueryString : NameValueCollection | UncontrolledFormatStringBad.cs:12:39:12:44 | access to local variable format | nodes +| ConsoleUncontrolledFormatString.cs:8:22:8:39 | call to method ReadLine : String | semmle.label | call to method ReadLine : String | +| ConsoleUncontrolledFormatString.cs:11:31:11:36 | access to local variable format | semmle.label | access to local variable format | | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString : NameValueCollection | semmle.label | access to property QueryString : NameValueCollection | | UncontrolledFormatString.cs:14:23:14:26 | access to local variable path | semmle.label | access to local variable path | | UncontrolledFormatString.cs:17:46:17:49 | access to local variable path | semmle.label | access to local variable path | @@ -10,6 +13,7 @@ nodes | UncontrolledFormatStringBad.cs:9:25:9:47 | access to property QueryString : NameValueCollection | semmle.label | access to property QueryString : NameValueCollection | | UncontrolledFormatStringBad.cs:12:39:12:44 | access to local variable format | semmle.label | access to local variable format | #select +| ConsoleUncontrolledFormatString.cs:11:31:11:36 | access to local variable format | ConsoleUncontrolledFormatString.cs:8:22:8:39 | call to method ReadLine : String | ConsoleUncontrolledFormatString.cs:11:31:11:36 | access to local variable format | $@ flows to here and is used as a format string. | ConsoleUncontrolledFormatString.cs:8:22:8:39 | call to method ReadLine | call to method ReadLine | | UncontrolledFormatString.cs:14:23:14:26 | access to local variable path | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString : NameValueCollection | UncontrolledFormatString.cs:14:23:14:26 | access to local variable path | $@ flows to here and is used as a format string. | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString | access to property QueryString | | UncontrolledFormatString.cs:17:46:17:49 | access to local variable path | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString : NameValueCollection | UncontrolledFormatString.cs:17:46:17:49 | access to local variable path | $@ flows to here and is used as a format string. | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString | access to property QueryString | | UncontrolledFormatString.cs:34:23:34:31 | access to property Text | UncontrolledFormatString.cs:34:23:34:31 | access to property Text | UncontrolledFormatString.cs:34:23:34:31 | access to property Text | $@ flows to here and is used as a format string. | UncontrolledFormatString.cs:34:23:34:31 | access to property Text | access to property Text | diff --git a/csharp/ql/test/query-tests/Stubs/MinimalStubsFromSource.expected b/csharp/ql/test/query-tests/Stubs/MinimalStubsFromSource.expected index d2dd3037e3c..8159a38c348 100644 --- a/csharp/ql/test/query-tests/Stubs/MinimalStubsFromSource.expected +++ b/csharp/ql/test/query-tests/Stubs/MinimalStubsFromSource.expected @@ -1 +1 @@ -| // This file contains auto-generated code.\n// original-extractor-options: /r:System.Text.RegularExpressions.dll /r:System.Collections.Specialized.dll /r:System.Net.dll /r:System.Web.dll /r:System.Net.HttpListener.dll /r:System.Collections.Specialized.dll /r:System.Private.Uri.dll /r:System.Runtime.Extensions.dll /r:System.Linq.Parallel.dll /r:System.Collections.Concurrent.dll /r:System.Linq.Expressions.dll /r:System.Collections.dll /r:System.Linq.Queryable.dll /r:System.Linq.dll /r:System.Collections.NonGeneric.dll /r:System.ObjectModel.dll /r:System.ComponentModel.TypeConverter.dll /r:System.IO.Compression.dll /r:System.IO.Pipes.dll /r:System.Net.Primitives.dll /r:System.Net.Security.dll /r:System.Security.Cryptography.Primitives.dll /r:System.Text.RegularExpressions.dll ${testdir}/../../resources/stubs/System.Web.cs /r:System.Runtime.Serialization.Primitives.dll\n\nnamespace System\n{\n// Generated from `System.Uri` in `System.Private.Uri, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Uri : System.Runtime.Serialization.ISerializable\n{\n public override bool Equals(object comparand) => throw null;\n public override int GetHashCode() => throw null;\n public override string ToString() => throw null;\n void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) => throw null;\n}\n\nnamespace Collections\n{\n// Generated from `System.Collections.Queue` in `System.Collections.NonGeneric, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Queue : System.ICloneable, System.Collections.IEnumerable, System.Collections.ICollection\n{\n public virtual System.Collections.IEnumerator GetEnumerator() => throw null;\n public virtual bool IsSynchronized { get => throw null; }\n public virtual int Count { get => throw null; }\n public virtual object Clone() => throw null;\n public virtual object SyncRoot { get => throw null; }\n public virtual void CopyTo(System.Array array, int index) => throw null;\n}\n\n// Generated from `System.Collections.SortedList` in `System.Collections.NonGeneric, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class SortedList : System.ICloneable, System.Collections.IEnumerable, System.Collections.IDictionary, System.Collections.ICollection\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public virtual System.Collections.ICollection Keys { get => throw null; }\n public virtual System.Collections.ICollection Values { get => throw null; }\n public virtual System.Collections.IDictionaryEnumerator GetEnumerator() => throw null;\n public virtual bool Contains(object key) => throw null;\n public virtual bool IsFixedSize { get => throw null; }\n public virtual bool IsReadOnly { get => throw null; }\n public virtual bool IsSynchronized { get => throw null; }\n public virtual int Count { get => throw null; }\n public virtual object Clone() => throw null;\n public virtual object GetByIndex(int index) => throw null;\n public virtual object SyncRoot { get => throw null; }\n public virtual object this[object key] { get => throw null; set => throw null; }\n public virtual void Add(object key, object value) => throw null;\n public virtual void Clear() => throw null;\n public virtual void CopyTo(System.Array array, int arrayIndex) => throw null;\n public virtual void Remove(object key) => throw null;\n}\n\n// Generated from `System.Collections.Stack` in `System.Collections.NonGeneric, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Stack : System.ICloneable, System.Collections.IEnumerable, System.Collections.ICollection\n{\n public virtual System.Collections.IEnumerator GetEnumerator() => throw null;\n public virtual bool IsSynchronized { get => throw null; }\n public virtual int Count { get => throw null; }\n public virtual object Clone() => throw null;\n public virtual object SyncRoot { get => throw null; }\n public virtual void CopyTo(System.Array array, int index) => throw null;\n}\n\nnamespace Concurrent\n{\n// Generated from `System.Collections.Concurrent.BlockingCollection<>` in `System.Collections.Concurrent, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class BlockingCollection : System.IDisposable, System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n public int Count { get => throw null; }\n public void Dispose() => throw null;\n void System.Collections.ICollection.CopyTo(System.Array array, int index) => throw null;\n}\n\n// Generated from `System.Collections.Concurrent.BlockingCollectionDebugView<>` in `System.Collections.Concurrent, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass BlockingCollectionDebugView\n{\n}\n\n// Generated from `System.Collections.Concurrent.IDictionaryDebugView<,>` in `System.Collections.Concurrent, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass IDictionaryDebugView\n{\n}\n\n}\nnamespace Generic\n{\n// Generated from `System.Collections.Generic.CollectionDebugView<>` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass CollectionDebugView\n{\n}\n\n// Generated from `System.Collections.Generic.DictionaryDebugView<,>` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass DictionaryDebugView\n{\n}\n\n// Generated from `System.Collections.Generic.QueueDebugView<>` in `System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass QueueDebugView\n{\n}\n\n// Generated from `System.Collections.Generic.SortedSet<>` in `System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class SortedSet : System.Runtime.Serialization.ISerializable, System.Runtime.Serialization.IDeserializationCallback, System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.ISet, System.Collections.Generic.IReadOnlySet, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.Generic.ICollection.IsReadOnly { get => throw null; }\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n public bool Add(T item) => throw null;\n public bool IsProperSubsetOf(System.Collections.Generic.IEnumerable other) => throw null;\n public bool IsProperSupersetOf(System.Collections.Generic.IEnumerable other) => throw null;\n public bool IsSubsetOf(System.Collections.Generic.IEnumerable other) => throw null;\n public bool IsSupersetOf(System.Collections.Generic.IEnumerable other) => throw null;\n public bool Overlaps(System.Collections.Generic.IEnumerable other) => throw null;\n public bool Remove(T item) => throw null;\n public bool SetEquals(System.Collections.Generic.IEnumerable other) => throw null;\n public int Count { get => throw null; }\n public virtual bool Contains(T item) => throw null;\n public virtual void Clear() => throw null;\n public virtual void IntersectWith(System.Collections.Generic.IEnumerable other) => throw null;\n public void CopyTo(T[] array, int index) => throw null;\n public void ExceptWith(System.Collections.Generic.IEnumerable other) => throw null;\n public void SymmetricExceptWith(System.Collections.Generic.IEnumerable other) => throw null;\n public void UnionWith(System.Collections.Generic.IEnumerable other) => throw null;\n void System.Collections.Generic.ICollection.Add(T item) => throw null;\n void System.Collections.ICollection.CopyTo(System.Array array, int index) => throw null;\n void System.Runtime.Serialization.IDeserializationCallback.OnDeserialization(object sender) => throw null;\n void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) => throw null;\n}\n\n// Generated from `System.Collections.Generic.Stack<>` in `System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Stack : System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n public T Peek() => throw null;\n public int Count { get => throw null; }\n void System.Collections.ICollection.CopyTo(System.Array array, int arrayIndex) => throw null;\n}\n\n// Generated from `System.Collections.Generic.StackDebugView<>` in `System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass StackDebugView\n{\n}\n\n}\nnamespace Specialized\n{\n// Generated from `System.Collections.Specialized.NameObjectCollectionBase` in `System.Collections.Specialized, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class NameObjectCollectionBase : System.Runtime.Serialization.ISerializable, System.Runtime.Serialization.IDeserializationCallback, System.Collections.IEnumerable, System.Collections.ICollection\n{\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n public virtual System.Collections.IEnumerator GetEnumerator() => throw null;\n public virtual int Count { get => throw null; }\n public virtual void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) => throw null;\n public virtual void OnDeserialization(object sender) => throw null;\n void System.Collections.ICollection.CopyTo(System.Array array, int index) => throw null;\n}\n\n// Generated from `System.Collections.Specialized.NameValueCollection` in `System.Collections.Specialized, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class NameValueCollection : System.Collections.Specialized.NameObjectCollectionBase\n{\n public string this[string name] { get => throw null; set => throw null; }\n}\n\n}\n}\nnamespace ComponentModel\n{\n// Generated from `System.ComponentModel.ComponentConverter` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ComponentConverter : System.ComponentModel.ReferenceConverter\n{\n}\n\n// Generated from `System.ComponentModel.DefaultEventAttribute` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DefaultEventAttribute : System.Attribute\n{\n public DefaultEventAttribute(string name) => throw null;\n public override bool Equals(object obj) => throw null;\n public override int GetHashCode() => throw null;\n}\n\n// Generated from `System.ComponentModel.DefaultPropertyAttribute` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DefaultPropertyAttribute : System.Attribute\n{\n public DefaultPropertyAttribute(string name) => throw null;\n public override bool Equals(object obj) => throw null;\n public override int GetHashCode() => throw null;\n}\n\n// Generated from `System.ComponentModel.INotifyPropertyChanged` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface INotifyPropertyChanged\n{\n}\n\n// Generated from `System.ComponentModel.ReferenceConverter` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ReferenceConverter : System.ComponentModel.TypeConverter\n{\n}\n\n// Generated from `System.ComponentModel.TypeConverterAttribute` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class TypeConverterAttribute : System.Attribute\n{\n public TypeConverterAttribute() => throw null;\n public TypeConverterAttribute(System.Type type) => throw null;\n public TypeConverterAttribute(string typeName) => throw null;\n public override bool Equals(object obj) => throw null;\n public override int GetHashCode() => throw null;\n}\n\n// Generated from `System.ComponentModel.TypeConverter` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class TypeConverter\n{\n}\n\n// Generated from `System.ComponentModel.TypeDescriptionProviderAttribute` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class TypeDescriptionProviderAttribute : System.Attribute\n{\n public TypeDescriptionProviderAttribute(System.Type type) => throw null;\n public TypeDescriptionProviderAttribute(string typeName) => throw null;\n}\n\n// Generated from `System.ComponentModel.TypeDescriptionProvider` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class TypeDescriptionProvider\n{\n}\n\n// Generated from `System.ComponentModel.TypeDescriptor` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class TypeDescriptor\n{\n}\n\nnamespace Design\n{\n// Generated from `System.ComponentModel.Design.DesignerOptionService` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class DesignerOptionService : System.ComponentModel.Design.IDesignerOptionService\n{\n}\n\n// Generated from `System.ComponentModel.Design.IDesignerOptionService` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IDesignerOptionService\n{\n}\n\n}\n}\nnamespace Dynamic\n{\n// Generated from `System.Dynamic.DynamicMetaObject` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DynamicMetaObject\n{\n}\n\n// Generated from `System.Dynamic.ExpandoObject` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ExpandoObject : System.Dynamic.IDynamicMetaObjectProvider, System.ComponentModel.INotifyPropertyChanged, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable>, System.Collections.Generic.IDictionary, System.Collections.Generic.ICollection>\n{\n System.Collections.Generic.ICollection System.Collections.Generic.IDictionary.Values { get => throw null; }\n System.Collections.Generic.ICollection System.Collections.Generic.IDictionary.Keys { get => throw null; }\n System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.Generic.ICollection>.Contains(System.Collections.Generic.KeyValuePair item) => throw null;\n bool System.Collections.Generic.ICollection>.IsReadOnly { get => throw null; }\n bool System.Collections.Generic.ICollection>.Remove(System.Collections.Generic.KeyValuePair item) => throw null;\n bool System.Collections.Generic.IDictionary.ContainsKey(string key) => throw null;\n bool System.Collections.Generic.IDictionary.Remove(string key) => throw null;\n bool System.Collections.Generic.IDictionary.TryGetValue(string key, out object value) => throw null;\n int System.Collections.Generic.ICollection>.Count { get => throw null; }\n object this[string key] { get => throw null; set => throw null; }\n void System.Collections.Generic.ICollection>.Add(System.Collections.Generic.KeyValuePair item) => throw null;\n void System.Collections.Generic.ICollection>.Clear() => throw null;\n void System.Collections.Generic.ICollection>.CopyTo(System.Collections.Generic.KeyValuePair[] array, int arrayIndex) => throw null;\n void System.Collections.Generic.IDictionary.Add(string key, object value) => throw null;\n}\n\n// Generated from `System.Dynamic.IDynamicMetaObjectProvider` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IDynamicMetaObjectProvider\n{\n}\n\nnamespace Utils\n{\n// Generated from `System.Dynamic.Utils.ListProvider<>` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract class ListProvider : System.Collections.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection where T: class\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n public T this[int index] { get => throw null; set => throw null; }\n public bool Contains(T item) => throw null;\n public bool IsReadOnly { get => throw null; }\n public bool Remove(T item) => throw null;\n public int Count { get => throw null; }\n public int IndexOf(T item) => throw null;\n public void Add(T item) => throw null;\n public void Clear() => throw null;\n public void CopyTo(T[] array, int index) => throw null;\n public void Insert(int index, T item) => throw null;\n public void RemoveAt(int index) => throw null;\n}\n\n}\n}\nnamespace IO\n{\nnamespace Compression\n{\n// Generated from `System.IO.Compression.DeflateStream` in `System.IO.Compression, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089`\npublic class DeflateStream : System.IO.Stream\n{\n protected override void Dispose(bool disposing) => throw null;\n public override System.IAsyncResult BeginRead(System.Byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.IAsyncResult BeginWrite(System.Byte[] array, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.Int64 Length { get => throw null; }\n public override System.Int64 Position { get => throw null; set => throw null; }\n public override System.Int64 Seek(System.Int64 offset, System.IO.SeekOrigin origin) => throw null;\n public override System.Threading.Tasks.Task CopyToAsync(System.IO.Stream destination, int bufferSize, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task WriteAsync(System.Byte[] array, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task ReadAsync(System.Byte[] array, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask DisposeAsync() => throw null;\n public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken) => throw null;\n public override bool CanRead { get => throw null; }\n public override bool CanSeek { get => throw null; }\n public override bool CanWrite { get => throw null; }\n public override int EndRead(System.IAsyncResult asyncResult) => throw null;\n public override int Read(System.Byte[] array, int offset, int count) => throw null;\n public override int Read(System.Span buffer) => throw null;\n public override int ReadByte() => throw null;\n public override void CopyTo(System.IO.Stream destination, int bufferSize) => throw null;\n public override void EndWrite(System.IAsyncResult asyncResult) => throw null;\n public override void Flush() => throw null;\n public override void SetLength(System.Int64 value) => throw null;\n public override void Write(System.Byte[] array, int offset, int count) => throw null;\n public override void Write(System.ReadOnlySpan buffer) => throw null;\n}\n\n}\n}\nnamespace Linq\n{\n// Generated from `System.Linq.Enumerable` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic public class Enumerable\n{\n public static System.Collections.Generic.IEnumerable Select(this System.Collections.Generic.IEnumerable source, System.Func selector) => throw null;\n}\n\n// Generated from `System.Linq.Grouping<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Grouping : System.Linq.IGrouping, System.Collections.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n TElement this[int index] { get => throw null; set => throw null; }\n bool System.Collections.Generic.ICollection.Contains(TElement item) => throw null;\n bool System.Collections.Generic.ICollection.IsReadOnly { get => throw null; }\n bool System.Collections.Generic.ICollection.Remove(TElement item) => throw null;\n int System.Collections.Generic.ICollection.Count { get => throw null; }\n int System.Collections.Generic.IList.IndexOf(TElement item) => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n void System.Collections.Generic.ICollection.Add(TElement item) => throw null;\n void System.Collections.Generic.ICollection.Clear() => throw null;\n void System.Collections.Generic.ICollection.CopyTo(TElement[] array, int arrayIndex) => throw null;\n void System.Collections.Generic.IList.Insert(int index, TElement item) => throw null;\n void System.Collections.Generic.IList.RemoveAt(int index) => throw null;\n}\n\n// Generated from `System.Linq.IGrouping<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IGrouping : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n}\n\n// Generated from `System.Linq.IIListProvider<>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\ninterface IIListProvider : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n}\n\n// Generated from `System.Linq.ILookup<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface ILookup : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable>\n{\n}\n\n// Generated from `System.Linq.IOrderedEnumerable<>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IOrderedEnumerable : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n}\n\n// Generated from `System.Linq.IPartition<>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\ninterface IPartition : System.Linq.IIListProvider, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n}\n\n// Generated from `System.Linq.IQueryable` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IQueryable : System.Collections.IEnumerable\n{\n}\n\n// Generated from `System.Linq.Lookup<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Lookup : System.Linq.ILookup, System.Linq.IIListProvider>, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable>\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator> GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.OrderedEnumerable<>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract class OrderedEnumerable : System.Linq.IPartition, System.Linq.IOrderedEnumerable, System.Linq.IIListProvider, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.ParallelEnumerable` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic public class ParallelEnumerable\n{\n public static System.Linq.ParallelQuery AsParallel(this System.Collections.IEnumerable source) => throw null;\n}\n\n// Generated from `System.Linq.ParallelQuery<>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ParallelQuery : System.Linq.ParallelQuery, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n public virtual System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.ParallelQuery` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ParallelQuery : System.Collections.IEnumerable\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.Queryable` in `System.Linq.Queryable, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic public class Queryable\n{\n public static System.Linq.IQueryable AsQueryable(this System.Collections.IEnumerable source) => throw null;\n}\n\n// Generated from `System.Linq.SystemLinq_GroupingDebugView<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass SystemLinq_GroupingDebugView\n{\n}\n\n// Generated from `System.Linq.SystemLinq_LookupDebugView<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass SystemLinq_LookupDebugView\n{\n}\n\nnamespace Expressions\n{\n// Generated from `System.Linq.Expressions.BlockExpressionList` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass BlockExpressionList : System.Collections.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n public System.Linq.Expressions.Expression this[int index] { get => throw null; set => throw null; }\n public bool Contains(System.Linq.Expressions.Expression item) => throw null;\n public bool IsReadOnly { get => throw null; }\n public bool Remove(System.Linq.Expressions.Expression item) => throw null;\n public int Count { get => throw null; }\n public int IndexOf(System.Linq.Expressions.Expression item) => throw null;\n public void Add(System.Linq.Expressions.Expression item) => throw null;\n public void Clear() => throw null;\n public void CopyTo(System.Linq.Expressions.Expression[] array, int index) => throw null;\n public void Insert(int index, System.Linq.Expressions.Expression item) => throw null;\n public void RemoveAt(int index) => throw null;\n}\n\n// Generated from `System.Linq.Expressions.Expression` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class Expression\n{\n public override string ToString() => throw null;\n}\n\n// Generated from `System.Linq.Expressions.ParameterExpression` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ParameterExpression : System.Linq.Expressions.Expression\n{\n}\n\nnamespace Compiler\n{\n// Generated from `System.Linq.Expressions.Compiler.ParameterList` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass ParameterList : System.Collections.IEnumerable, System.Collections.Generic.IReadOnlyList, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n public System.Linq.Expressions.ParameterExpression this[int index] { get => throw null; }\n public int Count { get => throw null; }\n}\n\n}\nnamespace Interpreter\n{\n// Generated from `System.Linq.Expressions.Interpreter.InstructionArray` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstruct InstructionArray\n{\n}\n\n// Generated from `System.Linq.Expressions.Interpreter.InstructionList` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass InstructionList\n{\n}\n\n// Generated from `System.Linq.Expressions.Interpreter.InterpretedFrameInfo` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstruct InterpretedFrameInfo\n{\n public override string ToString() => throw null;\n}\n\n// Generated from `System.Linq.Expressions.Interpreter.InterpretedFrame` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass InterpretedFrame\n{\n}\n\n}\n}\nnamespace Parallel\n{\n// Generated from `System.Linq.Parallel.ListChunk<>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass ListChunk : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.Parallel.Lookup<,>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass Lookup : System.Linq.ILookup, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable>\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator> GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.Parallel.PartitionerQueryOperator<>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass PartitionerQueryOperator : System.Linq.Parallel.QueryOperator\n{\n}\n\n// Generated from `System.Linq.Parallel.QueryOperator<>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract class QueryOperator : System.Linq.ParallelQuery\n{\n public override System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.Parallel.QueryResults<>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract class QueryResults : System.Collections.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.Generic.ICollection.Contains(T item) => throw null;\n bool System.Collections.Generic.ICollection.IsReadOnly { get => throw null; }\n bool System.Collections.Generic.ICollection.Remove(T item) => throw null;\n int System.Collections.Generic.IList.IndexOf(T item) => throw null;\n public T this[int index] { get => throw null; set => throw null; }\n public int Count { get => throw null; }\n void System.Collections.Generic.ICollection.Add(T item) => throw null;\n void System.Collections.Generic.ICollection.Clear() => throw null;\n void System.Collections.Generic.ICollection.CopyTo(T[] array, int arrayIndex) => throw null;\n void System.Collections.Generic.IList.Insert(int index, T item) => throw null;\n void System.Collections.Generic.IList.RemoveAt(int index) => throw null;\n}\n\n// Generated from `System.Linq.Parallel.ZipQueryOperator<,,>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass ZipQueryOperator : System.Linq.Parallel.QueryOperator\n{\n}\n\n}\n}\nnamespace Net\n{\n// Generated from `System.Net.CookieCollection` in `System.Net.Primitives, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class CookieCollection : System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.IEnumerator GetEnumerator() => throw null;\n public bool Contains(System.Net.Cookie cookie) => throw null;\n public bool IsReadOnly { get => throw null; }\n public bool IsSynchronized { get => throw null; }\n public bool Remove(System.Net.Cookie cookie) => throw null;\n public int Count { get => throw null; }\n public object SyncRoot { get => throw null; }\n public void Add(System.Net.Cookie cookie) => throw null;\n public void Clear() => throw null;\n public void CopyTo(System.Array array, int index) => throw null;\n public void CopyTo(System.Net.Cookie[] array, int index) => throw null;\n}\n\n// Generated from `System.Net.Cookie` in `System.Net.Primitives, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Cookie\n{\n public override bool Equals(object comparand) => throw null;\n public override int GetHashCode() => throw null;\n public override string ToString() => throw null;\n}\n\n// Generated from `System.Net.StreamFramer` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass StreamFramer\n{\n}\n\nnamespace Security\n{\n// Generated from `System.Net.Security.AuthenticatedStream` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class AuthenticatedStream : System.IO.Stream\n{\n protected override void Dispose(bool disposing) => throw null;\n public override System.Threading.Tasks.ValueTask DisposeAsync() => throw null;\n}\n\n// Generated from `System.Net.Security.CipherSuitesPolicy` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class CipherSuitesPolicy\n{\n}\n\n// Generated from `System.Net.Security.NegotiateStream` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class NegotiateStream : System.Net.Security.AuthenticatedStream\n{\n protected override void Dispose(bool disposing) => throw null;\n public override System.IAsyncResult BeginRead(System.Byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.IAsyncResult BeginWrite(System.Byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.Int64 Length { get => throw null; }\n public override System.Int64 Position { get => throw null; set => throw null; }\n public override System.Int64 Seek(System.Int64 offset, System.IO.SeekOrigin origin) => throw null;\n public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task WriteAsync(System.Byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task ReadAsync(System.Byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask DisposeAsync() => throw null;\n public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken) => throw null;\n public override bool CanRead { get => throw null; }\n public override bool CanSeek { get => throw null; }\n public override bool CanTimeout { get => throw null; }\n public override bool CanWrite { get => throw null; }\n public override int EndRead(System.IAsyncResult asyncResult) => throw null;\n public override int Read(System.Byte[] buffer, int offset, int count) => throw null;\n public override int ReadTimeout { get => throw null; set => throw null; }\n public override int WriteTimeout { get => throw null; set => throw null; }\n public override void EndWrite(System.IAsyncResult asyncResult) => throw null;\n public override void Flush() => throw null;\n public override void SetLength(System.Int64 value) => throw null;\n public override void Write(System.Byte[] buffer, int offset, int count) => throw null;\n}\n\n// Generated from `System.Net.Security.SslStream` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class SslStream : System.Net.Security.AuthenticatedStream\n{\n protected override void Dispose(bool disposing) => throw null;\n public override System.IAsyncResult BeginRead(System.Byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.IAsyncResult BeginWrite(System.Byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.Int64 Length { get => throw null; }\n public override System.Int64 Position { get => throw null; set => throw null; }\n public override System.Int64 Seek(System.Int64 offset, System.IO.SeekOrigin origin) => throw null;\n public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task WriteAsync(System.Byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task ReadAsync(System.Byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask DisposeAsync() => throw null;\n public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken) => throw null;\n public override bool CanRead { get => throw null; }\n public override bool CanSeek { get => throw null; }\n public override bool CanTimeout { get => throw null; }\n public override bool CanWrite { get => throw null; }\n public override int EndRead(System.IAsyncResult asyncResult) => throw null;\n public override int Read(System.Byte[] buffer, int offset, int count) => throw null;\n public override int ReadByte() => throw null;\n public override int ReadTimeout { get => throw null; set => throw null; }\n public override int WriteTimeout { get => throw null; set => throw null; }\n public override void EndWrite(System.IAsyncResult asyncResult) => throw null;\n public override void Flush() => throw null;\n public override void SetLength(System.Int64 value) => throw null;\n public override void Write(System.Byte[] buffer, int offset, int count) => throw null;\n}\n\n// Generated from `System.Net.Security.TlsCipherSuite` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic enum TlsCipherSuite\n{\n}\n\n}\n}\nnamespace Runtime\n{\nnamespace Serialization\n{\n// Generated from `System.Runtime.Serialization.DataContractAttribute` in `System.Runtime.Serialization.Primitives, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DataContractAttribute : System.Attribute\n{\n public DataContractAttribute() => throw null;\n}\n\n// Generated from `System.Runtime.Serialization.DataMemberAttribute` in `System.Runtime.Serialization.Primitives, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DataMemberAttribute : System.Attribute\n{\n public DataMemberAttribute() => throw null;\n}\n\n}\n}\nnamespace Text\n{\nnamespace RegularExpressions\n{\n// Generated from `System.Text.RegularExpressions.Capture` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Capture\n{\n public override string ToString() => throw null;\n}\n\n// Generated from `System.Text.RegularExpressions.CollectionDebuggerProxy<>` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass CollectionDebuggerProxy\n{\n}\n\n// Generated from `System.Text.RegularExpressions.GroupCollection` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class GroupCollection : System.Collections.IList, System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.IReadOnlyList, System.Collections.Generic.IReadOnlyDictionary, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlyCollection>, System.Collections.Generic.IList, System.Collections.Generic.IEnumerable, System.Collections.Generic.IEnumerable>, System.Collections.Generic.ICollection\n{\n System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() => throw null;\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Text.RegularExpressions.Group this[int index] { get => throw null; set => throw null; }\n bool System.Collections.Generic.ICollection.Contains(System.Text.RegularExpressions.Group item) => throw null;\n bool System.Collections.Generic.ICollection.Remove(System.Text.RegularExpressions.Group item) => throw null;\n bool System.Collections.IList.Contains(object value) => throw null;\n bool System.Collections.IList.IsFixedSize { get => throw null; }\n int System.Collections.Generic.IList.IndexOf(System.Text.RegularExpressions.Group item) => throw null;\n int System.Collections.IList.Add(object value) => throw null;\n int System.Collections.IList.IndexOf(object value) => throw null;\n object this[int index] { get => throw null; set => throw null; }\n public System.Collections.Generic.IEnumerable Values { get => throw null; }\n public System.Collections.Generic.IEnumerable Keys { get => throw null; }\n public System.Collections.IEnumerator GetEnumerator() => throw null;\n public System.Text.RegularExpressions.Group this[string groupname] { get => throw null; }\n public bool ContainsKey(string key) => throw null;\n public bool IsReadOnly { get => throw null; }\n public bool IsSynchronized { get => throw null; }\n public bool TryGetValue(string key, out System.Text.RegularExpressions.Group value) => throw null;\n public int Count { get => throw null; }\n public object SyncRoot { get => throw null; }\n public void CopyTo(System.Array array, int arrayIndex) => throw null;\n public void CopyTo(System.Text.RegularExpressions.Group[] array, int arrayIndex) => throw null;\n void System.Collections.Generic.ICollection.Add(System.Text.RegularExpressions.Group item) => throw null;\n void System.Collections.Generic.ICollection.Clear() => throw null;\n void System.Collections.Generic.IList.Insert(int index, System.Text.RegularExpressions.Group item) => throw null;\n void System.Collections.Generic.IList.RemoveAt(int index) => throw null;\n void System.Collections.IList.Clear() => throw null;\n void System.Collections.IList.Insert(int index, object value) => throw null;\n void System.Collections.IList.Remove(object value) => throw null;\n void System.Collections.IList.RemoveAt(int index) => throw null;\n}\n\n// Generated from `System.Text.RegularExpressions.Group` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Group : System.Text.RegularExpressions.Capture\n{\n}\n\n// Generated from `System.Text.RegularExpressions.Match` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Match : System.Text.RegularExpressions.Group\n{\n}\n\n// Generated from `System.Text.RegularExpressions.RegexOptions` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\n[System.Flags]\npublic enum RegexOptions\n{\n IgnoreCase,\n}\n\n// Generated from `System.Text.RegularExpressions.Regex` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Regex : System.Runtime.Serialization.ISerializable\n{\n public Regex(string pattern) => throw null;\n public Regex(string pattern, System.Text.RegularExpressions.RegexOptions options, System.TimeSpan matchTimeout) => throw null;\n public System.Text.RegularExpressions.Match Match(string input) => throw null;\n public override string ToString() => throw null;\n public static System.Text.RegularExpressions.Match Match(string input, string pattern) => throw null;\n public static System.Text.RegularExpressions.Match Match(string input, string pattern, System.Text.RegularExpressions.RegexOptions options, System.TimeSpan matchTimeout) => throw null;\n public string Replace(string input, string replacement) => throw null;\n void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext context) => throw null;\n}\n\n}\n}\nnamespace Timers\n{\n// Generated from `System.Timers.TimersDescriptionAttribute` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class TimersDescriptionAttribute\n{\n public TimersDescriptionAttribute(string description) => throw null;\n}\n\n}\nnamespace Windows\n{\nnamespace Markup\n{\n// Generated from `System.Windows.Markup.ValueSerializerAttribute` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ValueSerializerAttribute : System.Attribute\n{\n public ValueSerializerAttribute(System.Type valueSerializerType) => throw null;\n public ValueSerializerAttribute(string valueSerializerTypeName) => throw null;\n}\n\n}\n}\n}\n | +| // This file contains auto-generated code.\n// original-extractor-options: /r:System.Text.RegularExpressions.dll /r:System.Collections.Specialized.dll /r:System.Net.dll /r:System.Web.dll /r:System.Net.HttpListener.dll /r:System.Collections.Specialized.dll /r:System.Private.Uri.dll /r:System.Runtime.Extensions.dll /r:System.Linq.Parallel.dll /r:System.Collections.Concurrent.dll /r:System.Linq.Expressions.dll /r:System.Collections.dll /r:System.Linq.Queryable.dll /r:System.Linq.dll /r:System.Collections.NonGeneric.dll /r:System.ObjectModel.dll /r:System.ComponentModel.TypeConverter.dll /r:System.IO.Compression.dll /r:System.IO.Pipes.dll /r:System.Net.Primitives.dll /r:System.Net.Security.dll /r:System.Security.Cryptography.Primitives.dll /r:System.Text.RegularExpressions.dll ${testdir}/../../resources/stubs/System.Web.cs /r:System.Runtime.Serialization.Primitives.dll\n\nnamespace System\n{\n// Generated from `System.Uri` in `System.Private.Uri, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Uri : System.Runtime.Serialization.ISerializable\n{\n public override bool Equals(object comparand) => throw null;\n public override int GetHashCode() => throw null;\n public override string ToString() => throw null;\n void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) => throw null;\n}\n\nnamespace Collections\n{\n// Generated from `System.Collections.Queue` in `System.Collections.NonGeneric, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Queue : System.ICloneable, System.Collections.IEnumerable, System.Collections.ICollection\n{\n public virtual System.Collections.IEnumerator GetEnumerator() => throw null;\n public virtual bool IsSynchronized { get => throw null; }\n public virtual int Count { get => throw null; }\n public virtual object Clone() => throw null;\n public virtual object SyncRoot { get => throw null; }\n public virtual void CopyTo(System.Array array, int index) => throw null;\n}\n\n// Generated from `System.Collections.SortedList` in `System.Collections.NonGeneric, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class SortedList : System.ICloneable, System.Collections.IEnumerable, System.Collections.IDictionary, System.Collections.ICollection\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public virtual System.Collections.ICollection Keys { get => throw null; }\n public virtual System.Collections.ICollection Values { get => throw null; }\n public virtual System.Collections.IDictionaryEnumerator GetEnumerator() => throw null;\n public virtual bool Contains(object key) => throw null;\n public virtual bool IsFixedSize { get => throw null; }\n public virtual bool IsReadOnly { get => throw null; }\n public virtual bool IsSynchronized { get => throw null; }\n public virtual int Count { get => throw null; }\n public virtual object Clone() => throw null;\n public virtual object GetByIndex(int index) => throw null;\n public virtual object SyncRoot { get => throw null; }\n public virtual object this[object key] { get => throw null; set => throw null; }\n public virtual void Add(object key, object value) => throw null;\n public virtual void Clear() => throw null;\n public virtual void CopyTo(System.Array array, int arrayIndex) => throw null;\n public virtual void Remove(object key) => throw null;\n}\n\n// Generated from `System.Collections.Stack` in `System.Collections.NonGeneric, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Stack : System.ICloneable, System.Collections.IEnumerable, System.Collections.ICollection\n{\n public virtual System.Collections.IEnumerator GetEnumerator() => throw null;\n public virtual bool IsSynchronized { get => throw null; }\n public virtual int Count { get => throw null; }\n public virtual object Clone() => throw null;\n public virtual object SyncRoot { get => throw null; }\n public virtual void CopyTo(System.Array array, int index) => throw null;\n}\n\nnamespace Concurrent\n{\n// Generated from `System.Collections.Concurrent.BlockingCollection<>` in `System.Collections.Concurrent, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class BlockingCollection : System.IDisposable, System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n public int Count { get => throw null; }\n public void Dispose() => throw null;\n void System.Collections.ICollection.CopyTo(System.Array array, int index) => throw null;\n}\n\n// Generated from `System.Collections.Concurrent.BlockingCollectionDebugView<>` in `System.Collections.Concurrent, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass BlockingCollectionDebugView\n{\n}\n\n// Generated from `System.Collections.Concurrent.ConcurrentStack<>` in `System.Collections.Concurrent, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ConcurrentStack : System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable, System.Collections.Concurrent.IProducerConsumerCollection\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.Concurrent.IProducerConsumerCollection.TryAdd(T item) => throw null;\n bool System.Collections.Concurrent.IProducerConsumerCollection.TryTake(out T item) => throw null;\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n public T[] ToArray() => throw null;\n public int Count { get => throw null; }\n public void CopyTo(T[] array, int index) => throw null;\n void System.Collections.ICollection.CopyTo(System.Array array, int index) => throw null;\n}\n\n// Generated from `System.Collections.Concurrent.IDictionaryDebugView<,>` in `System.Collections.Concurrent, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass IDictionaryDebugView\n{\n}\n\n// Generated from `System.Collections.Concurrent.Partitioner` in `System.Collections.Concurrent, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic public class Partitioner\n{\n}\n\n}\nnamespace Generic\n{\n// Generated from `System.Collections.Generic.CollectionDebugView<>` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass CollectionDebugView\n{\n}\n\n// Generated from `System.Collections.Generic.DictionaryDebugView<,>` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass DictionaryDebugView\n{\n}\n\n// Generated from `System.Collections.Generic.QueueDebugView<>` in `System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass QueueDebugView\n{\n}\n\n// Generated from `System.Collections.Generic.SortedSet<>` in `System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class SortedSet : System.Runtime.Serialization.ISerializable, System.Runtime.Serialization.IDeserializationCallback, System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.ISet, System.Collections.Generic.IReadOnlySet, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.Generic.ICollection.IsReadOnly { get => throw null; }\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n public bool Add(T item) => throw null;\n public bool IsProperSubsetOf(System.Collections.Generic.IEnumerable other) => throw null;\n public bool IsProperSupersetOf(System.Collections.Generic.IEnumerable other) => throw null;\n public bool IsSubsetOf(System.Collections.Generic.IEnumerable other) => throw null;\n public bool IsSupersetOf(System.Collections.Generic.IEnumerable other) => throw null;\n public bool Overlaps(System.Collections.Generic.IEnumerable other) => throw null;\n public bool Remove(T item) => throw null;\n public bool SetEquals(System.Collections.Generic.IEnumerable other) => throw null;\n public int Count { get => throw null; }\n public virtual bool Contains(T item) => throw null;\n public virtual void Clear() => throw null;\n public virtual void IntersectWith(System.Collections.Generic.IEnumerable other) => throw null;\n public void CopyTo(T[] array, int index) => throw null;\n public void ExceptWith(System.Collections.Generic.IEnumerable other) => throw null;\n public void SymmetricExceptWith(System.Collections.Generic.IEnumerable other) => throw null;\n public void UnionWith(System.Collections.Generic.IEnumerable other) => throw null;\n void System.Collections.Generic.ICollection.Add(T item) => throw null;\n void System.Collections.ICollection.CopyTo(System.Array array, int index) => throw null;\n void System.Runtime.Serialization.IDeserializationCallback.OnDeserialization(object sender) => throw null;\n void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) => throw null;\n}\n\n// Generated from `System.Collections.Generic.Stack<>` in `System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Stack : System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n public T Peek() => throw null;\n public int Count { get => throw null; }\n void System.Collections.ICollection.CopyTo(System.Array array, int arrayIndex) => throw null;\n}\n\n// Generated from `System.Collections.Generic.StackDebugView<>` in `System.Collections, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass StackDebugView\n{\n}\n\n}\nnamespace Specialized\n{\n// Generated from `System.Collections.Specialized.NameObjectCollectionBase` in `System.Collections.Specialized, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class NameObjectCollectionBase : System.Runtime.Serialization.ISerializable, System.Runtime.Serialization.IDeserializationCallback, System.Collections.IEnumerable, System.Collections.ICollection\n{\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n public virtual System.Collections.IEnumerator GetEnumerator() => throw null;\n public virtual int Count { get => throw null; }\n public virtual void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) => throw null;\n public virtual void OnDeserialization(object sender) => throw null;\n void System.Collections.ICollection.CopyTo(System.Array array, int index) => throw null;\n}\n\n// Generated from `System.Collections.Specialized.NameValueCollection` in `System.Collections.Specialized, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class NameValueCollection : System.Collections.Specialized.NameObjectCollectionBase\n{\n public string this[string name] { get => throw null; set => throw null; }\n}\n\n}\n}\nnamespace ComponentModel\n{\n// Generated from `System.ComponentModel.ComponentConverter` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ComponentConverter : System.ComponentModel.ReferenceConverter\n{\n}\n\n// Generated from `System.ComponentModel.DefaultEventAttribute` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DefaultEventAttribute : System.Attribute\n{\n private static DefaultEventAttribute() => throw null;\n public DefaultEventAttribute(string name) => throw null;\n public override bool Equals(object obj) => throw null;\n public override int GetHashCode() => throw null;\n}\n\n// Generated from `System.ComponentModel.DefaultPropertyAttribute` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DefaultPropertyAttribute : System.Attribute\n{\n private static DefaultPropertyAttribute() => throw null;\n public DefaultPropertyAttribute(string name) => throw null;\n public override bool Equals(object obj) => throw null;\n public override int GetHashCode() => throw null;\n}\n\n// Generated from `System.ComponentModel.INotifyPropertyChanged` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface INotifyPropertyChanged\n{\n}\n\n// Generated from `System.ComponentModel.ReferenceConverter` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ReferenceConverter : System.ComponentModel.TypeConverter\n{\n}\n\n// Generated from `System.ComponentModel.TypeConverterAttribute` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class TypeConverterAttribute : System.Attribute\n{\n private static TypeConverterAttribute() => throw null;\n public TypeConverterAttribute() => throw null;\n public TypeConverterAttribute(System.Type type) => throw null;\n public TypeConverterAttribute(string typeName) => throw null;\n public override bool Equals(object obj) => throw null;\n public override int GetHashCode() => throw null;\n}\n\n// Generated from `System.ComponentModel.TypeConverter` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class TypeConverter\n{\n}\n\n// Generated from `System.ComponentModel.TypeDescriptionProviderAttribute` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class TypeDescriptionProviderAttribute : System.Attribute\n{\n public TypeDescriptionProviderAttribute(System.Type type) => throw null;\n public TypeDescriptionProviderAttribute(string typeName) => throw null;\n}\n\n// Generated from `System.ComponentModel.TypeDescriptionProvider` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class TypeDescriptionProvider\n{\n}\n\n// Generated from `System.ComponentModel.TypeDescriptor` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class TypeDescriptor\n{\n}\n\nnamespace Design\n{\n// Generated from `System.ComponentModel.Design.DesignerOptionService` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class DesignerOptionService : System.ComponentModel.Design.IDesignerOptionService\n{\n}\n\n// Generated from `System.ComponentModel.Design.IDesignerOptionService` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IDesignerOptionService\n{\n}\n\n}\n}\nnamespace Dynamic\n{\n// Generated from `System.Dynamic.BindingRestrictions` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class BindingRestrictions\n{\n}\n\n// Generated from `System.Dynamic.DynamicMetaObject` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DynamicMetaObject\n{\n}\n\n// Generated from `System.Dynamic.ExpandoObject` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ExpandoObject : System.Dynamic.IDynamicMetaObjectProvider, System.ComponentModel.INotifyPropertyChanged, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable>, System.Collections.Generic.IDictionary, System.Collections.Generic.ICollection>\n{\n System.Collections.Generic.ICollection System.Collections.Generic.IDictionary.Values { get => throw null; }\n System.Collections.Generic.ICollection System.Collections.Generic.IDictionary.Keys { get => throw null; }\n System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.Generic.ICollection>.Contains(System.Collections.Generic.KeyValuePair item) => throw null;\n bool System.Collections.Generic.ICollection>.IsReadOnly { get => throw null; }\n bool System.Collections.Generic.ICollection>.Remove(System.Collections.Generic.KeyValuePair item) => throw null;\n bool System.Collections.Generic.IDictionary.ContainsKey(string key) => throw null;\n bool System.Collections.Generic.IDictionary.Remove(string key) => throw null;\n bool System.Collections.Generic.IDictionary.TryGetValue(string key, out object value) => throw null;\n int System.Collections.Generic.ICollection>.Count { get => throw null; }\n object this[string key] { get => throw null; set => throw null; }\n void System.Collections.Generic.ICollection>.Add(System.Collections.Generic.KeyValuePair item) => throw null;\n void System.Collections.Generic.ICollection>.Clear() => throw null;\n void System.Collections.Generic.ICollection>.CopyTo(System.Collections.Generic.KeyValuePair[] array, int arrayIndex) => throw null;\n void System.Collections.Generic.IDictionary.Add(string key, object value) => throw null;\n}\n\n// Generated from `System.Dynamic.IDynamicMetaObjectProvider` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IDynamicMetaObjectProvider\n{\n}\n\n// Generated from `System.Dynamic.UpdateDelegates` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic class UpdateDelegates\n{\n}\n\nnamespace Utils\n{\n// Generated from `System.Dynamic.Utils.ListProvider<>` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract class ListProvider : System.Collections.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection where T: class\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n public T this[int index] { get => throw null; set => throw null; }\n public bool Contains(T item) => throw null;\n public bool IsReadOnly { get => throw null; }\n public bool Remove(T item) => throw null;\n public int Count { get => throw null; }\n public int IndexOf(T item) => throw null;\n public void Add(T item) => throw null;\n public void Clear() => throw null;\n public void CopyTo(T[] array, int index) => throw null;\n public void Insert(int index, T item) => throw null;\n public void RemoveAt(int index) => throw null;\n}\n\n}\n}\nnamespace IO\n{\nnamespace Compression\n{\n// Generated from `System.IO.Compression.DeflateManagedStream` in `System.IO.Compression, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089`\nclass DeflateManagedStream : System.IO.Stream\n{\n protected override void Dispose(bool disposing) => throw null;\n public override System.IAsyncResult BeginRead(System.Byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.Int64 Length { get => throw null; }\n public override System.Int64 Position { get => throw null; set => throw null; }\n public override System.Int64 Seek(System.Int64 offset, System.IO.SeekOrigin origin) => throw null;\n public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task ReadAsync(System.Byte[] array, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override bool CanRead { get => throw null; }\n public override bool CanSeek { get => throw null; }\n public override bool CanWrite { get => throw null; }\n public override int EndRead(System.IAsyncResult asyncResult) => throw null;\n public override int Read(System.Byte[] array, int offset, int count) => throw null;\n public override void Flush() => throw null;\n public override void SetLength(System.Int64 value) => throw null;\n public override void Write(System.Byte[] array, int offset, int count) => throw null;\n}\n\n// Generated from `System.IO.Compression.DeflateStream` in `System.IO.Compression, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089`\npublic class DeflateStream : System.IO.Stream\n{\n protected override void Dispose(bool disposing) => throw null;\n public override System.IAsyncResult BeginRead(System.Byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.IAsyncResult BeginWrite(System.Byte[] array, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.Int64 Length { get => throw null; }\n public override System.Int64 Position { get => throw null; set => throw null; }\n public override System.Int64 Seek(System.Int64 offset, System.IO.SeekOrigin origin) => throw null;\n public override System.Threading.Tasks.Task CopyToAsync(System.IO.Stream destination, int bufferSize, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task WriteAsync(System.Byte[] array, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task ReadAsync(System.Byte[] array, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask DisposeAsync() => throw null;\n public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) => throw null;\n public override bool CanRead { get => throw null; }\n public override bool CanSeek { get => throw null; }\n public override bool CanWrite { get => throw null; }\n public override int EndRead(System.IAsyncResult asyncResult) => throw null;\n public override int Read(System.Byte[] array, int offset, int count) => throw null;\n public override int Read(System.Span buffer) => throw null;\n public override int ReadByte() => throw null;\n public override void CopyTo(System.IO.Stream destination, int bufferSize) => throw null;\n public override void EndWrite(System.IAsyncResult asyncResult) => throw null;\n public override void Flush() => throw null;\n public override void SetLength(System.Int64 value) => throw null;\n public override void Write(System.Byte[] array, int offset, int count) => throw null;\n public override void Write(System.ReadOnlySpan buffer) => throw null;\n}\n\n}\nnamespace Pipes\n{\n// Generated from `System.IO.Pipes.NamedPipeServerStream` in `System.IO.Pipes, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class NamedPipeServerStream : System.IO.Pipes.PipeStream\n{\n}\n\n// Generated from `System.IO.Pipes.PipeStream` in `System.IO.Pipes, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class PipeStream : System.IO.Stream\n{\n protected override void Dispose(bool disposing) => throw null;\n public override System.IAsyncResult BeginRead(System.Byte[] buffer, int offset, int count, System.AsyncCallback callback, object state) => throw null;\n public override System.IAsyncResult BeginWrite(System.Byte[] buffer, int offset, int count, System.AsyncCallback callback, object state) => throw null;\n public override System.Int64 Length { get => throw null; }\n public override System.Int64 Position { get => throw null; set => throw null; }\n public override System.Int64 Seek(System.Int64 offset, System.IO.SeekOrigin origin) => throw null;\n public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task WriteAsync(System.Byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task ReadAsync(System.Byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) => throw null;\n public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) => throw null;\n public override bool CanRead { get => throw null; }\n public override bool CanSeek { get => throw null; }\n public override bool CanWrite { get => throw null; }\n public override int EndRead(System.IAsyncResult asyncResult) => throw null;\n public override int Read(System.Byte[] buffer, int offset, int count) => throw null;\n public override int Read(System.Span buffer) => throw null;\n public override int ReadByte() => throw null;\n public override void EndWrite(System.IAsyncResult asyncResult) => throw null;\n public override void Flush() => throw null;\n public override void SetLength(System.Int64 value) => throw null;\n public override void Write(System.Byte[] buffer, int offset, int count) => throw null;\n public override void Write(System.ReadOnlySpan buffer) => throw null;\n public override void WriteByte(System.Byte value) => throw null;\n}\n\n}\n}\nnamespace Linq\n{\n// Generated from `System.Linq.Enumerable` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic public class Enumerable\n{\n public static System.Collections.Generic.IEnumerable Select(this System.Collections.Generic.IEnumerable source, System.Func selector) => throw null;\n}\n\n// Generated from `System.Linq.Grouping<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Grouping : System.Linq.IGrouping, System.Collections.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n TElement this[int index] { get => throw null; set => throw null; }\n bool System.Collections.Generic.ICollection.Contains(TElement item) => throw null;\n bool System.Collections.Generic.ICollection.IsReadOnly { get => throw null; }\n bool System.Collections.Generic.ICollection.Remove(TElement item) => throw null;\n int System.Collections.Generic.ICollection.Count { get => throw null; }\n int System.Collections.Generic.IList.IndexOf(TElement item) => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n void System.Collections.Generic.ICollection.Add(TElement item) => throw null;\n void System.Collections.Generic.ICollection.Clear() => throw null;\n void System.Collections.Generic.ICollection.CopyTo(TElement[] array, int arrayIndex) => throw null;\n void System.Collections.Generic.IList.Insert(int index, TElement item) => throw null;\n void System.Collections.Generic.IList.RemoveAt(int index) => throw null;\n}\n\n// Generated from `System.Linq.IGrouping<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IGrouping : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n}\n\n// Generated from `System.Linq.IIListProvider<>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\ninterface IIListProvider : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n}\n\n// Generated from `System.Linq.ILookup<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface ILookup : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable>\n{\n}\n\n// Generated from `System.Linq.IOrderedEnumerable<>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IOrderedEnumerable : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n}\n\n// Generated from `System.Linq.IPartition<>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\ninterface IPartition : System.Linq.IIListProvider, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n}\n\n// Generated from `System.Linq.IQueryable` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface IQueryable : System.Collections.IEnumerable\n{\n}\n\n// Generated from `System.Linq.Lookup<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Lookup : System.Linq.ILookup, System.Linq.IIListProvider>, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable>\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator> GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.OrderedEnumerable<>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract class OrderedEnumerable : System.Linq.IPartition, System.Linq.IOrderedEnumerable, System.Linq.IIListProvider, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.ParallelEnumerable` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic public class ParallelEnumerable\n{\n public static System.Linq.ParallelQuery AsParallel(this System.Collections.IEnumerable source) => throw null;\n}\n\n// Generated from `System.Linq.ParallelQuery<>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ParallelQuery : System.Linq.ParallelQuery, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n public virtual System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.ParallelQuery` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ParallelQuery : System.Collections.IEnumerable\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.Queryable` in `System.Linq.Queryable, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic public class Queryable\n{\n public static System.Linq.IQueryable AsQueryable(this System.Collections.IEnumerable source) => throw null;\n}\n\n// Generated from `System.Linq.SystemLinq_GroupingDebugView<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass SystemLinq_GroupingDebugView\n{\n}\n\n// Generated from `System.Linq.SystemLinq_LookupDebugView<,>` in `System.Linq, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass SystemLinq_LookupDebugView\n{\n}\n\nnamespace Expressions\n{\n// Generated from `System.Linq.Expressions.BlockExpressionList` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass BlockExpressionList : System.Collections.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n public System.Linq.Expressions.Expression this[int index] { get => throw null; set => throw null; }\n public bool Contains(System.Linq.Expressions.Expression item) => throw null;\n public bool IsReadOnly { get => throw null; }\n public bool Remove(System.Linq.Expressions.Expression item) => throw null;\n public int Count { get => throw null; }\n public int IndexOf(System.Linq.Expressions.Expression item) => throw null;\n public void Add(System.Linq.Expressions.Expression item) => throw null;\n public void Clear() => throw null;\n public void CopyTo(System.Linq.Expressions.Expression[] array, int index) => throw null;\n public void Insert(int index, System.Linq.Expressions.Expression item) => throw null;\n public void RemoveAt(int index) => throw null;\n}\n\n// Generated from `System.Linq.Expressions.Expression` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class Expression\n{\n public override string ToString() => throw null;\n}\n\n// Generated from `System.Linq.Expressions.ParameterExpression` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ParameterExpression : System.Linq.Expressions.Expression\n{\n}\n\nnamespace Compiler\n{\n// Generated from `System.Linq.Expressions.Compiler.CompilerScope` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass CompilerScope\n{\n}\n\n// Generated from `System.Linq.Expressions.Compiler.ParameterList` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass ParameterList : System.Collections.IEnumerable, System.Collections.Generic.IReadOnlyList, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n public System.Linq.Expressions.ParameterExpression this[int index] { get => throw null; }\n public int Count { get => throw null; }\n}\n\n}\nnamespace Interpreter\n{\n// Generated from `System.Linq.Expressions.Interpreter.HybridReferenceDictionary<,>` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass HybridReferenceDictionary where TKey: class\n{\n}\n\n// Generated from `System.Linq.Expressions.Interpreter.InstructionArray` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstruct InstructionArray\n{\n}\n\n// Generated from `System.Linq.Expressions.Interpreter.InstructionList` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass InstructionList\n{\n}\n\n// Generated from `System.Linq.Expressions.Interpreter.InterpretedFrameInfo` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstruct InterpretedFrameInfo\n{\n public override string ToString() => throw null;\n}\n\n// Generated from `System.Linq.Expressions.Interpreter.InterpretedFrame` in `System.Linq.Expressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass InterpretedFrame\n{\n}\n\n}\n}\nnamespace Parallel\n{\n// Generated from `System.Linq.Parallel.CancellableEnumerable` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic class CancellableEnumerable\n{\n}\n\n// Generated from `System.Linq.Parallel.ExceptionAggregator` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nstatic class ExceptionAggregator\n{\n}\n\n// Generated from `System.Linq.Parallel.ListChunk<>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass ListChunk : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.Parallel.Lookup<,>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass Lookup : System.Linq.ILookup, System.Collections.IEnumerable, System.Collections.Generic.IEnumerable>\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.Generic.IEnumerator> GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.Parallel.PartitionerQueryOperator<>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass PartitionerQueryOperator : System.Linq.Parallel.QueryOperator\n{\n}\n\n// Generated from `System.Linq.Parallel.QueryOperator<>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract class QueryOperator : System.Linq.ParallelQuery\n{\n public override System.Collections.Generic.IEnumerator GetEnumerator() => throw null;\n}\n\n// Generated from `System.Linq.Parallel.QueryResults<>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract class QueryResults : System.Collections.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.Generic.ICollection.Contains(T item) => throw null;\n bool System.Collections.Generic.ICollection.IsReadOnly { get => throw null; }\n bool System.Collections.Generic.ICollection.Remove(T item) => throw null;\n int System.Collections.Generic.IList.IndexOf(T item) => throw null;\n public T this[int index] { get => throw null; set => throw null; }\n public int Count { get => throw null; }\n void System.Collections.Generic.ICollection.Add(T item) => throw null;\n void System.Collections.Generic.ICollection.Clear() => throw null;\n void System.Collections.Generic.ICollection.CopyTo(T[] array, int arrayIndex) => throw null;\n void System.Collections.Generic.IList.Insert(int index, T item) => throw null;\n void System.Collections.Generic.IList.RemoveAt(int index) => throw null;\n}\n\n// Generated from `System.Linq.Parallel.ZipQueryOperator<,,>` in `System.Linq.Parallel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass ZipQueryOperator : System.Linq.Parallel.QueryOperator\n{\n}\n\n}\n}\nnamespace Net\n{\n// Generated from `System.Net.CookieCollection` in `System.Net.Primitives, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class CookieCollection : System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.ICollection\n{\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n public System.Collections.IEnumerator GetEnumerator() => throw null;\n public bool Contains(System.Net.Cookie cookie) => throw null;\n public bool IsReadOnly { get => throw null; }\n public bool IsSynchronized { get => throw null; }\n public bool Remove(System.Net.Cookie cookie) => throw null;\n public int Count { get => throw null; }\n public object SyncRoot { get => throw null; }\n public void Add(System.Net.Cookie cookie) => throw null;\n public void Clear() => throw null;\n public void CopyTo(System.Array array, int index) => throw null;\n public void CopyTo(System.Net.Cookie[] array, int index) => throw null;\n}\n\n// Generated from `System.Net.Cookie` in `System.Net.Primitives, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Cookie\n{\n public override bool Equals(object comparand) => throw null;\n public override int GetHashCode() => throw null;\n public override string ToString() => throw null;\n}\n\n// Generated from `System.Net.HttpResponseStream` in `System.Net.HttpListener, Version=5.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51`\nclass HttpResponseStream : System.IO.Stream\n{\n protected override void Dispose(bool disposing) => throw null;\n public override System.IAsyncResult BeginRead(System.Byte[] buffer, int offset, int size, System.AsyncCallback callback, object state) => throw null;\n public override System.IAsyncResult BeginWrite(System.Byte[] buffer, int offset, int size, System.AsyncCallback callback, object state) => throw null;\n public override System.Int64 Length { get => throw null; }\n public override System.Int64 Position { get => throw null; set => throw null; }\n public override System.Int64 Seek(System.Int64 offset, System.IO.SeekOrigin origin) => throw null;\n public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) => throw null;\n public override bool CanRead { get => throw null; }\n public override bool CanSeek { get => throw null; }\n public override bool CanWrite { get => throw null; }\n public override int EndRead(System.IAsyncResult asyncResult) => throw null;\n public override int Read(System.Byte[] buffer, int offset, int size) => throw null;\n public override void EndWrite(System.IAsyncResult asyncResult) => throw null;\n public override void Flush() => throw null;\n public override void SetLength(System.Int64 value) => throw null;\n public override void Write(System.Byte[] buffer, int offset, int size) => throw null;\n}\n\n// Generated from `System.Net.StreamFramer` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass StreamFramer\n{\n}\n\nnamespace Security\n{\n// Generated from `System.Net.Security.AuthenticatedStream` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class AuthenticatedStream : System.IO.Stream\n{\n protected override void Dispose(bool disposing) => throw null;\n public override System.Threading.Tasks.ValueTask DisposeAsync() => throw null;\n}\n\n// Generated from `System.Net.Security.CipherSuitesPolicy` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class CipherSuitesPolicy\n{\n}\n\n// Generated from `System.Net.Security.NegotiateStream` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class NegotiateStream : System.Net.Security.AuthenticatedStream\n{\n protected override void Dispose(bool disposing) => throw null;\n public override System.IAsyncResult BeginRead(System.Byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.IAsyncResult BeginWrite(System.Byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.Int64 Length { get => throw null; }\n public override System.Int64 Position { get => throw null; set => throw null; }\n public override System.Int64 Seek(System.Int64 offset, System.IO.SeekOrigin origin) => throw null;\n public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task WriteAsync(System.Byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task ReadAsync(System.Byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask DisposeAsync() => throw null;\n public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) => throw null;\n public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) => throw null;\n public override bool CanRead { get => throw null; }\n public override bool CanSeek { get => throw null; }\n public override bool CanTimeout { get => throw null; }\n public override bool CanWrite { get => throw null; }\n public override int EndRead(System.IAsyncResult asyncResult) => throw null;\n public override int Read(System.Byte[] buffer, int offset, int count) => throw null;\n public override int ReadTimeout { get => throw null; set => throw null; }\n public override int WriteTimeout { get => throw null; set => throw null; }\n public override void EndWrite(System.IAsyncResult asyncResult) => throw null;\n public override void Flush() => throw null;\n public override void SetLength(System.Int64 value) => throw null;\n public override void Write(System.Byte[] buffer, int offset, int count) => throw null;\n}\n\n// Generated from `System.Net.Security.SslStream` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class SslStream : System.Net.Security.AuthenticatedStream\n{\n protected override void Dispose(bool disposing) => throw null;\n public override System.IAsyncResult BeginRead(System.Byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.IAsyncResult BeginWrite(System.Byte[] buffer, int offset, int count, System.AsyncCallback asyncCallback, object asyncState) => throw null;\n public override System.Int64 Length { get => throw null; }\n public override System.Int64 Position { get => throw null; set => throw null; }\n public override System.Int64 Seek(System.Int64 offset, System.IO.SeekOrigin origin) => throw null;\n public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task WriteAsync(System.Byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task ReadAsync(System.Byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask DisposeAsync() => throw null;\n public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) => throw null;\n public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) => throw null;\n public override bool CanRead { get => throw null; }\n public override bool CanSeek { get => throw null; }\n public override bool CanTimeout { get => throw null; }\n public override bool CanWrite { get => throw null; }\n public override int EndRead(System.IAsyncResult asyncResult) => throw null;\n public override int Read(System.Byte[] buffer, int offset, int count) => throw null;\n public override int ReadByte() => throw null;\n public override int ReadTimeout { get => throw null; set => throw null; }\n public override int WriteTimeout { get => throw null; set => throw null; }\n public override void EndWrite(System.IAsyncResult asyncResult) => throw null;\n public override void Flush() => throw null;\n public override void SetLength(System.Int64 value) => throw null;\n public override void Write(System.Byte[] buffer, int offset, int count) => throw null;\n}\n\n// Generated from `System.Net.Security.TlsCipherSuite` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic enum TlsCipherSuite\n{\n}\n\n// Generated from `System.Net.Security.TlsFrameHelper` in `System.Net.Security, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass TlsFrameHelper\n{\n}\n\n}\nnamespace WebSockets\n{\n// Generated from `System.Net.WebSockets.HttpWebSocket` in `System.Net.HttpListener, Version=5.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51`\nstatic class HttpWebSocket\n{\n}\n\n}\n}\nnamespace Runtime\n{\nnamespace Serialization\n{\n// Generated from `System.Runtime.Serialization.DataContractAttribute` in `System.Runtime.Serialization.Primitives, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DataContractAttribute : System.Attribute\n{\n public DataContractAttribute() => throw null;\n}\n\n// Generated from `System.Runtime.Serialization.DataMemberAttribute` in `System.Runtime.Serialization.Primitives, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class DataMemberAttribute : System.Attribute\n{\n public DataMemberAttribute() => throw null;\n}\n\n}\n}\nnamespace Security\n{\nnamespace Cryptography\n{\n// Generated from `System.Security.Cryptography.CryptoStream` in `System.Security.Cryptography.Primitives, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class CryptoStream : System.IO.Stream, System.IDisposable\n{\n protected override void Dispose(bool disposing) => throw null;\n public override System.IAsyncResult BeginRead(System.Byte[] buffer, int offset, int count, System.AsyncCallback callback, object state) => throw null;\n public override System.IAsyncResult BeginWrite(System.Byte[] buffer, int offset, int count, System.AsyncCallback callback, object state) => throw null;\n public override System.Int64 Length { get => throw null; }\n public override System.Int64 Position { get => throw null; set => throw null; }\n public override System.Int64 Seek(System.Int64 offset, System.IO.SeekOrigin origin) => throw null;\n public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task WriteAsync(System.Byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.Task ReadAsync(System.Byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null;\n public override System.Threading.Tasks.ValueTask DisposeAsync() => throw null;\n public override bool CanRead { get => throw null; }\n public override bool CanSeek { get => throw null; }\n public override bool CanWrite { get => throw null; }\n public override int EndRead(System.IAsyncResult asyncResult) => throw null;\n public override int Read(System.Byte[] buffer, int offset, int count) => throw null;\n public override int ReadByte() => throw null;\n public override void EndWrite(System.IAsyncResult asyncResult) => throw null;\n public override void Flush() => throw null;\n public override void SetLength(System.Int64 value) => throw null;\n public override void Write(System.Byte[] buffer, int offset, int count) => throw null;\n public override void WriteByte(System.Byte value) => throw null;\n}\n\n// Generated from `System.Security.Cryptography.HashAlgorithm` in `System.Security.Cryptography.Primitives, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nabstract public class HashAlgorithm : System.Security.Cryptography.ICryptoTransform, System.IDisposable\n{\n public void Dispose() => throw null;\n}\n\n// Generated from `System.Security.Cryptography.ICryptoTransform` in `System.Security.Cryptography.Primitives, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic interface ICryptoTransform : System.IDisposable\n{\n}\n\n}\n}\nnamespace Text\n{\nnamespace RegularExpressions\n{\n// Generated from `System.Text.RegularExpressions.Capture` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Capture\n{\n public override string ToString() => throw null;\n}\n\n// Generated from `System.Text.RegularExpressions.CollectionDebuggerProxy<>` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\nclass CollectionDebuggerProxy\n{\n}\n\n// Generated from `System.Text.RegularExpressions.GroupCollection` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class GroupCollection : System.Collections.IList, System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.Generic.IReadOnlyList, System.Collections.Generic.IReadOnlyDictionary, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlyCollection>, System.Collections.Generic.IList, System.Collections.Generic.IEnumerable, System.Collections.Generic.IEnumerable>, System.Collections.Generic.ICollection\n{\n System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() => throw null;\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Text.RegularExpressions.Group this[int index] { get => throw null; set => throw null; }\n bool System.Collections.Generic.ICollection.Contains(System.Text.RegularExpressions.Group item) => throw null;\n bool System.Collections.Generic.ICollection.Remove(System.Text.RegularExpressions.Group item) => throw null;\n bool System.Collections.IList.Contains(object value) => throw null;\n bool System.Collections.IList.IsFixedSize { get => throw null; }\n int System.Collections.Generic.IList.IndexOf(System.Text.RegularExpressions.Group item) => throw null;\n int System.Collections.IList.Add(object value) => throw null;\n int System.Collections.IList.IndexOf(object value) => throw null;\n object this[int index] { get => throw null; set => throw null; }\n public System.Collections.Generic.IEnumerable Values { get => throw null; }\n public System.Collections.Generic.IEnumerable Keys { get => throw null; }\n public System.Collections.IEnumerator GetEnumerator() => throw null;\n public System.Text.RegularExpressions.Group this[string groupname] { get => throw null; }\n public bool ContainsKey(string key) => throw null;\n public bool IsReadOnly { get => throw null; }\n public bool IsSynchronized { get => throw null; }\n public bool TryGetValue(string key, out System.Text.RegularExpressions.Group value) => throw null;\n public int Count { get => throw null; }\n public object SyncRoot { get => throw null; }\n public void CopyTo(System.Array array, int arrayIndex) => throw null;\n public void CopyTo(System.Text.RegularExpressions.Group[] array, int arrayIndex) => throw null;\n void System.Collections.Generic.ICollection.Add(System.Text.RegularExpressions.Group item) => throw null;\n void System.Collections.Generic.ICollection.Clear() => throw null;\n void System.Collections.Generic.IList.Insert(int index, System.Text.RegularExpressions.Group item) => throw null;\n void System.Collections.Generic.IList.RemoveAt(int index) => throw null;\n void System.Collections.IList.Clear() => throw null;\n void System.Collections.IList.Insert(int index, object value) => throw null;\n void System.Collections.IList.Remove(object value) => throw null;\n void System.Collections.IList.RemoveAt(int index) => throw null;\n}\n\n// Generated from `System.Text.RegularExpressions.Group` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Group : System.Text.RegularExpressions.Capture\n{\n}\n\n// Generated from `System.Text.RegularExpressions.Match` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Match : System.Text.RegularExpressions.Group\n{\n}\n\n// Generated from `System.Text.RegularExpressions.RegexOptions` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\n[System.Flags]\npublic enum RegexOptions\n{\n IgnoreCase,\n}\n\n// Generated from `System.Text.RegularExpressions.Regex` in `System.Text.RegularExpressions, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class Regex : System.Runtime.Serialization.ISerializable\n{\n public Regex(string pattern) => throw null;\n public Regex(string pattern, System.Text.RegularExpressions.RegexOptions options, System.TimeSpan matchTimeout) => throw null;\n public System.Text.RegularExpressions.Match Match(string input) => throw null;\n public override string ToString() => throw null;\n public static System.Text.RegularExpressions.Match Match(string input, string pattern) => throw null;\n public static System.Text.RegularExpressions.Match Match(string input, string pattern, System.Text.RegularExpressions.RegexOptions options, System.TimeSpan matchTimeout) => throw null;\n public string Replace(string input, string replacement) => throw null;\n void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext context) => throw null;\n}\n\n}\n}\nnamespace Timers\n{\n// Generated from `System.Timers.TimersDescriptionAttribute` in `System.ComponentModel.TypeConverter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class TimersDescriptionAttribute\n{\n internal TimersDescriptionAttribute(string description, string defaultValue) => throw null;\n public TimersDescriptionAttribute(string description) => throw null;\n}\n\n}\nnamespace Windows\n{\nnamespace Markup\n{\n// Generated from `System.Windows.Markup.ValueSerializerAttribute` in `System.ObjectModel, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`\npublic class ValueSerializerAttribute : System.Attribute\n{\n public ValueSerializerAttribute(System.Type valueSerializerType) => throw null;\n public ValueSerializerAttribute(string valueSerializerTypeName) => throw null;\n}\n\n}\n}\n}\n | diff --git a/csharp/ql/test/resources/stubs/Dapper.cs b/csharp/ql/test/resources/stubs/Dapper.cs new file mode 100644 index 00000000000..d96f1cd4ee6 --- /dev/null +++ b/csharp/ql/test/resources/stubs/Dapper.cs @@ -0,0 +1,34 @@ +// This file contains auto-generated code. +// original-extractor-options: /r:Dapper.dll /r:System.Data.SqlClient.dll ... + +namespace Dapper +{ + // Generated from `Dapper.CommandDefinition` in `Dapper, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null` + public struct CommandDefinition + { + public CommandDefinition(string commandText, object parameters = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null, Dapper.CommandFlags flags = CommandFlags.Buffered, System.Threading.CancellationToken cancellationToken = default) => throw null; + } + + // Generated from `Dapper.CommandFlags` in `Dapper, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null` + [System.Flags] + public enum CommandFlags + { + None = 0x0, + Buffered = 0x1, + Pipelined = 0x2, + NoCache = 0x4 + } + + // Generated from `Dapper.SqlMapper` in `Dapper, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null` + static public class SqlMapper + { + public static System.Collections.Generic.IEnumerable Query(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null; + public static System.Data.IDataReader ExecuteReader(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null; + public static System.Threading.Tasks.Task> QueryAsync(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null; + public static System.Threading.Tasks.Task QueryFirstAsync(this System.Data.IDbConnection cnn, Dapper.CommandDefinition command) => throw null; + public static System.Threading.Tasks.Task QueryFirstAsync(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null; + public static System.Threading.Tasks.Task ExecuteAsync(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null; + public static object ExecuteScalar(this System.Data.IDbConnection cnn, string sql, object param = null, System.Data.IDbTransaction transaction = null, int? commandTimeout = null, System.Data.CommandType? commandType = null) => throw null; + } +} + diff --git a/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/old.dbscheme b/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/old.dbscheme new file mode 100644 index 00000000000..9258e9b38d8 --- /dev/null +++ b/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/old.dbscheme @@ -0,0 +1,2083 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * csc f1.cs f2.cs f3.cs + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | --compiler + * 1 | *path to compiler* + * 2 | --cil + * 3 | f1.cs + * 4 | f2.cs + * 5 | f3.cs + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.cs + * 1 | f2.cs + * 2 | f3.cs + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The references used by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs /r:ref1.dll /r:ref2.dll /r:ref3.dll + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | ref1.dll + * 1 | ref2.dll + * 2 | ref3.dll + */ +#keyset[id, num] +compilation_referencing_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +extractor_messages( + unique int id: @extractor_message, + int severity: int ref, + string origin : string ref, + string text : string ref, + string entity : string ref, + int location: @location_default ref, + string stack_trace : string ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +compilation_assembly( + unique int id : @compilation ref, + int assembly: @assembly ref +) + +/* + * External artifacts + */ + +externalDefects( + unique int id: @externalDefect, + string queryPath: string ref, + int location: @location ref, + string message: string ref, + float severity: float ref); + +externalMetrics( + unique int id: @externalMetric, + string queryPath: string ref, + int location: @location ref, + float value: float ref); + +externalData( + int id: @externalDataElement, + string path: string ref, + int column: int ref, + string value: string ref); + +snapshotDate( + unique date snapshotDate: date ref); + +sourceLocationPrefix( + string prefix: string ref); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id: @duplication, + string relativePath: string ref, + int equivClass: int ref); + +similarCode( + unique int id: @similarity, + string relativePath: string ref, + int equivClass: int ref); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id: @duplication_or_similarity ref, + int offset: int ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +/* + * C# dbscheme + */ + +/** ELEMENTS **/ + +@element = @declaration | @stmt | @expr | @modifier | @attribute | @namespace_declaration + | @using_directive | @type_parameter_constraints | @external_element + | @xmllocatable | @asp_element | @namespace | @preprocessor_directive; + +@declaration = @callable | @generic | @assignable | @namespace; + +@named_element = @namespace | @declaration; + +@declaration_with_accessors = @property | @indexer | @event; + +@assignable = @variable | @assignable_with_accessors | @event; + +@assignable_with_accessors = @property | @indexer; + +@external_element = @externalMetric | @externalDefect | @externalDataElement; + +@attributable = @assembly | @field | @parameter | @operator | @method | @constructor + | @destructor | @callable_accessor | @value_or_ref_type | @declaration_with_accessors + | @local_function; + +/** LOCATIONS, ASEMMBLIES, MODULES, FILES and FOLDERS **/ + +@location = @location_default | @assembly; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +locations_mapped( + unique int id: @location_default ref, + int mapped_to: @location_default ref); + +@sourceline = @file | @callable | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref); + +assemblies( + unique int id: @assembly, + int file: @file ref, + string fullname: string ref, + string name: string ref, + string version: string ref); + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref); + +@container = @folder | @file ; + +containerparent( + int parent: @container ref, + unique int child: @container ref); + +file_extraction_mode( + unique int file: @file ref, + int mode: int ref + /* 0 = normal, 1 = standalone extractor */ + ); + +/** NAMESPACES **/ + +@type_container = @namespace | @type; + +namespaces( + unique int id: @namespace, + string name: string ref); + +namespace_declarations( + unique int id: @namespace_declaration, + int namespace_id: @namespace ref); + +namespace_declaration_location( + unique int id: @namespace_declaration ref, + int loc: @location ref); + +parent_namespace( + unique int child_id: @type_container ref, + int namespace_id: @namespace ref); + +@declaration_or_directive = @namespace_declaration | @type | @using_directive; + +parent_namespace_declaration( + int child_id: @declaration_or_directive ref, // cannot be unique because of partial classes + int namespace_id: @namespace_declaration ref); + +@using_directive = @using_namespace_directive | @using_static_directive; + +using_namespace_directives( + unique int id: @using_namespace_directive, + int namespace_id: @namespace ref); + +using_static_directives( + unique int id: @using_static_directive, + int type_id: @type_or_ref ref); + +using_directive_location( + unique int id: @using_directive ref, + int loc: @location ref); + +@preprocessor_directive = @pragma_warning | @pragma_checksum | @directive_define | @directive_undefine | @directive_warning + | @directive_error | @directive_nullable | @directive_line | @directive_region | @directive_endregion | @directive_if + | @directive_elif | @directive_else | @directive_endif; + +@conditional_directive = @directive_if | @directive_elif; +@branch_directive = @directive_if | @directive_elif | @directive_else; + +directive_ifs( + unique int id: @directive_if, + int branchTaken: int ref, /* 0: false, 1: true */ + int conditionValue: int ref); /* 0: false, 1: true */ + +directive_elifs( + unique int id: @directive_elif, + int branchTaken: int ref, /* 0: false, 1: true */ + int conditionValue: int ref, /* 0: false, 1: true */ + int parent: @directive_if ref, + int index: int ref); + +directive_elses( + unique int id: @directive_else, + int branchTaken: int ref, /* 0: false, 1: true */ + int parent: @directive_if ref, + int index: int ref); + +#keyset[id, start] +directive_endifs( + unique int id: @directive_endif, + unique int start: @directive_if ref); + +directive_define_symbols( + unique int id: @define_symbol_expr ref, + string name: string ref); + +directive_regions( + unique int id: @directive_region, + string name: string ref); + +#keyset[id, start] +directive_endregions( + unique int id: @directive_endregion, + unique int start: @directive_region ref); + +directive_lines( + unique int id: @directive_line, + int kind: int ref); /* 0: default, 1: hidden, 2: numeric */ + +directive_line_value( + unique int id: @directive_line ref, + int line: int ref); + +directive_line_file( + unique int id: @directive_line ref, + int file: @file ref +) + +directive_nullables( + unique int id: @directive_nullable, + int setting: int ref, /* 0: disable, 1: enable, 2: restore */ + int target: int ref); /* 0: none, 1: annotations, 2: warnings */ + +directive_warnings( + unique int id: @directive_warning, + string message: string ref); + +directive_errors( + unique int id: @directive_error, + string message: string ref); + +directive_undefines( + unique int id: @directive_undefine, + string name: string ref); + +directive_defines( + unique int id: @directive_define, + string name: string ref); + +pragma_checksums( + unique int id: @pragma_checksum, + int file: @file ref, + string guid: string ref, + string bytes: string ref); + +pragma_warnings( + unique int id: @pragma_warning, + int kind: int ref /* 0 = disable, 1 = restore */); + +#keyset[id, index] +pragma_warning_error_codes( + int id: @pragma_warning ref, + string errorCode: string ref, + int index: int ref); + +preprocessor_directive_location( + unique int id: @preprocessor_directive ref, + int loc: @location ref); + +preprocessor_directive_compilation( + unique int id: @preprocessor_directive ref, + int compilation: @compilation ref); + +preprocessor_directive_active( + unique int id: @preprocessor_directive ref, + int active: int ref); /* 0: false, 1: true */ + +/** TYPES **/ + +types( + unique int id: @type, + int kind: int ref, + string name: string ref); + +case @type.kind of + 1 = @bool_type +| 2 = @char_type +| 3 = @decimal_type +| 4 = @sbyte_type +| 5 = @short_type +| 6 = @int_type +| 7 = @long_type +| 8 = @byte_type +| 9 = @ushort_type +| 10 = @uint_type +| 11 = @ulong_type +| 12 = @float_type +| 13 = @double_type +| 14 = @enum_type +| 15 = @struct_type +| 17 = @class_type +| 19 = @interface_type +| 20 = @delegate_type +| 21 = @null_type +| 22 = @type_parameter +| 23 = @pointer_type +| 24 = @nullable_type +| 25 = @array_type +| 26 = @void_type +| 27 = @int_ptr_type +| 28 = @uint_ptr_type +| 29 = @dynamic_type +| 30 = @arglist_type +| 31 = @unknown_type +| 32 = @tuple_type +| 33 = @function_pointer_type + ; + +@simple_type = @bool_type | @char_type | @integral_type | @floating_point_type | @decimal_type; +@integral_type = @signed_integral_type | @unsigned_integral_type; +@signed_integral_type = @sbyte_type | @short_type | @int_type | @long_type; +@unsigned_integral_type = @byte_type | @ushort_type | @uint_type | @ulong_type; +@floating_point_type = @float_type | @double_type; +@value_type = @simple_type | @enum_type | @struct_type | @nullable_type | @int_ptr_type + | @uint_ptr_type | @tuple_type; +@ref_type = @class_type | @interface_type | @array_type | @delegate_type | @null_type + | @dynamic_type; +@value_or_ref_type = @value_type | @ref_type; + +typerefs( + unique int id: @typeref, + string name: string ref); + +typeref_type( + int id: @typeref ref, + unique int typeId: @type ref); + +@type_or_ref = @type | @typeref; + +array_element_type( + unique int array: @array_type ref, + int dimension: int ref, + int rank: int ref, + int element: @type_or_ref ref); + +nullable_underlying_type( + unique int nullable: @nullable_type ref, + int underlying: @type_or_ref ref); + +pointer_referent_type( + unique int pointer: @pointer_type ref, + int referent: @type_or_ref ref); + +enum_underlying_type( + unique int enum_id: @enum_type ref, + int underlying_type_id: @type_or_ref ref); + +delegate_return_type( + unique int delegate_id: @delegate_type ref, + int return_type_id: @type_or_ref ref); + +function_pointer_return_type( + unique int function_pointer_id: @function_pointer_type ref, + int return_type_id: @type_or_ref ref); + +extend( + unique int sub: @type ref, + int super: @type_or_ref ref); + +anonymous_types( + unique int id: @type ref); + +@interface_or_ref = @interface_type | @typeref; + +implement( + int sub: @type ref, + int super: @type_or_ref ref); + +type_location( + int id: @type ref, + int loc: @location ref); + +tuple_underlying_type( + unique int tuple: @tuple_type ref, + int struct: @type_or_ref ref); + +#keyset[tuple, index] +tuple_element( + int tuple: @tuple_type ref, + int index: int ref, + unique int field: @field ref); + +attributes( + unique int id: @attribute, + int type_id: @type_or_ref ref, + int target: @attributable ref); + +attribute_location( + int id: @attribute ref, + int loc: @location ref); + +@type_mention_parent = @element | @type_mention; + +type_mention( + unique int id: @type_mention, + int type_id: @type_or_ref ref, + int parent: @type_mention_parent ref); + +type_mention_location( + unique int id: @type_mention ref, + int loc: @location ref); + +@has_type_annotation = @assignable | @type_parameter | @callable | @expr | @delegate_type | @generic | @function_pointer_type; + +/** + * A direct annotation on an entity, for example `string? x;`. + * + * Annotations: + * 2 = reftype is not annotated "!" + * 3 = reftype is annotated "?" + * 4 = readonly ref type / in parameter + * 5 = ref type parameter, return or local variable + * 6 = out parameter + * + * Note that the annotation depends on the element it annotates. + * @assignable: The annotation is on the type of the assignable, for example the variable type. + * @type_parameter: The annotation is on the reftype constraint + * @callable: The annotation is on the return type + * @array_type: The annotation is on the element type + */ +type_annotation(int id: @has_type_annotation ref, int annotation: int ref); + +nullability(unique int nullability: @nullability, int kind: int ref); + +case @nullability.kind of + 0 = @oblivious +| 1 = @not_annotated +| 2 = @annotated +; + +#keyset[parent, index] +nullability_parent(int nullability: @nullability ref, int index: int ref, int parent: @nullability ref) + +type_nullability(int id: @has_type_annotation ref, int nullability: @nullability ref); + +/** + * The nullable flow state of an expression, as determined by Roslyn. + * 0 = none (default, not populated) + * 1 = not null + * 2 = maybe null + */ +expr_flowstate(unique int id: @expr ref, int state: int ref); + +/** GENERICS **/ + +@generic = @type | @method | @local_function; + +type_parameters( + unique int id: @type_parameter ref, + int index: int ref, + int generic_id: @generic ref, + int variance: int ref /* none = 0, out = 1, in = 2 */); + +#keyset[constructed_id, index] +type_arguments( + int id: @type_or_ref ref, + int index: int ref, + int constructed_id: @generic_or_ref ref); + +@generic_or_ref = @generic | @typeref; + +constructed_generic( + unique int constructed: @generic ref, + int generic: @generic_or_ref ref); + +type_parameter_constraints( + unique int id: @type_parameter_constraints, + int param_id: @type_parameter ref); + +type_parameter_constraints_location( + int id: @type_parameter_constraints ref, + int loc: @location ref); + +general_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int kind: int ref /* class = 1, struct = 2, new = 3 */); + +specific_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref); + +specific_type_parameter_nullability( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref, + int nullability: @nullability ref); + +/** FUNCTION POINTERS */ + +function_pointer_calling_conventions( + int id: @function_pointer_type ref, + int kind: int ref); + +#keyset[id, index] +has_unmanaged_calling_conventions( + int id: @function_pointer_type ref, + int index: int ref, + int conv_id: @type_or_ref ref); + +/** MODIFIERS */ + +@modifiable = @modifiable_direct | @event_accessor; + +@modifiable_direct = @member | @accessor | @local_function | @anonymous_function_expr; + +modifiers( + unique int id: @modifier, + string name: string ref); + +has_modifiers( + int id: @modifiable_direct ref, + int mod_id: @modifier ref); + +compiler_generated(unique int id: @modifiable_direct ref); + +/** MEMBERS **/ + +@member = @method | @constructor | @destructor | @field | @property | @event | @operator | @indexer | @type; + +@named_exprorstmt = @goto_stmt | @labeled_stmt | @expr; + +@virtualizable = @method | @property | @indexer | @event; + +exprorstmt_name( + unique int parent_id: @named_exprorstmt ref, + string name: string ref); + +nested_types( + unique int id: @type ref, + int declaring_type_id: @type ref, + int unbound_id: @type ref); + +properties( + unique int id: @property, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @property ref); + +property_location( + int id: @property ref, + int loc: @location ref); + +indexers( + unique int id: @indexer, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @indexer ref); + +indexer_location( + int id: @indexer ref, + int loc: @location ref); + +accessors( + unique int id: @accessor, + int kind: int ref, + string name: string ref, + int declaring_member_id: @member ref, + int unbound_id: @accessor ref); + +case @accessor.kind of + 1 = @getter +| 2 = @setter + ; + +init_only_accessors( + unique int id: @accessor ref); + +accessor_location( + int id: @accessor ref, + int loc: @location ref); + +events( + unique int id: @event, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @event ref); + +event_location( + int id: @event ref, + int loc: @location ref); + +event_accessors( + unique int id: @event_accessor, + int kind: int ref, + string name: string ref, + int declaring_event_id: @event ref, + int unbound_id: @event_accessor ref); + +case @event_accessor.kind of + 1 = @add_event_accessor +| 2 = @remove_event_accessor + ; + +event_accessor_location( + int id: @event_accessor ref, + int loc: @location ref); + +operators( + unique int id: @operator, + string name: string ref, + string symbol: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @operator ref); + +operator_location( + int id: @operator ref, + int loc: @location ref); + +constant_value( + int id: @variable ref, + string value: string ref); + +/** CALLABLES **/ + +@callable = @method | @constructor | @destructor | @operator | @callable_accessor | @anonymous_function_expr | @local_function; + +@callable_accessor = @accessor | @event_accessor; + +methods( + unique int id: @method, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @method ref); + +method_location( + int id: @method ref, + int loc: @location ref); + +constructors( + unique int id: @constructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @constructor ref); + +constructor_location( + int id: @constructor ref, + int loc: @location ref); + +destructors( + unique int id: @destructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @destructor ref); + +destructor_location( + int id: @destructor ref, + int loc: @location ref); + +overrides( + int id: @callable ref, + int base_id: @callable ref); + +explicitly_implements( + int id: @member ref, + int interface_id: @interface_or_ref ref); + +local_functions( + unique int id: @local_function, + string name: string ref, + int return_type: @type ref, + int unbound_id: @local_function ref); + +local_function_stmts( + unique int fn: @local_function_stmt ref, + int stmt: @local_function ref); + +/** VARIABLES **/ + +@variable = @local_scope_variable | @field; + +@local_scope_variable = @local_variable | @parameter; + +fields( + unique int id: @field, + int kind: int ref, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @field ref); + +case @field.kind of + 1 = @addressable_field +| 2 = @constant + ; + +field_location( + int id: @field ref, + int loc: @location ref); + +localvars( + unique int id: @local_variable, + int kind: int ref, + string name: string ref, + int implicitly_typed: int ref /* 0 = no, 1 = yes */, + int type_id: @type_or_ref ref, + int parent_id: @local_var_decl_expr ref); + +case @local_variable.kind of + 1 = @addressable_local_variable +| 2 = @local_constant +| 3 = @local_variable_ref + ; + +localvar_location( + unique int id: @local_variable ref, + int loc: @location ref); + +@parameterizable = @callable | @delegate_type | @indexer | @function_pointer_type; + +#keyset[name, parent_id] +#keyset[index, parent_id] +params( + unique int id: @parameter, + string name: string ref, + int type_id: @type_or_ref ref, + int index: int ref, + int mode: int ref, /* value = 0, ref = 1, out = 2, array = 3, this = 4 */ + int parent_id: @parameterizable ref, + int unbound_id: @parameter ref); + +param_location( + int id: @parameter ref, + int loc: @location ref); + +/** STATEMENTS **/ + +@exprorstmt_parent = @control_flow_element | @top_level_exprorstmt_parent; + +statements( + unique int id: @stmt, + int kind: int ref); + +#keyset[index, parent] +stmt_parent( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_stmt_parent = @callable; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +stmt_parent_top_level( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @top_level_stmt_parent ref); + +case @stmt.kind of + 1 = @block_stmt +| 2 = @expr_stmt +| 3 = @if_stmt +| 4 = @switch_stmt +| 5 = @while_stmt +| 6 = @do_stmt +| 7 = @for_stmt +| 8 = @foreach_stmt +| 9 = @break_stmt +| 10 = @continue_stmt +| 11 = @goto_stmt +| 12 = @goto_case_stmt +| 13 = @goto_default_stmt +| 14 = @throw_stmt +| 15 = @return_stmt +| 16 = @yield_stmt +| 17 = @try_stmt +| 18 = @checked_stmt +| 19 = @unchecked_stmt +| 20 = @lock_stmt +| 21 = @using_block_stmt +| 22 = @var_decl_stmt +| 23 = @const_decl_stmt +| 24 = @empty_stmt +| 25 = @unsafe_stmt +| 26 = @fixed_stmt +| 27 = @label_stmt +| 28 = @catch +| 29 = @case_stmt +| 30 = @local_function_stmt +| 31 = @using_decl_stmt + ; + +@using_stmt = @using_block_stmt | @using_decl_stmt; + +@labeled_stmt = @label_stmt | @case; + +@decl_stmt = @var_decl_stmt | @const_decl_stmt | @using_decl_stmt; + +@cond_stmt = @if_stmt | @switch_stmt; + +@loop_stmt = @while_stmt | @do_stmt | @for_stmt | @foreach_stmt; + +@jump_stmt = @break_stmt | @goto_any_stmt | @continue_stmt | @throw_stmt | @return_stmt + | @yield_stmt; + +@goto_any_stmt = @goto_default_stmt | @goto_case_stmt | @goto_stmt; + + +stmt_location( + unique int id: @stmt ref, + int loc: @location ref); + +catch_type( + unique int catch_id: @catch ref, + int type_id: @type_or_ref ref, + int kind: int ref /* explicit = 1, implicit = 2 */); + +foreach_stmt_info( + unique int id: @foreach_stmt ref, + int kind: int ref /* non-async = 1, async = 2 */); + +@foreach_symbol = @method | @property | @type_or_ref; + +#keyset[id, kind] +foreach_stmt_desugar( + int id: @foreach_stmt ref, + int symbol: @foreach_symbol ref, + int kind: int ref /* GetEnumeratorMethod = 1, CurrentProperty = 2, MoveNextMethod = 3, DisposeMethod = 4, ElementType = 5 */); + +/** EXPRESSIONS **/ + +expressions( + unique int id: @expr, + int kind: int ref, + int type_id: @type_or_ref ref); + +#keyset[index, parent] +expr_parent( + unique int expr: @expr ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_expr_parent = @attribute | @field | @property | @indexer | @parameter | @directive_if | @directive_elif; + +@top_level_exprorstmt_parent = @top_level_expr_parent | @top_level_stmt_parent; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +expr_parent_top_level( + unique int expr: @expr ref, + int index: int ref, + int parent: @top_level_exprorstmt_parent ref); + +case @expr.kind of +/* literal */ + 1 = @bool_literal_expr +| 2 = @char_literal_expr +| 3 = @decimal_literal_expr +| 4 = @int_literal_expr +| 5 = @long_literal_expr +| 6 = @uint_literal_expr +| 7 = @ulong_literal_expr +| 8 = @float_literal_expr +| 9 = @double_literal_expr +| 10 = @string_literal_expr +| 11 = @null_literal_expr +/* primary & unary */ +| 12 = @this_access_expr +| 13 = @base_access_expr +| 14 = @local_variable_access_expr +| 15 = @parameter_access_expr +| 16 = @field_access_expr +| 17 = @property_access_expr +| 18 = @method_access_expr +| 19 = @event_access_expr +| 20 = @indexer_access_expr +| 21 = @array_access_expr +| 22 = @type_access_expr +| 23 = @typeof_expr +| 24 = @method_invocation_expr +| 25 = @delegate_invocation_expr +| 26 = @operator_invocation_expr +| 27 = @cast_expr +| 28 = @object_creation_expr +| 29 = @explicit_delegate_creation_expr +| 30 = @implicit_delegate_creation_expr +| 31 = @array_creation_expr +| 32 = @default_expr +| 33 = @plus_expr +| 34 = @minus_expr +| 35 = @bit_not_expr +| 36 = @log_not_expr +| 37 = @post_incr_expr +| 38 = @post_decr_expr +| 39 = @pre_incr_expr +| 40 = @pre_decr_expr +/* multiplicative */ +| 41 = @mul_expr +| 42 = @div_expr +| 43 = @rem_expr +/* additive */ +| 44 = @add_expr +| 45 = @sub_expr +/* shift */ +| 46 = @lshift_expr +| 47 = @rshift_expr +/* relational */ +| 48 = @lt_expr +| 49 = @gt_expr +| 50 = @le_expr +| 51 = @ge_expr +/* equality */ +| 52 = @eq_expr +| 53 = @ne_expr +/* logical */ +| 54 = @bit_and_expr +| 55 = @bit_xor_expr +| 56 = @bit_or_expr +| 57 = @log_and_expr +| 58 = @log_or_expr +/* type testing */ +| 59 = @is_expr +| 60 = @as_expr +/* null coalescing */ +| 61 = @null_coalescing_expr +/* conditional */ +| 62 = @conditional_expr +/* assignment */ +| 63 = @simple_assign_expr +| 64 = @assign_add_expr +| 65 = @assign_sub_expr +| 66 = @assign_mul_expr +| 67 = @assign_div_expr +| 68 = @assign_rem_expr +| 69 = @assign_and_expr +| 70 = @assign_xor_expr +| 71 = @assign_or_expr +| 72 = @assign_lshift_expr +| 73 = @assign_rshift_expr +/* more */ +| 74 = @object_init_expr +| 75 = @collection_init_expr +| 76 = @array_init_expr +| 77 = @checked_expr +| 78 = @unchecked_expr +| 79 = @constructor_init_expr +| 80 = @add_event_expr +| 81 = @remove_event_expr +| 82 = @par_expr +| 83 = @local_var_decl_expr +| 84 = @lambda_expr +| 85 = @anonymous_method_expr +| 86 = @namespace_expr +/* dynamic */ +| 92 = @dynamic_element_access_expr +| 93 = @dynamic_member_access_expr +/* unsafe */ +| 100 = @pointer_indirection_expr +| 101 = @address_of_expr +| 102 = @sizeof_expr +/* async */ +| 103 = @await_expr +/* C# 6.0 */ +| 104 = @nameof_expr +| 105 = @interpolated_string_expr +| 106 = @unknown_expr +/* C# 7.0 */ +| 107 = @throw_expr +| 108 = @tuple_expr +| 109 = @local_function_invocation_expr +| 110 = @ref_expr +| 111 = @discard_expr +/* C# 8.0 */ +| 112 = @range_expr +| 113 = @index_expr +| 114 = @switch_expr +| 115 = @recursive_pattern_expr +| 116 = @property_pattern_expr +| 117 = @positional_pattern_expr +| 118 = @switch_case_expr +| 119 = @assign_coalesce_expr +| 120 = @suppress_nullable_warning_expr +| 121 = @namespace_access_expr +/* C# 9.0 */ +| 122 = @lt_pattern_expr +| 123 = @gt_pattern_expr +| 124 = @le_pattern_expr +| 125 = @ge_pattern_expr +| 126 = @not_pattern_expr +| 127 = @and_pattern_expr +| 128 = @or_pattern_expr +| 129 = @function_pointer_invocation_expr +| 130 = @with_expr +/* Preprocessor */ +| 999 = @define_symbol_expr +; + +@switch = @switch_stmt | @switch_expr; +@case = @case_stmt | @switch_case_expr; +@pattern_match = @case | @is_expr; +@unary_pattern_expr = @not_pattern_expr; +@relational_pattern_expr = @gt_pattern_expr | @lt_pattern_expr | @ge_pattern_expr | @le_pattern_expr; +@binary_pattern_expr = @and_pattern_expr | @or_pattern_expr; + +@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr; +@real_literal_expr = @float_literal_expr | @double_literal_expr | @decimal_literal_expr; +@literal_expr = @bool_literal_expr | @char_literal_expr | @integer_literal_expr | @real_literal_expr + | @string_literal_expr | @null_literal_expr; + +@assign_expr = @simple_assign_expr | @assign_op_expr | @local_var_decl_expr; +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr | @assign_event_expr | @assign_coalesce_expr; +@assign_event_expr = @add_event_expr | @remove_event_expr; + +@assign_arith_expr = @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr + | @assign_rem_expr +@assign_bitwise_expr = @assign_and_expr | @assign_or_expr | @assign_xor_expr + | @assign_lshift_expr | @assign_rshift_expr; + +@member_access_expr = @field_access_expr | @property_access_expr | @indexer_access_expr | @event_access_expr + | @method_access_expr | @type_access_expr | @dynamic_member_access_expr; +@access_expr = @member_access_expr | @this_access_expr | @base_access_expr | @assignable_access_expr | @namespace_access_expr; +@element_access_expr = @indexer_access_expr | @array_access_expr | @dynamic_element_access_expr; + +@local_variable_access = @local_variable_access_expr | @local_var_decl_expr; +@local_scope_variable_access_expr = @parameter_access_expr | @local_variable_access; +@variable_access_expr = @local_scope_variable_access_expr | @field_access_expr; + +@assignable_access_expr = @variable_access_expr | @property_access_expr | @element_access_expr + | @event_access_expr | @dynamic_member_access_expr; + +@objectorcollection_init_expr = @object_init_expr | @collection_init_expr; + +@delegate_creation_expr = @explicit_delegate_creation_expr | @implicit_delegate_creation_expr; + +@bin_arith_op_expr = @mul_expr | @div_expr | @rem_expr | @add_expr | @sub_expr; +@incr_op_expr = @pre_incr_expr | @post_incr_expr; +@decr_op_expr = @pre_decr_expr | @post_decr_expr; +@mut_op_expr = @incr_op_expr | @decr_op_expr; +@un_arith_op_expr = @plus_expr | @minus_expr | @mut_op_expr; +@arith_op_expr = @bin_arith_op_expr | @un_arith_op_expr; + +@ternary_log_op_expr = @conditional_expr; +@bin_log_op_expr = @log_and_expr | @log_or_expr | @null_coalescing_expr; +@un_log_op_expr = @log_not_expr; +@log_expr = @un_log_op_expr | @bin_log_op_expr | @ternary_log_op_expr; + +@bin_bit_op_expr = @bit_and_expr | @bit_or_expr | @bit_xor_expr | @lshift_expr + | @rshift_expr; +@un_bit_op_expr = @bit_not_expr; +@bit_expr = @un_bit_op_expr | @bin_bit_op_expr; + +@equality_op_expr = @eq_expr | @ne_expr; +@rel_op_expr = @gt_expr | @lt_expr| @ge_expr | @le_expr; +@comp_expr = @equality_op_expr | @rel_op_expr; + +@op_expr = @assign_expr | @un_op | @bin_op | @ternary_op; + +@ternary_op = @ternary_log_op_expr; +@bin_op = @bin_arith_op_expr | @bin_log_op_expr | @bin_bit_op_expr | @comp_expr; +@un_op = @un_arith_op_expr | @un_log_op_expr | @un_bit_op_expr | @sizeof_expr + | @pointer_indirection_expr | @address_of_expr; + +@anonymous_function_expr = @lambda_expr | @anonymous_method_expr; + +@call = @method_invocation_expr | @constructor_init_expr | @operator_invocation_expr + | @delegate_invocation_expr | @object_creation_expr | @call_access_expr + | @local_function_invocation_expr | @function_pointer_invocation_expr; + +@call_access_expr = @property_access_expr | @event_access_expr | @indexer_access_expr; + +@late_bindable_expr = @dynamic_element_access_expr | @dynamic_member_access_expr + | @object_creation_expr | @method_invocation_expr | @operator_invocation_expr; + +@throw_element = @throw_expr | @throw_stmt; + +@implicitly_typeable_object_creation_expr = @object_creation_expr | @explicit_delegate_creation_expr; + +implicitly_typed_array_creation( + unique int id: @array_creation_expr ref); + +explicitly_sized_array_creation( + unique int id: @array_creation_expr ref); + +stackalloc_array_creation( + unique int id: @array_creation_expr ref); + +implicitly_typed_object_creation( + unique int id: @implicitly_typeable_object_creation_expr ref); + +mutator_invocation_mode( + unique int id: @operator_invocation_expr ref, + int mode: int ref /* prefix = 1, postfix = 2*/); + +expr_compiler_generated( + unique int id: @expr ref); + +expr_value( + unique int id: @expr ref, + string value: string ref); + +expr_call( + unique int caller_id: @expr ref, + int target_id: @callable ref); + +expr_access( + unique int accesser_id: @access_expr ref, + int target_id: @accessible ref); + +@accessible = @method | @assignable | @local_function | @namespace; + +expr_location( + unique int id: @expr ref, + int loc: @location ref); + +dynamic_member_name( + unique int id: @late_bindable_expr ref, + string name: string ref); + +@qualifiable_expr = @member_access_expr + | @method_invocation_expr + | @element_access_expr; + +conditional_access( + unique int id: @qualifiable_expr ref); + +expr_argument( + unique int id: @expr ref, + int mode: int ref); + /* mode is the same as params: value = 0, ref = 1, out = 2 */ + +expr_argument_name( + unique int id: @expr ref, + string name: string ref); + +/** CONTROL/DATA FLOW **/ + +@control_flow_element = @stmt | @expr; + +/* XML Files */ + +xmlEncoding ( + unique int id: @file ref, + string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* Comments */ + +commentline( + unique int id: @commentline, + int kind: int ref, + string text: string ref, + string rawtext: string ref); + +case @commentline.kind of + 0 = @singlelinecomment +| 1 = @xmldoccomment +| 2 = @multilinecomment; + +commentline_location( + unique int id: @commentline ref, + int loc: @location ref); + +commentblock( + unique int id : @commentblock); + +commentblock_location( + unique int id: @commentblock ref, + int loc: @location ref); + +commentblock_binding( + int id: @commentblock ref, + int entity: @element ref, + int bindtype: int ref); /* 0: Parent, 1: Best, 2: Before, 3: After */ + +commentblock_child( + int id: @commentblock ref, + int commentline: @commentline ref, + int index: int ref); + +/* ASP.NET */ + +case @asp_element.kind of + 0=@asp_close_tag +| 1=@asp_code +| 2=@asp_comment +| 3=@asp_data_binding +| 4=@asp_directive +| 5=@asp_open_tag +| 6=@asp_quoted_string +| 7=@asp_text +| 8=@asp_xml_directive; + +@asp_attribute = @asp_code | @asp_data_binding | @asp_quoted_string; + +asp_elements( + unique int id: @asp_element, + int kind: int ref, + int loc: @location ref); + +asp_comment_server(unique int comment: @asp_comment ref); +asp_code_inline(unique int code: @asp_code ref); +asp_directive_attribute( + int directive: @asp_directive ref, + int index: int ref, + string name: string ref, + int value: @asp_quoted_string ref); +asp_directive_name( + unique int directive: @asp_directive ref, + string name: string ref); +asp_element_body( + unique int element: @asp_element ref, + string body: string ref); +asp_tag_attribute( + int tag: @asp_open_tag ref, + int index: int ref, + string name: string ref, + int attribute: @asp_attribute ref); +asp_tag_name( + unique int tag: @asp_open_tag ref, + string name: string ref); +asp_tag_isempty(int tag: @asp_open_tag ref); + +/* Common Intermediate Language - CIL */ + +case @cil_instruction.opcode of + 0 = @cil_nop +| 1 = @cil_break +| 2 = @cil_ldarg_0 +| 3 = @cil_ldarg_1 +| 4 = @cil_ldarg_2 +| 5 = @cil_ldarg_3 +| 6 = @cil_ldloc_0 +| 7 = @cil_ldloc_1 +| 8 = @cil_ldloc_2 +| 9 = @cil_ldloc_3 +| 10 = @cil_stloc_0 +| 11 = @cil_stloc_1 +| 12 = @cil_stloc_2 +| 13 = @cil_stloc_3 +| 14 = @cil_ldarg_s +| 15 = @cil_ldarga_s +| 16 = @cil_starg_s +| 17 = @cil_ldloc_s +| 18 = @cil_ldloca_s +| 19 = @cil_stloc_s +| 20 = @cil_ldnull +| 21 = @cil_ldc_i4_m1 +| 22 = @cil_ldc_i4_0 +| 23 = @cil_ldc_i4_1 +| 24 = @cil_ldc_i4_2 +| 25 = @cil_ldc_i4_3 +| 26 = @cil_ldc_i4_4 +| 27 = @cil_ldc_i4_5 +| 28 = @cil_ldc_i4_6 +| 29 = @cil_ldc_i4_7 +| 30 = @cil_ldc_i4_8 +| 31 = @cil_ldc_i4_s +| 32 = @cil_ldc_i4 +| 33 = @cil_ldc_i8 +| 34 = @cil_ldc_r4 +| 35 = @cil_ldc_r8 +| 37 = @cil_dup +| 38 = @cil_pop +| 39 = @cil_jmp +| 40 = @cil_call +| 41 = @cil_calli +| 42 = @cil_ret +| 43 = @cil_br_s +| 44 = @cil_brfalse_s +| 45 = @cil_brtrue_s +| 46 = @cil_beq_s +| 47 = @cil_bge_s +| 48 = @cil_bgt_s +| 49 = @cil_ble_s +| 50 = @cil_blt_s +| 51 = @cil_bne_un_s +| 52 = @cil_bge_un_s +| 53 = @cil_bgt_un_s +| 54 = @cil_ble_un_s +| 55 = @cil_blt_un_s +| 56 = @cil_br +| 57 = @cil_brfalse +| 58 = @cil_brtrue +| 59 = @cil_beq +| 60 = @cil_bge +| 61 = @cil_bgt +| 62 = @cil_ble +| 63 = @cil_blt +| 64 = @cil_bne_un +| 65 = @cil_bge_un +| 66 = @cil_bgt_un +| 67 = @cil_ble_un +| 68 = @cil_blt_un +| 69 = @cil_switch +| 70 = @cil_ldind_i1 +| 71 = @cil_ldind_u1 +| 72 = @cil_ldind_i2 +| 73 = @cil_ldind_u2 +| 74 = @cil_ldind_i4 +| 75 = @cil_ldind_u4 +| 76 = @cil_ldind_i8 +| 77 = @cil_ldind_i +| 78 = @cil_ldind_r4 +| 79 = @cil_ldind_r8 +| 80 = @cil_ldind_ref +| 81 = @cil_stind_ref +| 82 = @cil_stind_i1 +| 83 = @cil_stind_i2 +| 84 = @cil_stind_i4 +| 85 = @cil_stind_i8 +| 86 = @cil_stind_r4 +| 87 = @cil_stind_r8 +| 88 = @cil_add +| 89 = @cil_sub +| 90 = @cil_mul +| 91 = @cil_div +| 92 = @cil_div_un +| 93 = @cil_rem +| 94 = @cil_rem_un +| 95 = @cil_and +| 96 = @cil_or +| 97 = @cil_xor +| 98 = @cil_shl +| 99 = @cil_shr +| 100 = @cil_shr_un +| 101 = @cil_neg +| 102 = @cil_not +| 103 = @cil_conv_i1 +| 104 = @cil_conv_i2 +| 105 = @cil_conv_i4 +| 106 = @cil_conv_i8 +| 107 = @cil_conv_r4 +| 108 = @cil_conv_r8 +| 109 = @cil_conv_u4 +| 110 = @cil_conv_u8 +| 111 = @cil_callvirt +| 112 = @cil_cpobj +| 113 = @cil_ldobj +| 114 = @cil_ldstr +| 115 = @cil_newobj +| 116 = @cil_castclass +| 117 = @cil_isinst +| 118 = @cil_conv_r_un +| 121 = @cil_unbox +| 122 = @cil_throw +| 123 = @cil_ldfld +| 124 = @cil_ldflda +| 125 = @cil_stfld +| 126 = @cil_ldsfld +| 127 = @cil_ldsflda +| 128 = @cil_stsfld +| 129 = @cil_stobj +| 130 = @cil_conv_ovf_i1_un +| 131 = @cil_conv_ovf_i2_un +| 132 = @cil_conv_ovf_i4_un +| 133 = @cil_conv_ovf_i8_un +| 134 = @cil_conv_ovf_u1_un +| 135 = @cil_conv_ovf_u2_un +| 136 = @cil_conv_ovf_u4_un +| 137 = @cil_conv_ovf_u8_un +| 138 = @cil_conv_ovf_i_un +| 139 = @cil_conv_ovf_u_un +| 140 = @cil_box +| 141 = @cil_newarr +| 142 = @cil_ldlen +| 143 = @cil_ldelema +| 144 = @cil_ldelem_i1 +| 145 = @cil_ldelem_u1 +| 146 = @cil_ldelem_i2 +| 147 = @cil_ldelem_u2 +| 148 = @cil_ldelem_i4 +| 149 = @cil_ldelem_u4 +| 150 = @cil_ldelem_i8 +| 151 = @cil_ldelem_i +| 152 = @cil_ldelem_r4 +| 153 = @cil_ldelem_r8 +| 154 = @cil_ldelem_ref +| 155 = @cil_stelem_i +| 156 = @cil_stelem_i1 +| 157 = @cil_stelem_i2 +| 158 = @cil_stelem_i4 +| 159 = @cil_stelem_i8 +| 160 = @cil_stelem_r4 +| 161 = @cil_stelem_r8 +| 162 = @cil_stelem_ref +| 163 = @cil_ldelem +| 164 = @cil_stelem +| 165 = @cil_unbox_any +| 179 = @cil_conv_ovf_i1 +| 180 = @cil_conv_ovf_u1 +| 181 = @cil_conv_ovf_i2 +| 182 = @cil_conv_ovf_u2 +| 183 = @cil_conv_ovf_i4 +| 184 = @cil_conv_ovf_u4 +| 185 = @cil_conv_ovf_i8 +| 186 = @cil_conv_ovf_u8 +| 194 = @cil_refanyval +| 195 = @cil_ckinfinite +| 198 = @cil_mkrefany +| 208 = @cil_ldtoken +| 209 = @cil_conv_u2 +| 210 = @cil_conv_u1 +| 211 = @cil_conv_i +| 212 = @cil_conv_ovf_i +| 213 = @cil_conv_ovf_u +| 214 = @cil_add_ovf +| 215 = @cil_add_ovf_un +| 216 = @cil_mul_ovf +| 217 = @cil_mul_ovf_un +| 218 = @cil_sub_ovf +| 219 = @cil_sub_ovf_un +| 220 = @cil_endfinally +| 221 = @cil_leave +| 222 = @cil_leave_s +| 223 = @cil_stind_i +| 224 = @cil_conv_u +| 65024 = @cil_arglist +| 65025 = @cil_ceq +| 65026 = @cil_cgt +| 65027 = @cil_cgt_un +| 65028 = @cil_clt +| 65029 = @cil_clt_un +| 65030 = @cil_ldftn +| 65031 = @cil_ldvirtftn +| 65033 = @cil_ldarg +| 65034 = @cil_ldarga +| 65035 = @cil_starg +| 65036 = @cil_ldloc +| 65037 = @cil_ldloca +| 65038 = @cil_stloc +| 65039 = @cil_localloc +| 65041 = @cil_endfilter +| 65042 = @cil_unaligned +| 65043 = @cil_volatile +| 65044 = @cil_tail +| 65045 = @cil_initobj +| 65046 = @cil_constrained +| 65047 = @cil_cpblk +| 65048 = @cil_initblk +| 65050 = @cil_rethrow +| 65052 = @cil_sizeof +| 65053 = @cil_refanytype +| 65054 = @cil_readonly +; + +// CIL ignored instructions + +@cil_ignore = @cil_nop | @cil_break | @cil_volatile | @cil_unaligned; + +// CIL local/parameter/field access + +@cil_ldarg_any = @cil_ldarg_0 | @cil_ldarg_1 | @cil_ldarg_2 | @cil_ldarg_3 | @cil_ldarg_s | @cil_ldarga_s | @cil_ldarg | @cil_ldarga; +@cil_starg_any = @cil_starg | @cil_starg_s; + +@cil_ldloc_any = @cil_ldloc_0 | @cil_ldloc_1 | @cil_ldloc_2 | @cil_ldloc_3 | @cil_ldloc_s | @cil_ldloca_s | @cil_ldloc | @cil_ldloca; +@cil_stloc_any = @cil_stloc_0 | @cil_stloc_1 | @cil_stloc_2 | @cil_stloc_3 | @cil_stloc_s | @cil_stloc; + +@cil_ldfld_any = @cil_ldfld | @cil_ldsfld | @cil_ldsflda | @cil_ldflda; +@cil_stfld_any = @cil_stfld | @cil_stsfld; + +@cil_local_access = @cil_stloc_any | @cil_ldloc_any; +@cil_arg_access = @cil_starg_any | @cil_ldarg_any; +@cil_read_access = @cil_ldloc_any | @cil_ldarg_any | @cil_ldfld_any; +@cil_write_access = @cil_stloc_any | @cil_starg_any | @cil_stfld_any; + +@cil_stack_access = @cil_local_access | @cil_arg_access; +@cil_field_access = @cil_ldfld_any | @cil_stfld_any; + +@cil_access = @cil_read_access | @cil_write_access; + +// CIL constant/literal instructions + +@cil_ldc_i = @cil_ldc_i4_any | @cil_ldc_i8; + +@cil_ldc_i4_any = @cil_ldc_i4_m1 | @cil_ldc_i4_0 | @cil_ldc_i4_1 | @cil_ldc_i4_2 | @cil_ldc_i4_3 | + @cil_ldc_i4_4 | @cil_ldc_i4_5 | @cil_ldc_i4_6 | @cil_ldc_i4_7 | @cil_ldc_i4_8 | @cil_ldc_i4_s | @cil_ldc_i4; + +@cil_ldc_r = @cil_ldc_r4 | @cil_ldc_r8; + +@cil_literal = @cil_ldnull | @cil_ldc_i | @cil_ldc_r | @cil_ldstr; + +// Control flow + +@cil_conditional_jump = @cil_binary_jump | @cil_unary_jump; +@cil_binary_jump = @cil_beq_s | @cil_bge_s | @cil_bgt_s | @cil_ble_s | @cil_blt_s | + @cil_bne_un_s | @cil_bge_un_s | @cil_bgt_un_s | @cil_ble_un_s | @cil_blt_un_s | + @cil_beq | @cil_bge | @cil_bgt | @cil_ble | @cil_blt | + @cil_bne_un | @cil_bge_un | @cil_bgt_un | @cil_ble_un | @cil_blt_un; +@cil_unary_jump = @cil_brfalse_s | @cil_brtrue_s | @cil_brfalse | @cil_brtrue | @cil_switch; +@cil_unconditional_jump = @cil_br | @cil_br_s | @cil_leave_any; +@cil_leave_any = @cil_leave | @cil_leave_s; +@cil_jump = @cil_unconditional_jump | @cil_conditional_jump; + +// CIL call instructions + +@cil_call_any = @cil_jmp | @cil_call | @cil_calli | @cil_tail | @cil_callvirt | @cil_newobj; + +// CIL expression instructions + +@cil_expr = @cil_literal | @cil_binary_expr | @cil_unary_expr | @cil_call_any | @cil_read_access | + @cil_newarr | @cil_ldtoken | @cil_sizeof | + @cil_ldftn | @cil_ldvirtftn | @cil_localloc | @cil_mkrefany | @cil_refanytype | @cil_arglist | @cil_dup; + +@cil_unary_expr = + @cil_conversion_operation | @cil_unary_arithmetic_operation | @cil_unary_bitwise_operation| + @cil_ldlen | @cil_isinst | @cil_box | @cil_ldobj | @cil_castclass | @cil_unbox_any | + @cil_ldind | @cil_unbox; + +@cil_conversion_operation = + @cil_conv_i1 | @cil_conv_i2 | @cil_conv_i4 | @cil_conv_i8 | + @cil_conv_u1 | @cil_conv_u2 | @cil_conv_u4 | @cil_conv_u8 | + @cil_conv_ovf_i | @cil_conv_ovf_i_un | @cil_conv_ovf_i1 | @cil_conv_ovf_i1_un | + @cil_conv_ovf_i2 | @cil_conv_ovf_i2_un | @cil_conv_ovf_i4 | @cil_conv_ovf_i4_un | + @cil_conv_ovf_i8 | @cil_conv_ovf_i8_un | @cil_conv_ovf_u | @cil_conv_ovf_u_un | + @cil_conv_ovf_u1 | @cil_conv_ovf_u1_un | @cil_conv_ovf_u2 | @cil_conv_ovf_u2_un | + @cil_conv_ovf_u4 | @cil_conv_ovf_u4_un | @cil_conv_ovf_u8 | @cil_conv_ovf_u8_un | + @cil_conv_r4 | @cil_conv_r8 | @cil_conv_ovf_u2 | @cil_conv_ovf_u2_un | + @cil_conv_i | @cil_conv_u | @cil_conv_r_un; + +@cil_ldind = @cil_ldind_i | @cil_ldind_i1 | @cil_ldind_i2 | @cil_ldind_i4 | @cil_ldind_i8 | + @cil_ldind_r4 | @cil_ldind_r8 | @cil_ldind_ref | @cil_ldind_u1 | @cil_ldind_u2 | @cil_ldind_u4; + +@cil_stind = @cil_stind_i | @cil_stind_i1 | @cil_stind_i2 | @cil_stind_i4 | @cil_stind_i8 | + @cil_stind_r4 | @cil_stind_r8 | @cil_stind_ref; + +@cil_bitwise_operation = @cil_binary_bitwise_operation | @cil_unary_bitwise_operation; + +@cil_binary_bitwise_operation = @cil_and | @cil_or | @cil_xor | @cil_shr | @cil_shr | @cil_shr_un | @cil_shl; + +@cil_binary_arithmetic_operation = @cil_add | @cil_sub | @cil_mul | @cil_div | @cil_div_un | + @cil_rem | @cil_rem_un | @cil_add_ovf | @cil_add_ovf_un | @cil_mul_ovf | @cil_mul_ovf_un | + @cil_sub_ovf | @cil_sub_ovf_un; + +@cil_unary_bitwise_operation = @cil_not; + +@cil_binary_expr = @cil_binary_arithmetic_operation | @cil_binary_bitwise_operation | @cil_read_array | @cil_comparison_operation; + +@cil_unary_arithmetic_operation = @cil_neg; + +@cil_comparison_operation = @cil_cgt_un | @cil_ceq | @cil_cgt | @cil_clt | @cil_clt_un; + +// Elements that retrieve an address of something +@cil_read_ref = @cil_ldloca_s | @cil_ldarga_s | @cil_ldflda | @cil_ldsflda | @cil_ldelema; + +// CIL array instructions + +@cil_read_array = + @cil_ldelem | @cil_ldelema | @cil_ldelem_i1 | @cil_ldelem_ref | @cil_ldelem_i | + @cil_ldelem_i1 | @cil_ldelem_i2 | @cil_ldelem_i4 | @cil_ldelem_i8 | @cil_ldelem_r4 | + @cil_ldelem_r8 | @cil_ldelem_u1 | @cil_ldelem_u2 | @cil_ldelem_u4; + +@cil_write_array = @cil_stelem | @cil_stelem_ref | + @cil_stelem_i | @cil_stelem_i1 | @cil_stelem_i2 | @cil_stelem_i4 | @cil_stelem_i8 | + @cil_stelem_r4 | @cil_stelem_r8; + +@cil_throw_any = @cil_throw | @cil_rethrow; + +#keyset[impl, index] +cil_instruction( + unique int id: @cil_instruction, + int opcode: int ref, + int index: int ref, + int impl: @cil_method_implementation ref); + +cil_jump( + unique int instruction: @cil_jump ref, + int target: @cil_instruction ref); + +cil_access( + unique int instruction: @cil_instruction ref, + int target: @cil_accessible ref); + +cil_value( + unique int instruction: @cil_literal ref, + string value: string ref); + +#keyset[instruction, index] +cil_switch( + int instruction: @cil_switch ref, + int index: int ref, + int target: @cil_instruction ref); + +cil_instruction_location( + unique int id: @cil_instruction ref, + int loc: @location ref); + +cil_type_location( + int id: @cil_type ref, + int loc: @location ref); + +cil_method_location( + int id: @cil_method ref, + int loc: @location ref); + +@cil_namespace = @namespace; + +@cil_type_container = @cil_type | @cil_namespace | @cil_method; + +case @cil_type.kind of + 0 = @cil_valueorreftype +| 1 = @cil_typeparameter +| 2 = @cil_array_type +| 3 = @cil_pointer_type +| 4 = @cil_function_pointer_type +; + +cil_type( + unique int id: @cil_type, + string name: string ref, + int kind: int ref, + int parent: @cil_type_container ref, + int sourceDecl: @cil_type ref); + +cil_pointer_type( + unique int id: @cil_pointer_type ref, + int pointee: @cil_type ref); + +cil_array_type( + unique int id: @cil_array_type ref, + int element_type: @cil_type ref, + int rank: int ref); + +cil_function_pointer_return_type( + unique int id: @cil_function_pointer_type ref, + int return_type: @cil_type ref); + +cil_method( + unique int id: @cil_method, + string name: string ref, + int parent: @cil_type ref, + int return_type: @cil_type ref); + +cil_method_source_declaration( + unique int method: @cil_method ref, + int source: @cil_method ref); + +cil_method_implementation( + unique int id: @cil_method_implementation, + int method: @cil_method ref, + int location: @assembly ref); + +cil_implements( + int id: @cil_method ref, + int decl: @cil_method ref); + +#keyset[parent, name] +cil_field( + unique int id: @cil_field, + int parent: @cil_type ref, + string name: string ref, + int field_type: @cil_type ref); + +@cil_element = @cil_instruction | @cil_declaration | @cil_handler | @cil_attribute | @cil_namespace; +@cil_named_element = @cil_declaration | @cil_namespace; +@cil_declaration = @cil_variable | @cil_method | @cil_type | @cil_member; +@cil_accessible = @cil_declaration; +@cil_variable = @cil_field | @cil_stack_variable; +@cil_stack_variable = @cil_local_variable | @cil_parameter; +@cil_member = @cil_method | @cil_type | @cil_field | @cil_property | @cil_event; +@cil_custom_modifier_receiver = @cil_method | @cil_property | @cil_parameter | @cil_field | @cil_function_pointer_type; +@cil_parameterizable = @cil_method | @cil_function_pointer_type; +@cil_has_type_annotation = @cil_stack_variable | @cil_property | @cil_method | @cil_function_pointer_type; + +#keyset[parameterizable, index] +cil_parameter( + unique int id: @cil_parameter, + int parameterizable: @cil_parameterizable ref, + int index: int ref, + int param_type: @cil_type ref); + +cil_parameter_in(unique int id: @cil_parameter ref); +cil_parameter_out(unique int id: @cil_parameter ref); + +cil_setter(unique int prop: @cil_property ref, + int method: @cil_method ref); + +#keyset[id, modifier] +cil_custom_modifiers( + int id: @cil_custom_modifier_receiver ref, + int modifier: @cil_type ref, + int kind: int ref); // modreq: 1, modopt: 0 + +cil_type_annotation( + int id: @cil_has_type_annotation ref, + int annotation: int ref); + +cil_getter(unique int prop: @cil_property ref, + int method: @cil_method ref); + +cil_adder(unique int event: @cil_event ref, + int method: @cil_method ref); + +cil_remover(unique int event: @cil_event ref, int method: @cil_method ref); + +cil_raiser(unique int event: @cil_event ref, int method: @cil_method ref); + +cil_property( + unique int id: @cil_property, + int parent: @cil_type ref, + string name: string ref, + int property_type: @cil_type ref); + +#keyset[parent, name] +cil_event(unique int id: @cil_event, + int parent: @cil_type ref, + string name: string ref, + int event_type: @cil_type ref); + +#keyset[impl, index] +cil_local_variable( + unique int id: @cil_local_variable, + int impl: @cil_method_implementation ref, + int index: int ref, + int var_type: @cil_type ref); + +cil_function_pointer_calling_conventions( + int id: @cil_function_pointer_type ref, + int kind: int ref); + +// CIL handlers (exception handlers etc). + +case @cil_handler.kind of + 0 = @cil_catch_handler +| 1 = @cil_filter_handler +| 2 = @cil_finally_handler +| 4 = @cil_fault_handler +; + +#keyset[impl, index] +cil_handler( + unique int id: @cil_handler, + int impl: @cil_method_implementation ref, + int index: int ref, + int kind: int ref, + int try_start: @cil_instruction ref, + int try_end: @cil_instruction ref, + int handler_start: @cil_instruction ref); + +cil_handler_filter( + unique int id: @cil_handler ref, + int filter_start: @cil_instruction ref); + +cil_handler_type( + unique int id: @cil_handler ref, + int catch_type: @cil_type ref); + +@cil_controlflow_node = @cil_entry_point | @cil_instruction; + +@cil_entry_point = @cil_method_implementation | @cil_handler; + +@cil_dataflow_node = @cil_instruction | @cil_variable | @cil_method; + +cil_method_stack_size( + unique int method: @cil_method_implementation ref, + int size: int ref); + +// CIL modifiers + +cil_public(int id: @cil_member ref); +cil_private(int id: @cil_member ref); +cil_protected(int id: @cil_member ref); +cil_internal(int id: @cil_member ref); +cil_static(int id: @cil_member ref); +cil_sealed(int id: @cil_member ref); +cil_virtual(int id: @cil_method ref); +cil_abstract(int id: @cil_member ref); +cil_class(int id: @cil_type ref); +cil_interface(int id: @cil_type ref); +cil_security(int id: @cil_member ref); +cil_requiresecobject(int id: @cil_method ref); +cil_specialname(int id: @cil_method ref); +cil_newslot(int id: @cil_method ref); + +cil_base_class(unique int id: @cil_type ref, int base: @cil_type ref); +cil_base_interface(int id: @cil_type ref, int base: @cil_type ref); +cil_enum_underlying_type(unique int id: @cil_type ref, int underlying: @cil_type ref); + +#keyset[unbound, index] +cil_type_parameter( + int unbound: @cil_member ref, + int index: int ref, + int param: @cil_typeparameter ref); + +#keyset[bound, index] +cil_type_argument( + int bound: @cil_member ref, + int index: int ref, + int t: @cil_type ref); + +// CIL type parameter constraints + +cil_typeparam_covariant(int tp: @cil_typeparameter ref); +cil_typeparam_contravariant(int tp: @cil_typeparameter ref); +cil_typeparam_class(int tp: @cil_typeparameter ref); +cil_typeparam_struct(int tp: @cil_typeparameter ref); +cil_typeparam_new(int tp: @cil_typeparameter ref); +cil_typeparam_constraint(int tp: @cil_typeparameter ref, int supertype: @cil_type ref); + +// CIL attributes + +cil_attribute( + unique int attributeid: @cil_attribute, + int element: @cil_declaration ref, + int constructor: @cil_method ref); + +#keyset[attribute_id, param] +cil_attribute_named_argument( + int attribute_id: @cil_attribute ref, + string param: string ref, + string value: string ref); + +#keyset[attribute_id, index] +cil_attribute_positional_argument( + int attribute_id: @cil_attribute ref, + int index: int ref, + string value: string ref); + + +// Common .Net data model covering both C# and CIL + +// Common elements +@dotnet_element = @element | @cil_element; +@dotnet_named_element = @named_element | @cil_named_element; +@dotnet_callable = @callable | @cil_method; +@dotnet_variable = @variable | @cil_variable; +@dotnet_field = @field | @cil_field; +@dotnet_parameter = @parameter | @cil_parameter; +@dotnet_declaration = @declaration | @cil_declaration; +@dotnet_member = @member | @cil_member; +@dotnet_event = @event | @cil_event; +@dotnet_property = @property | @cil_property | @indexer; +@dotnet_parameterizable = @parameterizable | @cil_parameterizable; + +// Common types +@dotnet_type = @type | @cil_type; +@dotnet_call = @call | @cil_call_any; +@dotnet_throw = @throw_element | @cil_throw_any; +@dotnet_valueorreftype = @cil_valueorreftype | @value_or_ref_type | @cil_array_type | @void_type; +@dotnet_typeparameter = @type_parameter | @cil_typeparameter; +@dotnet_array_type = @array_type | @cil_array_type; +@dotnet_pointer_type = @pointer_type | @cil_pointer_type; +@dotnet_type_parameter = @type_parameter | @cil_typeparameter; +@dotnet_generic = @dotnet_valueorreftype | @dotnet_callable; + +// Attributes +@dotnet_attribute = @attribute | @cil_attribute; + +// Expressions +@dotnet_expr = @expr | @cil_expr; + +// Literals +@dotnet_literal = @literal_expr | @cil_literal; +@dotnet_string_literal = @string_literal_expr | @cil_ldstr; +@dotnet_int_literal = @integer_literal_expr | @cil_ldc_i; +@dotnet_float_literal = @float_literal_expr | @cil_ldc_r; +@dotnet_null_literal = @null_literal_expr | @cil_ldnull; + +@metadata_entity = @cil_method | @cil_type | @cil_field | @cil_property | @field | @property | + @callable | @value_or_ref_type | @void_type; + +#keyset[entity, location] +metadata_handle(int entity : @metadata_entity ref, int location: @assembly ref, int handle: int ref) diff --git a/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/semmlecode.csharp.dbscheme b/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/semmlecode.csharp.dbscheme new file mode 100644 index 00000000000..770f844243d --- /dev/null +++ b/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/semmlecode.csharp.dbscheme @@ -0,0 +1,2083 @@ + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * csc f1.cs f2.cs f3.cs + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + unique int id : @compilation, + string cwd : string ref +); + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | --compiler + * 1 | *path to compiler* + * 2 | --cil + * 3 | f1.cs + * 4 | f2.cs + * 5 | f3.cs + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | f1.cs + * 1 | f2.cs + * 2 | f3.cs + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The references used by a compiler invocation. + * If `id` is for the compiler invocation + * + * csc f1.cs f2.cs f3.cs /r:ref1.dll /r:ref2.dll /r:ref3.dll + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | ref1.dll + * 1 | ref2.dll + * 2 | ref3.dll + */ +#keyset[id, num] +compilation_referencing_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +extractor_messages( + unique int id: @extractor_message, + int severity: int ref, + string origin : string ref, + string text : string ref, + string entity : string ref, + int location: @location_default ref, + string stack_trace : string ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +compilation_assembly( + unique int id : @compilation ref, + int assembly: @assembly ref +) + +/* + * External artifacts + */ + +externalDefects( + unique int id: @externalDefect, + string queryPath: string ref, + int location: @location ref, + string message: string ref, + float severity: float ref); + +externalMetrics( + unique int id: @externalMetric, + string queryPath: string ref, + int location: @location ref, + float value: float ref); + +externalData( + int id: @externalDataElement, + string path: string ref, + int column: int ref, + string value: string ref); + +snapshotDate( + unique date snapshotDate: date ref); + +sourceLocationPrefix( + string prefix: string ref); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id: @duplication, + string relativePath: string ref, + int equivClass: int ref); + +similarCode( + unique int id: @similarity, + string relativePath: string ref, + int equivClass: int ref); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id: @duplication_or_similarity ref, + int offset: int ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +/* + * C# dbscheme + */ + +/** ELEMENTS **/ + +@element = @declaration | @stmt | @expr | @modifier | @attribute | @namespace_declaration + | @using_directive | @type_parameter_constraints | @external_element + | @xmllocatable | @asp_element | @namespace | @preprocessor_directive; + +@declaration = @callable | @generic | @assignable | @namespace; + +@named_element = @namespace | @declaration; + +@declaration_with_accessors = @property | @indexer | @event; + +@assignable = @variable | @assignable_with_accessors | @event; + +@assignable_with_accessors = @property | @indexer; + +@external_element = @externalMetric | @externalDefect | @externalDataElement; + +@attributable = @assembly | @field | @parameter | @operator | @method | @constructor + | @destructor | @callable_accessor | @value_or_ref_type | @declaration_with_accessors + | @local_function; + +/** LOCATIONS, ASEMMBLIES, MODULES, FILES and FOLDERS **/ + +@location = @location_default | @assembly; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref); + +locations_mapped( + unique int id: @location_default ref, + int mapped_to: @location_default ref); + +@sourceline = @file | @callable | @xmllocatable; + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref); + +assemblies( + unique int id: @assembly, + int file: @file ref, + string fullname: string ref, + string name: string ref, + string version: string ref); + +/* + fromSource(0) = unknown, + fromSource(1) = from source, + fromSource(2) = from library +*/ +files( + unique int id: @file, + string name: string ref, + string simple: string ref, + string ext: string ref, + int fromSource: int ref); + +folders( + unique int id: @folder, + string name: string ref, + string simple: string ref); + +@container = @folder | @file ; + +containerparent( + int parent: @container ref, + unique int child: @container ref); + +file_extraction_mode( + unique int file: @file ref, + int mode: int ref + /* 0 = normal, 1 = standalone extractor */ + ); + +/** NAMESPACES **/ + +@type_container = @namespace | @type; + +namespaces( + unique int id: @namespace, + string name: string ref); + +namespace_declarations( + unique int id: @namespace_declaration, + int namespace_id: @namespace ref); + +namespace_declaration_location( + unique int id: @namespace_declaration ref, + int loc: @location ref); + +parent_namespace( + unique int child_id: @type_container ref, + int namespace_id: @namespace ref); + +@declaration_or_directive = @namespace_declaration | @type | @using_directive; + +parent_namespace_declaration( + int child_id: @declaration_or_directive ref, // cannot be unique because of partial classes + int namespace_id: @namespace_declaration ref); + +@using_directive = @using_namespace_directive | @using_static_directive; + +using_namespace_directives( + unique int id: @using_namespace_directive, + int namespace_id: @namespace ref); + +using_static_directives( + unique int id: @using_static_directive, + int type_id: @type_or_ref ref); + +using_directive_location( + unique int id: @using_directive ref, + int loc: @location ref); + +@preprocessor_directive = @pragma_warning | @pragma_checksum | @directive_define | @directive_undefine | @directive_warning + | @directive_error | @directive_nullable | @directive_line | @directive_region | @directive_endregion | @directive_if + | @directive_elif | @directive_else | @directive_endif; + +@conditional_directive = @directive_if | @directive_elif; +@branch_directive = @directive_if | @directive_elif | @directive_else; + +directive_ifs( + unique int id: @directive_if, + int branchTaken: int ref, /* 0: false, 1: true */ + int conditionValue: int ref); /* 0: false, 1: true */ + +directive_elifs( + unique int id: @directive_elif, + int branchTaken: int ref, /* 0: false, 1: true */ + int conditionValue: int ref, /* 0: false, 1: true */ + int parent: @directive_if ref, + int index: int ref); + +directive_elses( + unique int id: @directive_else, + int branchTaken: int ref, /* 0: false, 1: true */ + int parent: @directive_if ref, + int index: int ref); + +#keyset[id, start] +directive_endifs( + unique int id: @directive_endif, + unique int start: @directive_if ref); + +directive_define_symbols( + unique int id: @define_symbol_expr ref, + string name: string ref); + +directive_regions( + unique int id: @directive_region, + string name: string ref); + +#keyset[id, start] +directive_endregions( + unique int id: @directive_endregion, + unique int start: @directive_region ref); + +directive_lines( + unique int id: @directive_line, + int kind: int ref); /* 0: default, 1: hidden, 2: numeric */ + +directive_line_value( + unique int id: @directive_line ref, + int line: int ref); + +directive_line_file( + unique int id: @directive_line ref, + int file: @file ref +) + +directive_nullables( + unique int id: @directive_nullable, + int setting: int ref, /* 0: disable, 1: enable, 2: restore */ + int target: int ref); /* 0: none, 1: annotations, 2: warnings */ + +directive_warnings( + unique int id: @directive_warning, + string message: string ref); + +directive_errors( + unique int id: @directive_error, + string message: string ref); + +directive_undefines( + unique int id: @directive_undefine, + string name: string ref); + +directive_defines( + unique int id: @directive_define, + string name: string ref); + +pragma_checksums( + unique int id: @pragma_checksum, + int file: @file ref, + string guid: string ref, + string bytes: string ref); + +pragma_warnings( + unique int id: @pragma_warning, + int kind: int ref /* 0 = disable, 1 = restore */); + +#keyset[id, index] +pragma_warning_error_codes( + int id: @pragma_warning ref, + string errorCode: string ref, + int index: int ref); + +preprocessor_directive_location( + unique int id: @preprocessor_directive ref, + int loc: @location ref); + +preprocessor_directive_compilation( + unique int id: @preprocessor_directive ref, + int compilation: @compilation ref); + +preprocessor_directive_active( + unique int id: @preprocessor_directive ref, + int active: int ref); /* 0: false, 1: true */ + +/** TYPES **/ + +types( + unique int id: @type, + int kind: int ref, + string name: string ref); + +case @type.kind of + 1 = @bool_type +| 2 = @char_type +| 3 = @decimal_type +| 4 = @sbyte_type +| 5 = @short_type +| 6 = @int_type +| 7 = @long_type +| 8 = @byte_type +| 9 = @ushort_type +| 10 = @uint_type +| 11 = @ulong_type +| 12 = @float_type +| 13 = @double_type +| 14 = @enum_type +| 15 = @struct_type +| 17 = @class_type +| 19 = @interface_type +| 20 = @delegate_type +| 21 = @null_type +| 22 = @type_parameter +| 23 = @pointer_type +| 24 = @nullable_type +| 25 = @array_type +| 26 = @void_type +| 27 = @int_ptr_type +| 28 = @uint_ptr_type +| 29 = @dynamic_type +| 30 = @arglist_type +| 31 = @unknown_type +| 32 = @tuple_type +| 33 = @function_pointer_type + ; + +@simple_type = @bool_type | @char_type | @integral_type | @floating_point_type | @decimal_type; +@integral_type = @signed_integral_type | @unsigned_integral_type; +@signed_integral_type = @sbyte_type | @short_type | @int_type | @long_type; +@unsigned_integral_type = @byte_type | @ushort_type | @uint_type | @ulong_type; +@floating_point_type = @float_type | @double_type; +@value_type = @simple_type | @enum_type | @struct_type | @nullable_type | @int_ptr_type + | @uint_ptr_type | @tuple_type; +@ref_type = @class_type | @interface_type | @array_type | @delegate_type | @null_type + | @dynamic_type; +@value_or_ref_type = @value_type | @ref_type; + +typerefs( + unique int id: @typeref, + string name: string ref); + +typeref_type( + int id: @typeref ref, + unique int typeId: @type ref); + +@type_or_ref = @type | @typeref; + +array_element_type( + unique int array: @array_type ref, + int dimension: int ref, + int rank: int ref, + int element: @type_or_ref ref); + +nullable_underlying_type( + unique int nullable: @nullable_type ref, + int underlying: @type_or_ref ref); + +pointer_referent_type( + unique int pointer: @pointer_type ref, + int referent: @type_or_ref ref); + +enum_underlying_type( + unique int enum_id: @enum_type ref, + int underlying_type_id: @type_or_ref ref); + +delegate_return_type( + unique int delegate_id: @delegate_type ref, + int return_type_id: @type_or_ref ref); + +function_pointer_return_type( + unique int function_pointer_id: @function_pointer_type ref, + int return_type_id: @type_or_ref ref); + +extend( + int sub: @type ref, + int super: @type_or_ref ref); + +anonymous_types( + unique int id: @type ref); + +@interface_or_ref = @interface_type | @typeref; + +implement( + int sub: @type ref, + int super: @type_or_ref ref); + +type_location( + int id: @type ref, + int loc: @location ref); + +tuple_underlying_type( + unique int tuple: @tuple_type ref, + int struct: @type_or_ref ref); + +#keyset[tuple, index] +tuple_element( + int tuple: @tuple_type ref, + int index: int ref, + unique int field: @field ref); + +attributes( + unique int id: @attribute, + int type_id: @type_or_ref ref, + int target: @attributable ref); + +attribute_location( + int id: @attribute ref, + int loc: @location ref); + +@type_mention_parent = @element | @type_mention; + +type_mention( + unique int id: @type_mention, + int type_id: @type_or_ref ref, + int parent: @type_mention_parent ref); + +type_mention_location( + unique int id: @type_mention ref, + int loc: @location ref); + +@has_type_annotation = @assignable | @type_parameter | @callable | @expr | @delegate_type | @generic | @function_pointer_type; + +/** + * A direct annotation on an entity, for example `string? x;`. + * + * Annotations: + * 2 = reftype is not annotated "!" + * 3 = reftype is annotated "?" + * 4 = readonly ref type / in parameter + * 5 = ref type parameter, return or local variable + * 6 = out parameter + * + * Note that the annotation depends on the element it annotates. + * @assignable: The annotation is on the type of the assignable, for example the variable type. + * @type_parameter: The annotation is on the reftype constraint + * @callable: The annotation is on the return type + * @array_type: The annotation is on the element type + */ +type_annotation(int id: @has_type_annotation ref, int annotation: int ref); + +nullability(unique int nullability: @nullability, int kind: int ref); + +case @nullability.kind of + 0 = @oblivious +| 1 = @not_annotated +| 2 = @annotated +; + +#keyset[parent, index] +nullability_parent(int nullability: @nullability ref, int index: int ref, int parent: @nullability ref) + +type_nullability(int id: @has_type_annotation ref, int nullability: @nullability ref); + +/** + * The nullable flow state of an expression, as determined by Roslyn. + * 0 = none (default, not populated) + * 1 = not null + * 2 = maybe null + */ +expr_flowstate(unique int id: @expr ref, int state: int ref); + +/** GENERICS **/ + +@generic = @type | @method | @local_function; + +type_parameters( + unique int id: @type_parameter ref, + int index: int ref, + int generic_id: @generic ref, + int variance: int ref /* none = 0, out = 1, in = 2 */); + +#keyset[constructed_id, index] +type_arguments( + int id: @type_or_ref ref, + int index: int ref, + int constructed_id: @generic_or_ref ref); + +@generic_or_ref = @generic | @typeref; + +constructed_generic( + unique int constructed: @generic ref, + int generic: @generic_or_ref ref); + +type_parameter_constraints( + unique int id: @type_parameter_constraints, + int param_id: @type_parameter ref); + +type_parameter_constraints_location( + int id: @type_parameter_constraints ref, + int loc: @location ref); + +general_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int kind: int ref /* class = 1, struct = 2, new = 3 */); + +specific_type_parameter_constraints( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref); + +specific_type_parameter_nullability( + int id: @type_parameter_constraints ref, + int base_id: @type_or_ref ref, + int nullability: @nullability ref); + +/** FUNCTION POINTERS */ + +function_pointer_calling_conventions( + int id: @function_pointer_type ref, + int kind: int ref); + +#keyset[id, index] +has_unmanaged_calling_conventions( + int id: @function_pointer_type ref, + int index: int ref, + int conv_id: @type_or_ref ref); + +/** MODIFIERS */ + +@modifiable = @modifiable_direct | @event_accessor; + +@modifiable_direct = @member | @accessor | @local_function | @anonymous_function_expr; + +modifiers( + unique int id: @modifier, + string name: string ref); + +has_modifiers( + int id: @modifiable_direct ref, + int mod_id: @modifier ref); + +compiler_generated(unique int id: @modifiable_direct ref); + +/** MEMBERS **/ + +@member = @method | @constructor | @destructor | @field | @property | @event | @operator | @indexer | @type; + +@named_exprorstmt = @goto_stmt | @labeled_stmt | @expr; + +@virtualizable = @method | @property | @indexer | @event; + +exprorstmt_name( + unique int parent_id: @named_exprorstmt ref, + string name: string ref); + +nested_types( + unique int id: @type ref, + int declaring_type_id: @type ref, + int unbound_id: @type ref); + +properties( + unique int id: @property, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @property ref); + +property_location( + int id: @property ref, + int loc: @location ref); + +indexers( + unique int id: @indexer, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @indexer ref); + +indexer_location( + int id: @indexer ref, + int loc: @location ref); + +accessors( + unique int id: @accessor, + int kind: int ref, + string name: string ref, + int declaring_member_id: @member ref, + int unbound_id: @accessor ref); + +case @accessor.kind of + 1 = @getter +| 2 = @setter + ; + +init_only_accessors( + unique int id: @accessor ref); + +accessor_location( + int id: @accessor ref, + int loc: @location ref); + +events( + unique int id: @event, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @event ref); + +event_location( + int id: @event ref, + int loc: @location ref); + +event_accessors( + unique int id: @event_accessor, + int kind: int ref, + string name: string ref, + int declaring_event_id: @event ref, + int unbound_id: @event_accessor ref); + +case @event_accessor.kind of + 1 = @add_event_accessor +| 2 = @remove_event_accessor + ; + +event_accessor_location( + int id: @event_accessor ref, + int loc: @location ref); + +operators( + unique int id: @operator, + string name: string ref, + string symbol: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @operator ref); + +operator_location( + int id: @operator ref, + int loc: @location ref); + +constant_value( + int id: @variable ref, + string value: string ref); + +/** CALLABLES **/ + +@callable = @method | @constructor | @destructor | @operator | @callable_accessor | @anonymous_function_expr | @local_function; + +@callable_accessor = @accessor | @event_accessor; + +methods( + unique int id: @method, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @method ref); + +method_location( + int id: @method ref, + int loc: @location ref); + +constructors( + unique int id: @constructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @constructor ref); + +constructor_location( + int id: @constructor ref, + int loc: @location ref); + +destructors( + unique int id: @destructor, + string name: string ref, + int declaring_type_id: @type ref, + int unbound_id: @destructor ref); + +destructor_location( + int id: @destructor ref, + int loc: @location ref); + +overrides( + int id: @callable ref, + int base_id: @callable ref); + +explicitly_implements( + int id: @member ref, + int interface_id: @interface_or_ref ref); + +local_functions( + unique int id: @local_function, + string name: string ref, + int return_type: @type ref, + int unbound_id: @local_function ref); + +local_function_stmts( + unique int fn: @local_function_stmt ref, + int stmt: @local_function ref); + +/** VARIABLES **/ + +@variable = @local_scope_variable | @field; + +@local_scope_variable = @local_variable | @parameter; + +fields( + unique int id: @field, + int kind: int ref, + string name: string ref, + int declaring_type_id: @type ref, + int type_id: @type_or_ref ref, + int unbound_id: @field ref); + +case @field.kind of + 1 = @addressable_field +| 2 = @constant + ; + +field_location( + int id: @field ref, + int loc: @location ref); + +localvars( + unique int id: @local_variable, + int kind: int ref, + string name: string ref, + int implicitly_typed: int ref /* 0 = no, 1 = yes */, + int type_id: @type_or_ref ref, + int parent_id: @local_var_decl_expr ref); + +case @local_variable.kind of + 1 = @addressable_local_variable +| 2 = @local_constant +| 3 = @local_variable_ref + ; + +localvar_location( + unique int id: @local_variable ref, + int loc: @location ref); + +@parameterizable = @callable | @delegate_type | @indexer | @function_pointer_type; + +#keyset[name, parent_id] +#keyset[index, parent_id] +params( + unique int id: @parameter, + string name: string ref, + int type_id: @type_or_ref ref, + int index: int ref, + int mode: int ref, /* value = 0, ref = 1, out = 2, array = 3, this = 4 */ + int parent_id: @parameterizable ref, + int unbound_id: @parameter ref); + +param_location( + int id: @parameter ref, + int loc: @location ref); + +/** STATEMENTS **/ + +@exprorstmt_parent = @control_flow_element | @top_level_exprorstmt_parent; + +statements( + unique int id: @stmt, + int kind: int ref); + +#keyset[index, parent] +stmt_parent( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_stmt_parent = @callable; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +stmt_parent_top_level( + unique int stmt: @stmt ref, + int index: int ref, + int parent: @top_level_stmt_parent ref); + +case @stmt.kind of + 1 = @block_stmt +| 2 = @expr_stmt +| 3 = @if_stmt +| 4 = @switch_stmt +| 5 = @while_stmt +| 6 = @do_stmt +| 7 = @for_stmt +| 8 = @foreach_stmt +| 9 = @break_stmt +| 10 = @continue_stmt +| 11 = @goto_stmt +| 12 = @goto_case_stmt +| 13 = @goto_default_stmt +| 14 = @throw_stmt +| 15 = @return_stmt +| 16 = @yield_stmt +| 17 = @try_stmt +| 18 = @checked_stmt +| 19 = @unchecked_stmt +| 20 = @lock_stmt +| 21 = @using_block_stmt +| 22 = @var_decl_stmt +| 23 = @const_decl_stmt +| 24 = @empty_stmt +| 25 = @unsafe_stmt +| 26 = @fixed_stmt +| 27 = @label_stmt +| 28 = @catch +| 29 = @case_stmt +| 30 = @local_function_stmt +| 31 = @using_decl_stmt + ; + +@using_stmt = @using_block_stmt | @using_decl_stmt; + +@labeled_stmt = @label_stmt | @case; + +@decl_stmt = @var_decl_stmt | @const_decl_stmt | @using_decl_stmt; + +@cond_stmt = @if_stmt | @switch_stmt; + +@loop_stmt = @while_stmt | @do_stmt | @for_stmt | @foreach_stmt; + +@jump_stmt = @break_stmt | @goto_any_stmt | @continue_stmt | @throw_stmt | @return_stmt + | @yield_stmt; + +@goto_any_stmt = @goto_default_stmt | @goto_case_stmt | @goto_stmt; + + +stmt_location( + unique int id: @stmt ref, + int loc: @location ref); + +catch_type( + unique int catch_id: @catch ref, + int type_id: @type_or_ref ref, + int kind: int ref /* explicit = 1, implicit = 2 */); + +foreach_stmt_info( + unique int id: @foreach_stmt ref, + int kind: int ref /* non-async = 1, async = 2 */); + +@foreach_symbol = @method | @property | @type_or_ref; + +#keyset[id, kind] +foreach_stmt_desugar( + int id: @foreach_stmt ref, + int symbol: @foreach_symbol ref, + int kind: int ref /* GetEnumeratorMethod = 1, CurrentProperty = 2, MoveNextMethod = 3, DisposeMethod = 4, ElementType = 5 */); + +/** EXPRESSIONS **/ + +expressions( + unique int id: @expr, + int kind: int ref, + int type_id: @type_or_ref ref); + +#keyset[index, parent] +expr_parent( + unique int expr: @expr ref, + int index: int ref, + int parent: @control_flow_element ref); + +@top_level_expr_parent = @attribute | @field | @property | @indexer | @parameter | @directive_if | @directive_elif; + +@top_level_exprorstmt_parent = @top_level_expr_parent | @top_level_stmt_parent; + +// [index, parent] is not a keyset because the same parent may be compiled multiple times +expr_parent_top_level( + unique int expr: @expr ref, + int index: int ref, + int parent: @top_level_exprorstmt_parent ref); + +case @expr.kind of +/* literal */ + 1 = @bool_literal_expr +| 2 = @char_literal_expr +| 3 = @decimal_literal_expr +| 4 = @int_literal_expr +| 5 = @long_literal_expr +| 6 = @uint_literal_expr +| 7 = @ulong_literal_expr +| 8 = @float_literal_expr +| 9 = @double_literal_expr +| 10 = @string_literal_expr +| 11 = @null_literal_expr +/* primary & unary */ +| 12 = @this_access_expr +| 13 = @base_access_expr +| 14 = @local_variable_access_expr +| 15 = @parameter_access_expr +| 16 = @field_access_expr +| 17 = @property_access_expr +| 18 = @method_access_expr +| 19 = @event_access_expr +| 20 = @indexer_access_expr +| 21 = @array_access_expr +| 22 = @type_access_expr +| 23 = @typeof_expr +| 24 = @method_invocation_expr +| 25 = @delegate_invocation_expr +| 26 = @operator_invocation_expr +| 27 = @cast_expr +| 28 = @object_creation_expr +| 29 = @explicit_delegate_creation_expr +| 30 = @implicit_delegate_creation_expr +| 31 = @array_creation_expr +| 32 = @default_expr +| 33 = @plus_expr +| 34 = @minus_expr +| 35 = @bit_not_expr +| 36 = @log_not_expr +| 37 = @post_incr_expr +| 38 = @post_decr_expr +| 39 = @pre_incr_expr +| 40 = @pre_decr_expr +/* multiplicative */ +| 41 = @mul_expr +| 42 = @div_expr +| 43 = @rem_expr +/* additive */ +| 44 = @add_expr +| 45 = @sub_expr +/* shift */ +| 46 = @lshift_expr +| 47 = @rshift_expr +/* relational */ +| 48 = @lt_expr +| 49 = @gt_expr +| 50 = @le_expr +| 51 = @ge_expr +/* equality */ +| 52 = @eq_expr +| 53 = @ne_expr +/* logical */ +| 54 = @bit_and_expr +| 55 = @bit_xor_expr +| 56 = @bit_or_expr +| 57 = @log_and_expr +| 58 = @log_or_expr +/* type testing */ +| 59 = @is_expr +| 60 = @as_expr +/* null coalescing */ +| 61 = @null_coalescing_expr +/* conditional */ +| 62 = @conditional_expr +/* assignment */ +| 63 = @simple_assign_expr +| 64 = @assign_add_expr +| 65 = @assign_sub_expr +| 66 = @assign_mul_expr +| 67 = @assign_div_expr +| 68 = @assign_rem_expr +| 69 = @assign_and_expr +| 70 = @assign_xor_expr +| 71 = @assign_or_expr +| 72 = @assign_lshift_expr +| 73 = @assign_rshift_expr +/* more */ +| 74 = @object_init_expr +| 75 = @collection_init_expr +| 76 = @array_init_expr +| 77 = @checked_expr +| 78 = @unchecked_expr +| 79 = @constructor_init_expr +| 80 = @add_event_expr +| 81 = @remove_event_expr +| 82 = @par_expr +| 83 = @local_var_decl_expr +| 84 = @lambda_expr +| 85 = @anonymous_method_expr +| 86 = @namespace_expr +/* dynamic */ +| 92 = @dynamic_element_access_expr +| 93 = @dynamic_member_access_expr +/* unsafe */ +| 100 = @pointer_indirection_expr +| 101 = @address_of_expr +| 102 = @sizeof_expr +/* async */ +| 103 = @await_expr +/* C# 6.0 */ +| 104 = @nameof_expr +| 105 = @interpolated_string_expr +| 106 = @unknown_expr +/* C# 7.0 */ +| 107 = @throw_expr +| 108 = @tuple_expr +| 109 = @local_function_invocation_expr +| 110 = @ref_expr +| 111 = @discard_expr +/* C# 8.0 */ +| 112 = @range_expr +| 113 = @index_expr +| 114 = @switch_expr +| 115 = @recursive_pattern_expr +| 116 = @property_pattern_expr +| 117 = @positional_pattern_expr +| 118 = @switch_case_expr +| 119 = @assign_coalesce_expr +| 120 = @suppress_nullable_warning_expr +| 121 = @namespace_access_expr +/* C# 9.0 */ +| 122 = @lt_pattern_expr +| 123 = @gt_pattern_expr +| 124 = @le_pattern_expr +| 125 = @ge_pattern_expr +| 126 = @not_pattern_expr +| 127 = @and_pattern_expr +| 128 = @or_pattern_expr +| 129 = @function_pointer_invocation_expr +| 130 = @with_expr +/* Preprocessor */ +| 999 = @define_symbol_expr +; + +@switch = @switch_stmt | @switch_expr; +@case = @case_stmt | @switch_case_expr; +@pattern_match = @case | @is_expr; +@unary_pattern_expr = @not_pattern_expr; +@relational_pattern_expr = @gt_pattern_expr | @lt_pattern_expr | @ge_pattern_expr | @le_pattern_expr; +@binary_pattern_expr = @and_pattern_expr | @or_pattern_expr; + +@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr; +@real_literal_expr = @float_literal_expr | @double_literal_expr | @decimal_literal_expr; +@literal_expr = @bool_literal_expr | @char_literal_expr | @integer_literal_expr | @real_literal_expr + | @string_literal_expr | @null_literal_expr; + +@assign_expr = @simple_assign_expr | @assign_op_expr | @local_var_decl_expr; +@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr | @assign_event_expr | @assign_coalesce_expr; +@assign_event_expr = @add_event_expr | @remove_event_expr; + +@assign_arith_expr = @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr + | @assign_rem_expr +@assign_bitwise_expr = @assign_and_expr | @assign_or_expr | @assign_xor_expr + | @assign_lshift_expr | @assign_rshift_expr; + +@member_access_expr = @field_access_expr | @property_access_expr | @indexer_access_expr | @event_access_expr + | @method_access_expr | @type_access_expr | @dynamic_member_access_expr; +@access_expr = @member_access_expr | @this_access_expr | @base_access_expr | @assignable_access_expr | @namespace_access_expr; +@element_access_expr = @indexer_access_expr | @array_access_expr | @dynamic_element_access_expr; + +@local_variable_access = @local_variable_access_expr | @local_var_decl_expr; +@local_scope_variable_access_expr = @parameter_access_expr | @local_variable_access; +@variable_access_expr = @local_scope_variable_access_expr | @field_access_expr; + +@assignable_access_expr = @variable_access_expr | @property_access_expr | @element_access_expr + | @event_access_expr | @dynamic_member_access_expr; + +@objectorcollection_init_expr = @object_init_expr | @collection_init_expr; + +@delegate_creation_expr = @explicit_delegate_creation_expr | @implicit_delegate_creation_expr; + +@bin_arith_op_expr = @mul_expr | @div_expr | @rem_expr | @add_expr | @sub_expr; +@incr_op_expr = @pre_incr_expr | @post_incr_expr; +@decr_op_expr = @pre_decr_expr | @post_decr_expr; +@mut_op_expr = @incr_op_expr | @decr_op_expr; +@un_arith_op_expr = @plus_expr | @minus_expr | @mut_op_expr; +@arith_op_expr = @bin_arith_op_expr | @un_arith_op_expr; + +@ternary_log_op_expr = @conditional_expr; +@bin_log_op_expr = @log_and_expr | @log_or_expr | @null_coalescing_expr; +@un_log_op_expr = @log_not_expr; +@log_expr = @un_log_op_expr | @bin_log_op_expr | @ternary_log_op_expr; + +@bin_bit_op_expr = @bit_and_expr | @bit_or_expr | @bit_xor_expr | @lshift_expr + | @rshift_expr; +@un_bit_op_expr = @bit_not_expr; +@bit_expr = @un_bit_op_expr | @bin_bit_op_expr; + +@equality_op_expr = @eq_expr | @ne_expr; +@rel_op_expr = @gt_expr | @lt_expr| @ge_expr | @le_expr; +@comp_expr = @equality_op_expr | @rel_op_expr; + +@op_expr = @assign_expr | @un_op | @bin_op | @ternary_op; + +@ternary_op = @ternary_log_op_expr; +@bin_op = @bin_arith_op_expr | @bin_log_op_expr | @bin_bit_op_expr | @comp_expr; +@un_op = @un_arith_op_expr | @un_log_op_expr | @un_bit_op_expr | @sizeof_expr + | @pointer_indirection_expr | @address_of_expr; + +@anonymous_function_expr = @lambda_expr | @anonymous_method_expr; + +@call = @method_invocation_expr | @constructor_init_expr | @operator_invocation_expr + | @delegate_invocation_expr | @object_creation_expr | @call_access_expr + | @local_function_invocation_expr | @function_pointer_invocation_expr; + +@call_access_expr = @property_access_expr | @event_access_expr | @indexer_access_expr; + +@late_bindable_expr = @dynamic_element_access_expr | @dynamic_member_access_expr + | @object_creation_expr | @method_invocation_expr | @operator_invocation_expr; + +@throw_element = @throw_expr | @throw_stmt; + +@implicitly_typeable_object_creation_expr = @object_creation_expr | @explicit_delegate_creation_expr; + +implicitly_typed_array_creation( + unique int id: @array_creation_expr ref); + +explicitly_sized_array_creation( + unique int id: @array_creation_expr ref); + +stackalloc_array_creation( + unique int id: @array_creation_expr ref); + +implicitly_typed_object_creation( + unique int id: @implicitly_typeable_object_creation_expr ref); + +mutator_invocation_mode( + unique int id: @operator_invocation_expr ref, + int mode: int ref /* prefix = 1, postfix = 2*/); + +expr_compiler_generated( + unique int id: @expr ref); + +expr_value( + unique int id: @expr ref, + string value: string ref); + +expr_call( + unique int caller_id: @expr ref, + int target_id: @callable ref); + +expr_access( + unique int accesser_id: @access_expr ref, + int target_id: @accessible ref); + +@accessible = @method | @assignable | @local_function | @namespace; + +expr_location( + unique int id: @expr ref, + int loc: @location ref); + +dynamic_member_name( + unique int id: @late_bindable_expr ref, + string name: string ref); + +@qualifiable_expr = @member_access_expr + | @method_invocation_expr + | @element_access_expr; + +conditional_access( + unique int id: @qualifiable_expr ref); + +expr_argument( + unique int id: @expr ref, + int mode: int ref); + /* mode is the same as params: value = 0, ref = 1, out = 2 */ + +expr_argument_name( + unique int id: @expr ref, + string name: string ref); + +/** CONTROL/DATA FLOW **/ + +@control_flow_element = @stmt | @expr; + +/* XML Files */ + +xmlEncoding ( + unique int id: @file ref, + string encoding: string ref); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* Comments */ + +commentline( + unique int id: @commentline, + int kind: int ref, + string text: string ref, + string rawtext: string ref); + +case @commentline.kind of + 0 = @singlelinecomment +| 1 = @xmldoccomment +| 2 = @multilinecomment; + +commentline_location( + unique int id: @commentline ref, + int loc: @location ref); + +commentblock( + unique int id : @commentblock); + +commentblock_location( + unique int id: @commentblock ref, + int loc: @location ref); + +commentblock_binding( + int id: @commentblock ref, + int entity: @element ref, + int bindtype: int ref); /* 0: Parent, 1: Best, 2: Before, 3: After */ + +commentblock_child( + int id: @commentblock ref, + int commentline: @commentline ref, + int index: int ref); + +/* ASP.NET */ + +case @asp_element.kind of + 0=@asp_close_tag +| 1=@asp_code +| 2=@asp_comment +| 3=@asp_data_binding +| 4=@asp_directive +| 5=@asp_open_tag +| 6=@asp_quoted_string +| 7=@asp_text +| 8=@asp_xml_directive; + +@asp_attribute = @asp_code | @asp_data_binding | @asp_quoted_string; + +asp_elements( + unique int id: @asp_element, + int kind: int ref, + int loc: @location ref); + +asp_comment_server(unique int comment: @asp_comment ref); +asp_code_inline(unique int code: @asp_code ref); +asp_directive_attribute( + int directive: @asp_directive ref, + int index: int ref, + string name: string ref, + int value: @asp_quoted_string ref); +asp_directive_name( + unique int directive: @asp_directive ref, + string name: string ref); +asp_element_body( + unique int element: @asp_element ref, + string body: string ref); +asp_tag_attribute( + int tag: @asp_open_tag ref, + int index: int ref, + string name: string ref, + int attribute: @asp_attribute ref); +asp_tag_name( + unique int tag: @asp_open_tag ref, + string name: string ref); +asp_tag_isempty(int tag: @asp_open_tag ref); + +/* Common Intermediate Language - CIL */ + +case @cil_instruction.opcode of + 0 = @cil_nop +| 1 = @cil_break +| 2 = @cil_ldarg_0 +| 3 = @cil_ldarg_1 +| 4 = @cil_ldarg_2 +| 5 = @cil_ldarg_3 +| 6 = @cil_ldloc_0 +| 7 = @cil_ldloc_1 +| 8 = @cil_ldloc_2 +| 9 = @cil_ldloc_3 +| 10 = @cil_stloc_0 +| 11 = @cil_stloc_1 +| 12 = @cil_stloc_2 +| 13 = @cil_stloc_3 +| 14 = @cil_ldarg_s +| 15 = @cil_ldarga_s +| 16 = @cil_starg_s +| 17 = @cil_ldloc_s +| 18 = @cil_ldloca_s +| 19 = @cil_stloc_s +| 20 = @cil_ldnull +| 21 = @cil_ldc_i4_m1 +| 22 = @cil_ldc_i4_0 +| 23 = @cil_ldc_i4_1 +| 24 = @cil_ldc_i4_2 +| 25 = @cil_ldc_i4_3 +| 26 = @cil_ldc_i4_4 +| 27 = @cil_ldc_i4_5 +| 28 = @cil_ldc_i4_6 +| 29 = @cil_ldc_i4_7 +| 30 = @cil_ldc_i4_8 +| 31 = @cil_ldc_i4_s +| 32 = @cil_ldc_i4 +| 33 = @cil_ldc_i8 +| 34 = @cil_ldc_r4 +| 35 = @cil_ldc_r8 +| 37 = @cil_dup +| 38 = @cil_pop +| 39 = @cil_jmp +| 40 = @cil_call +| 41 = @cil_calli +| 42 = @cil_ret +| 43 = @cil_br_s +| 44 = @cil_brfalse_s +| 45 = @cil_brtrue_s +| 46 = @cil_beq_s +| 47 = @cil_bge_s +| 48 = @cil_bgt_s +| 49 = @cil_ble_s +| 50 = @cil_blt_s +| 51 = @cil_bne_un_s +| 52 = @cil_bge_un_s +| 53 = @cil_bgt_un_s +| 54 = @cil_ble_un_s +| 55 = @cil_blt_un_s +| 56 = @cil_br +| 57 = @cil_brfalse +| 58 = @cil_brtrue +| 59 = @cil_beq +| 60 = @cil_bge +| 61 = @cil_bgt +| 62 = @cil_ble +| 63 = @cil_blt +| 64 = @cil_bne_un +| 65 = @cil_bge_un +| 66 = @cil_bgt_un +| 67 = @cil_ble_un +| 68 = @cil_blt_un +| 69 = @cil_switch +| 70 = @cil_ldind_i1 +| 71 = @cil_ldind_u1 +| 72 = @cil_ldind_i2 +| 73 = @cil_ldind_u2 +| 74 = @cil_ldind_i4 +| 75 = @cil_ldind_u4 +| 76 = @cil_ldind_i8 +| 77 = @cil_ldind_i +| 78 = @cil_ldind_r4 +| 79 = @cil_ldind_r8 +| 80 = @cil_ldind_ref +| 81 = @cil_stind_ref +| 82 = @cil_stind_i1 +| 83 = @cil_stind_i2 +| 84 = @cil_stind_i4 +| 85 = @cil_stind_i8 +| 86 = @cil_stind_r4 +| 87 = @cil_stind_r8 +| 88 = @cil_add +| 89 = @cil_sub +| 90 = @cil_mul +| 91 = @cil_div +| 92 = @cil_div_un +| 93 = @cil_rem +| 94 = @cil_rem_un +| 95 = @cil_and +| 96 = @cil_or +| 97 = @cil_xor +| 98 = @cil_shl +| 99 = @cil_shr +| 100 = @cil_shr_un +| 101 = @cil_neg +| 102 = @cil_not +| 103 = @cil_conv_i1 +| 104 = @cil_conv_i2 +| 105 = @cil_conv_i4 +| 106 = @cil_conv_i8 +| 107 = @cil_conv_r4 +| 108 = @cil_conv_r8 +| 109 = @cil_conv_u4 +| 110 = @cil_conv_u8 +| 111 = @cil_callvirt +| 112 = @cil_cpobj +| 113 = @cil_ldobj +| 114 = @cil_ldstr +| 115 = @cil_newobj +| 116 = @cil_castclass +| 117 = @cil_isinst +| 118 = @cil_conv_r_un +| 121 = @cil_unbox +| 122 = @cil_throw +| 123 = @cil_ldfld +| 124 = @cil_ldflda +| 125 = @cil_stfld +| 126 = @cil_ldsfld +| 127 = @cil_ldsflda +| 128 = @cil_stsfld +| 129 = @cil_stobj +| 130 = @cil_conv_ovf_i1_un +| 131 = @cil_conv_ovf_i2_un +| 132 = @cil_conv_ovf_i4_un +| 133 = @cil_conv_ovf_i8_un +| 134 = @cil_conv_ovf_u1_un +| 135 = @cil_conv_ovf_u2_un +| 136 = @cil_conv_ovf_u4_un +| 137 = @cil_conv_ovf_u8_un +| 138 = @cil_conv_ovf_i_un +| 139 = @cil_conv_ovf_u_un +| 140 = @cil_box +| 141 = @cil_newarr +| 142 = @cil_ldlen +| 143 = @cil_ldelema +| 144 = @cil_ldelem_i1 +| 145 = @cil_ldelem_u1 +| 146 = @cil_ldelem_i2 +| 147 = @cil_ldelem_u2 +| 148 = @cil_ldelem_i4 +| 149 = @cil_ldelem_u4 +| 150 = @cil_ldelem_i8 +| 151 = @cil_ldelem_i +| 152 = @cil_ldelem_r4 +| 153 = @cil_ldelem_r8 +| 154 = @cil_ldelem_ref +| 155 = @cil_stelem_i +| 156 = @cil_stelem_i1 +| 157 = @cil_stelem_i2 +| 158 = @cil_stelem_i4 +| 159 = @cil_stelem_i8 +| 160 = @cil_stelem_r4 +| 161 = @cil_stelem_r8 +| 162 = @cil_stelem_ref +| 163 = @cil_ldelem +| 164 = @cil_stelem +| 165 = @cil_unbox_any +| 179 = @cil_conv_ovf_i1 +| 180 = @cil_conv_ovf_u1 +| 181 = @cil_conv_ovf_i2 +| 182 = @cil_conv_ovf_u2 +| 183 = @cil_conv_ovf_i4 +| 184 = @cil_conv_ovf_u4 +| 185 = @cil_conv_ovf_i8 +| 186 = @cil_conv_ovf_u8 +| 194 = @cil_refanyval +| 195 = @cil_ckinfinite +| 198 = @cil_mkrefany +| 208 = @cil_ldtoken +| 209 = @cil_conv_u2 +| 210 = @cil_conv_u1 +| 211 = @cil_conv_i +| 212 = @cil_conv_ovf_i +| 213 = @cil_conv_ovf_u +| 214 = @cil_add_ovf +| 215 = @cil_add_ovf_un +| 216 = @cil_mul_ovf +| 217 = @cil_mul_ovf_un +| 218 = @cil_sub_ovf +| 219 = @cil_sub_ovf_un +| 220 = @cil_endfinally +| 221 = @cil_leave +| 222 = @cil_leave_s +| 223 = @cil_stind_i +| 224 = @cil_conv_u +| 65024 = @cil_arglist +| 65025 = @cil_ceq +| 65026 = @cil_cgt +| 65027 = @cil_cgt_un +| 65028 = @cil_clt +| 65029 = @cil_clt_un +| 65030 = @cil_ldftn +| 65031 = @cil_ldvirtftn +| 65033 = @cil_ldarg +| 65034 = @cil_ldarga +| 65035 = @cil_starg +| 65036 = @cil_ldloc +| 65037 = @cil_ldloca +| 65038 = @cil_stloc +| 65039 = @cil_localloc +| 65041 = @cil_endfilter +| 65042 = @cil_unaligned +| 65043 = @cil_volatile +| 65044 = @cil_tail +| 65045 = @cil_initobj +| 65046 = @cil_constrained +| 65047 = @cil_cpblk +| 65048 = @cil_initblk +| 65050 = @cil_rethrow +| 65052 = @cil_sizeof +| 65053 = @cil_refanytype +| 65054 = @cil_readonly +; + +// CIL ignored instructions + +@cil_ignore = @cil_nop | @cil_break | @cil_volatile | @cil_unaligned; + +// CIL local/parameter/field access + +@cil_ldarg_any = @cil_ldarg_0 | @cil_ldarg_1 | @cil_ldarg_2 | @cil_ldarg_3 | @cil_ldarg_s | @cil_ldarga_s | @cil_ldarg | @cil_ldarga; +@cil_starg_any = @cil_starg | @cil_starg_s; + +@cil_ldloc_any = @cil_ldloc_0 | @cil_ldloc_1 | @cil_ldloc_2 | @cil_ldloc_3 | @cil_ldloc_s | @cil_ldloca_s | @cil_ldloc | @cil_ldloca; +@cil_stloc_any = @cil_stloc_0 | @cil_stloc_1 | @cil_stloc_2 | @cil_stloc_3 | @cil_stloc_s | @cil_stloc; + +@cil_ldfld_any = @cil_ldfld | @cil_ldsfld | @cil_ldsflda | @cil_ldflda; +@cil_stfld_any = @cil_stfld | @cil_stsfld; + +@cil_local_access = @cil_stloc_any | @cil_ldloc_any; +@cil_arg_access = @cil_starg_any | @cil_ldarg_any; +@cil_read_access = @cil_ldloc_any | @cil_ldarg_any | @cil_ldfld_any; +@cil_write_access = @cil_stloc_any | @cil_starg_any | @cil_stfld_any; + +@cil_stack_access = @cil_local_access | @cil_arg_access; +@cil_field_access = @cil_ldfld_any | @cil_stfld_any; + +@cil_access = @cil_read_access | @cil_write_access; + +// CIL constant/literal instructions + +@cil_ldc_i = @cil_ldc_i4_any | @cil_ldc_i8; + +@cil_ldc_i4_any = @cil_ldc_i4_m1 | @cil_ldc_i4_0 | @cil_ldc_i4_1 | @cil_ldc_i4_2 | @cil_ldc_i4_3 | + @cil_ldc_i4_4 | @cil_ldc_i4_5 | @cil_ldc_i4_6 | @cil_ldc_i4_7 | @cil_ldc_i4_8 | @cil_ldc_i4_s | @cil_ldc_i4; + +@cil_ldc_r = @cil_ldc_r4 | @cil_ldc_r8; + +@cil_literal = @cil_ldnull | @cil_ldc_i | @cil_ldc_r | @cil_ldstr; + +// Control flow + +@cil_conditional_jump = @cil_binary_jump | @cil_unary_jump; +@cil_binary_jump = @cil_beq_s | @cil_bge_s | @cil_bgt_s | @cil_ble_s | @cil_blt_s | + @cil_bne_un_s | @cil_bge_un_s | @cil_bgt_un_s | @cil_ble_un_s | @cil_blt_un_s | + @cil_beq | @cil_bge | @cil_bgt | @cil_ble | @cil_blt | + @cil_bne_un | @cil_bge_un | @cil_bgt_un | @cil_ble_un | @cil_blt_un; +@cil_unary_jump = @cil_brfalse_s | @cil_brtrue_s | @cil_brfalse | @cil_brtrue | @cil_switch; +@cil_unconditional_jump = @cil_br | @cil_br_s | @cil_leave_any; +@cil_leave_any = @cil_leave | @cil_leave_s; +@cil_jump = @cil_unconditional_jump | @cil_conditional_jump; + +// CIL call instructions + +@cil_call_any = @cil_jmp | @cil_call | @cil_calli | @cil_tail | @cil_callvirt | @cil_newobj; + +// CIL expression instructions + +@cil_expr = @cil_literal | @cil_binary_expr | @cil_unary_expr | @cil_call_any | @cil_read_access | + @cil_newarr | @cil_ldtoken | @cil_sizeof | + @cil_ldftn | @cil_ldvirtftn | @cil_localloc | @cil_mkrefany | @cil_refanytype | @cil_arglist | @cil_dup; + +@cil_unary_expr = + @cil_conversion_operation | @cil_unary_arithmetic_operation | @cil_unary_bitwise_operation| + @cil_ldlen | @cil_isinst | @cil_box | @cil_ldobj | @cil_castclass | @cil_unbox_any | + @cil_ldind | @cil_unbox; + +@cil_conversion_operation = + @cil_conv_i1 | @cil_conv_i2 | @cil_conv_i4 | @cil_conv_i8 | + @cil_conv_u1 | @cil_conv_u2 | @cil_conv_u4 | @cil_conv_u8 | + @cil_conv_ovf_i | @cil_conv_ovf_i_un | @cil_conv_ovf_i1 | @cil_conv_ovf_i1_un | + @cil_conv_ovf_i2 | @cil_conv_ovf_i2_un | @cil_conv_ovf_i4 | @cil_conv_ovf_i4_un | + @cil_conv_ovf_i8 | @cil_conv_ovf_i8_un | @cil_conv_ovf_u | @cil_conv_ovf_u_un | + @cil_conv_ovf_u1 | @cil_conv_ovf_u1_un | @cil_conv_ovf_u2 | @cil_conv_ovf_u2_un | + @cil_conv_ovf_u4 | @cil_conv_ovf_u4_un | @cil_conv_ovf_u8 | @cil_conv_ovf_u8_un | + @cil_conv_r4 | @cil_conv_r8 | @cil_conv_ovf_u2 | @cil_conv_ovf_u2_un | + @cil_conv_i | @cil_conv_u | @cil_conv_r_un; + +@cil_ldind = @cil_ldind_i | @cil_ldind_i1 | @cil_ldind_i2 | @cil_ldind_i4 | @cil_ldind_i8 | + @cil_ldind_r4 | @cil_ldind_r8 | @cil_ldind_ref | @cil_ldind_u1 | @cil_ldind_u2 | @cil_ldind_u4; + +@cil_stind = @cil_stind_i | @cil_stind_i1 | @cil_stind_i2 | @cil_stind_i4 | @cil_stind_i8 | + @cil_stind_r4 | @cil_stind_r8 | @cil_stind_ref; + +@cil_bitwise_operation = @cil_binary_bitwise_operation | @cil_unary_bitwise_operation; + +@cil_binary_bitwise_operation = @cil_and | @cil_or | @cil_xor | @cil_shr | @cil_shr | @cil_shr_un | @cil_shl; + +@cil_binary_arithmetic_operation = @cil_add | @cil_sub | @cil_mul | @cil_div | @cil_div_un | + @cil_rem | @cil_rem_un | @cil_add_ovf | @cil_add_ovf_un | @cil_mul_ovf | @cil_mul_ovf_un | + @cil_sub_ovf | @cil_sub_ovf_un; + +@cil_unary_bitwise_operation = @cil_not; + +@cil_binary_expr = @cil_binary_arithmetic_operation | @cil_binary_bitwise_operation | @cil_read_array | @cil_comparison_operation; + +@cil_unary_arithmetic_operation = @cil_neg; + +@cil_comparison_operation = @cil_cgt_un | @cil_ceq | @cil_cgt | @cil_clt | @cil_clt_un; + +// Elements that retrieve an address of something +@cil_read_ref = @cil_ldloca_s | @cil_ldarga_s | @cil_ldflda | @cil_ldsflda | @cil_ldelema; + +// CIL array instructions + +@cil_read_array = + @cil_ldelem | @cil_ldelema | @cil_ldelem_i1 | @cil_ldelem_ref | @cil_ldelem_i | + @cil_ldelem_i1 | @cil_ldelem_i2 | @cil_ldelem_i4 | @cil_ldelem_i8 | @cil_ldelem_r4 | + @cil_ldelem_r8 | @cil_ldelem_u1 | @cil_ldelem_u2 | @cil_ldelem_u4; + +@cil_write_array = @cil_stelem | @cil_stelem_ref | + @cil_stelem_i | @cil_stelem_i1 | @cil_stelem_i2 | @cil_stelem_i4 | @cil_stelem_i8 | + @cil_stelem_r4 | @cil_stelem_r8; + +@cil_throw_any = @cil_throw | @cil_rethrow; + +#keyset[impl, index] +cil_instruction( + unique int id: @cil_instruction, + int opcode: int ref, + int index: int ref, + int impl: @cil_method_implementation ref); + +cil_jump( + unique int instruction: @cil_jump ref, + int target: @cil_instruction ref); + +cil_access( + unique int instruction: @cil_instruction ref, + int target: @cil_accessible ref); + +cil_value( + unique int instruction: @cil_literal ref, + string value: string ref); + +#keyset[instruction, index] +cil_switch( + int instruction: @cil_switch ref, + int index: int ref, + int target: @cil_instruction ref); + +cil_instruction_location( + unique int id: @cil_instruction ref, + int loc: @location ref); + +cil_type_location( + int id: @cil_type ref, + int loc: @location ref); + +cil_method_location( + int id: @cil_method ref, + int loc: @location ref); + +@cil_namespace = @namespace; + +@cil_type_container = @cil_type | @cil_namespace | @cil_method; + +case @cil_type.kind of + 0 = @cil_valueorreftype +| 1 = @cil_typeparameter +| 2 = @cil_array_type +| 3 = @cil_pointer_type +| 4 = @cil_function_pointer_type +; + +cil_type( + unique int id: @cil_type, + string name: string ref, + int kind: int ref, + int parent: @cil_type_container ref, + int sourceDecl: @cil_type ref); + +cil_pointer_type( + unique int id: @cil_pointer_type ref, + int pointee: @cil_type ref); + +cil_array_type( + unique int id: @cil_array_type ref, + int element_type: @cil_type ref, + int rank: int ref); + +cil_function_pointer_return_type( + unique int id: @cil_function_pointer_type ref, + int return_type: @cil_type ref); + +cil_method( + unique int id: @cil_method, + string name: string ref, + int parent: @cil_type ref, + int return_type: @cil_type ref); + +cil_method_source_declaration( + unique int method: @cil_method ref, + int source: @cil_method ref); + +cil_method_implementation( + unique int id: @cil_method_implementation, + int method: @cil_method ref, + int location: @assembly ref); + +cil_implements( + int id: @cil_method ref, + int decl: @cil_method ref); + +#keyset[parent, name] +cil_field( + unique int id: @cil_field, + int parent: @cil_type ref, + string name: string ref, + int field_type: @cil_type ref); + +@cil_element = @cil_instruction | @cil_declaration | @cil_handler | @cil_attribute | @cil_namespace; +@cil_named_element = @cil_declaration | @cil_namespace; +@cil_declaration = @cil_variable | @cil_method | @cil_type | @cil_member; +@cil_accessible = @cil_declaration; +@cil_variable = @cil_field | @cil_stack_variable; +@cil_stack_variable = @cil_local_variable | @cil_parameter; +@cil_member = @cil_method | @cil_type | @cil_field | @cil_property | @cil_event; +@cil_custom_modifier_receiver = @cil_method | @cil_property | @cil_parameter | @cil_field | @cil_function_pointer_type; +@cil_parameterizable = @cil_method | @cil_function_pointer_type; +@cil_has_type_annotation = @cil_stack_variable | @cil_property | @cil_method | @cil_function_pointer_type; + +#keyset[parameterizable, index] +cil_parameter( + unique int id: @cil_parameter, + int parameterizable: @cil_parameterizable ref, + int index: int ref, + int param_type: @cil_type ref); + +cil_parameter_in(unique int id: @cil_parameter ref); +cil_parameter_out(unique int id: @cil_parameter ref); + +cil_setter(unique int prop: @cil_property ref, + int method: @cil_method ref); + +#keyset[id, modifier] +cil_custom_modifiers( + int id: @cil_custom_modifier_receiver ref, + int modifier: @cil_type ref, + int kind: int ref); // modreq: 1, modopt: 0 + +cil_type_annotation( + int id: @cil_has_type_annotation ref, + int annotation: int ref); + +cil_getter(unique int prop: @cil_property ref, + int method: @cil_method ref); + +cil_adder(unique int event: @cil_event ref, + int method: @cil_method ref); + +cil_remover(unique int event: @cil_event ref, int method: @cil_method ref); + +cil_raiser(unique int event: @cil_event ref, int method: @cil_method ref); + +cil_property( + unique int id: @cil_property, + int parent: @cil_type ref, + string name: string ref, + int property_type: @cil_type ref); + +#keyset[parent, name] +cil_event(unique int id: @cil_event, + int parent: @cil_type ref, + string name: string ref, + int event_type: @cil_type ref); + +#keyset[impl, index] +cil_local_variable( + unique int id: @cil_local_variable, + int impl: @cil_method_implementation ref, + int index: int ref, + int var_type: @cil_type ref); + +cil_function_pointer_calling_conventions( + int id: @cil_function_pointer_type ref, + int kind: int ref); + +// CIL handlers (exception handlers etc). + +case @cil_handler.kind of + 0 = @cil_catch_handler +| 1 = @cil_filter_handler +| 2 = @cil_finally_handler +| 4 = @cil_fault_handler +; + +#keyset[impl, index] +cil_handler( + unique int id: @cil_handler, + int impl: @cil_method_implementation ref, + int index: int ref, + int kind: int ref, + int try_start: @cil_instruction ref, + int try_end: @cil_instruction ref, + int handler_start: @cil_instruction ref); + +cil_handler_filter( + unique int id: @cil_handler ref, + int filter_start: @cil_instruction ref); + +cil_handler_type( + unique int id: @cil_handler ref, + int catch_type: @cil_type ref); + +@cil_controlflow_node = @cil_entry_point | @cil_instruction; + +@cil_entry_point = @cil_method_implementation | @cil_handler; + +@cil_dataflow_node = @cil_instruction | @cil_variable | @cil_method; + +cil_method_stack_size( + unique int method: @cil_method_implementation ref, + int size: int ref); + +// CIL modifiers + +cil_public(int id: @cil_member ref); +cil_private(int id: @cil_member ref); +cil_protected(int id: @cil_member ref); +cil_internal(int id: @cil_member ref); +cil_static(int id: @cil_member ref); +cil_sealed(int id: @cil_member ref); +cil_virtual(int id: @cil_method ref); +cil_abstract(int id: @cil_member ref); +cil_class(int id: @cil_type ref); +cil_interface(int id: @cil_type ref); +cil_security(int id: @cil_member ref); +cil_requiresecobject(int id: @cil_method ref); +cil_specialname(int id: @cil_method ref); +cil_newslot(int id: @cil_method ref); + +cil_base_class(unique int id: @cil_type ref, int base: @cil_type ref); +cil_base_interface(int id: @cil_type ref, int base: @cil_type ref); +cil_enum_underlying_type(unique int id: @cil_type ref, int underlying: @cil_type ref); + +#keyset[unbound, index] +cil_type_parameter( + int unbound: @cil_member ref, + int index: int ref, + int param: @cil_typeparameter ref); + +#keyset[bound, index] +cil_type_argument( + int bound: @cil_member ref, + int index: int ref, + int t: @cil_type ref); + +// CIL type parameter constraints + +cil_typeparam_covariant(int tp: @cil_typeparameter ref); +cil_typeparam_contravariant(int tp: @cil_typeparameter ref); +cil_typeparam_class(int tp: @cil_typeparameter ref); +cil_typeparam_struct(int tp: @cil_typeparameter ref); +cil_typeparam_new(int tp: @cil_typeparameter ref); +cil_typeparam_constraint(int tp: @cil_typeparameter ref, int supertype: @cil_type ref); + +// CIL attributes + +cil_attribute( + unique int attributeid: @cil_attribute, + int element: @cil_declaration ref, + int constructor: @cil_method ref); + +#keyset[attribute_id, param] +cil_attribute_named_argument( + int attribute_id: @cil_attribute ref, + string param: string ref, + string value: string ref); + +#keyset[attribute_id, index] +cil_attribute_positional_argument( + int attribute_id: @cil_attribute ref, + int index: int ref, + string value: string ref); + + +// Common .Net data model covering both C# and CIL + +// Common elements +@dotnet_element = @element | @cil_element; +@dotnet_named_element = @named_element | @cil_named_element; +@dotnet_callable = @callable | @cil_method; +@dotnet_variable = @variable | @cil_variable; +@dotnet_field = @field | @cil_field; +@dotnet_parameter = @parameter | @cil_parameter; +@dotnet_declaration = @declaration | @cil_declaration; +@dotnet_member = @member | @cil_member; +@dotnet_event = @event | @cil_event; +@dotnet_property = @property | @cil_property | @indexer; +@dotnet_parameterizable = @parameterizable | @cil_parameterizable; + +// Common types +@dotnet_type = @type | @cil_type; +@dotnet_call = @call | @cil_call_any; +@dotnet_throw = @throw_element | @cil_throw_any; +@dotnet_valueorreftype = @cil_valueorreftype | @value_or_ref_type | @cil_array_type | @void_type; +@dotnet_typeparameter = @type_parameter | @cil_typeparameter; +@dotnet_array_type = @array_type | @cil_array_type; +@dotnet_pointer_type = @pointer_type | @cil_pointer_type; +@dotnet_type_parameter = @type_parameter | @cil_typeparameter; +@dotnet_generic = @dotnet_valueorreftype | @dotnet_callable; + +// Attributes +@dotnet_attribute = @attribute | @cil_attribute; + +// Expressions +@dotnet_expr = @expr | @cil_expr; + +// Literals +@dotnet_literal = @literal_expr | @cil_literal; +@dotnet_string_literal = @string_literal_expr | @cil_ldstr; +@dotnet_int_literal = @integer_literal_expr | @cil_ldc_i; +@dotnet_float_literal = @float_literal_expr | @cil_ldc_r; +@dotnet_null_literal = @null_literal_expr | @cil_ldnull; + +@metadata_entity = @cil_method | @cil_type | @cil_field | @cil_property | @field | @property | + @callable | @value_or_ref_type | @void_type; + +#keyset[entity, location] +metadata_handle(int entity : @metadata_entity ref, int location: @assembly ref, int handle: int ref) diff --git a/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/upgrade.properties b/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/upgrade.properties new file mode 100644 index 00000000000..64b44aecc3c --- /dev/null +++ b/csharp/upgrades/9258e9b38d85f92cee9559f2ed21e241f0c7a29e/upgrade.properties @@ -0,0 +1,2 @@ +description: Removed unique base class constraint +compatibility: backwards diff --git a/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst b/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst index 60126d12d0a..da87b3d57d5 100644 --- a/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst +++ b/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst @@ -24,11 +24,12 @@ Before starting an analysis you must: Running ``codeql database analyze`` ------------------------------------ -When you run ``database analyze``, it does two things: +When you run ``database analyze``, it: #. Executes one or more query files, by running them over a CodeQL database. #. Interprets the results, based on certain query metadata, so that alerts can be displayed in the correct location in the source code. +#. Reports the results of any diagnostic and summary queries to standard output. You can analyze a database by running the following command:: @@ -101,42 +102,54 @@ You can also run your own custom queries with the ``database analyze`` command. For more information about preparing your queries to use with the CodeQL CLI, see ":doc:`Using custom queries with the CodeQL CLI `." - -Running LGTM.com query suites -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Running GitHub code scanning suites +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The CodeQL repository also includes query suites, which can be run over your code as part of a broader code review. CodeQL query suites are ``.qls`` files that use directives to select queries to run based on certain metadata properties. -The query suites included in the CodeQL repository select the same set of -queries that are run by default on `LGTM.com `__. The queries -are selected to highlight the most relevant and useful results for each -language. - -The language-specific LGTM query suites are located at the following paths in +The CodeQL repository includes query suites that are used by the CodeQL action on +`GitHub.com `__. The query suites are located at the following paths in the CodeQL repository:: - ql//ql/src/codeql-suites/-lgtm.qls + ql//ql/src/codeql-suites/-code-scanning.qls and at the following path in the CodeQL for Go repository:: - ql/src/codeql-suites/go-lgtm.qls + ql/src/codeql-suites/go-code-scanning.qls These locations are specified in the metadata included in the standard QL packs. -This means that CodeQL knows where to find the suite files automatically, and +This means that the CodeQL CLI knows where to find the suite files automatically, and you don't have to specify the full path on the command line when running an analysis. For more information, see ":ref:`About QL packs `." -For example, to run the LGTM.com query suite on a C++ codebase (generating -results in the latest SARIF format), you would run:: +.. pull-quote:: + + Important + + If you plan to upload the results to GitHub, you must generate SARIF results. + For more information, see `Analyzing a CodeQL database `__ in the GitHub documentation. + +For example, to run the code scanning query suite on a C++ codebase and generate +results in the v2.1 SARIF format supported by all versions of GitHub, you would run:: + + codeql database analyze cpp-code-scanning.qls --format=sarifv2.1.0 --output=cpp-analysis/cpp-results.sarif + +The repository also includes the query suites used by `LGTM.com `__. +These are stored alongside the code scanning suites with names of the form: ``-lgtm.qls``. - codeql database analyze cpp-lgtm.qls --format=sarif-latest --output=cpp-analysis/cpp-results.sarif - For information about creating custom query suites, see ":doc:`Creating CodeQL query suites `." +Diagnostic and summary information +.................................. + +When you create a CodeQL database, the extractor stores diagnostic data in the database. The code scanning query suites include additional queries to report on this diagnostic data and calculate summary metrics. When the ``database analyze`` command completes, the CLI generates the results file and reports any diagnostic and summary data to standard output. If you choose to generate SARIF output, the additional data is also included in the SARIF file. + +If the analysis found fewer results for standard queries than you expected, review the results of the diagnostic and summary queries to check whether the CodeQL database is likely to be a good representation of the codebase that you want to analyze. + Running all queries in a directory ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/codeql/codeql-cli/codeql-cli-reference.rst b/docs/codeql/codeql-cli/codeql-cli-reference.rst index 75513a06b54..0c53121d379 100644 --- a/docs/codeql/codeql-cli/codeql-cli-reference.rst +++ b/docs/codeql/codeql-cli/codeql-cli-reference.rst @@ -21,11 +21,3 @@ Learn more about the files you can use when running CodeQL processes and the res - :doc:`SARIF output `: CodeQL supports SARIF as an output format for sharing static analysis results. - :doc:`Exit codes `: The CodeQL CLI reports the status of each command it runs as an exit code. This exit code provides information for subsequent commands or for other tools that rely on the CodeQL CLI. - -.. _cli-commands: - -CodeQL CLI manual ------------------ - -To view detailed information about each CodeQL CLI command, -including its usage and options, add the ``--help`` flag or visit the "`CodeQL CLI manual <../manual>`__." diff --git a/docs/codeql/codeql-cli/creating-codeql-databases.rst b/docs/codeql/codeql-cli/creating-codeql-databases.rst index 4f7212050df..637df58555b 100644 --- a/docs/codeql/codeql-cli/creating-codeql-databases.rst +++ b/docs/codeql/codeql-cli/creating-codeql-databases.rst @@ -165,13 +165,15 @@ build steps, you may need to explicitly define each step in the command line. .. pull-quote:: Creating databases for Go - For Go, you should always use the CodeQL autobuilder. Install the Go - toolchain (version 1.11 or later) and, if there are dependencies, the - appropriate dependency manager (such as `dep + For Go, install the Go toolchain (version 1.11 or later) and, if there + are dependencies, the appropriate dependency manager (such as `dep `__). - Do not specify any build commands, as you will override the autobuilder - invocation, which will create an empty database. + The Go autobuilder attempts to automatically detect code written in Go in a repository, + and only runs build scripts in an attempt to fetch dependencies. To force + CodeQL to limit extraction to the files compiled by your build script, set the environment variable + `CODEQL_EXTRACTOR_GO_BUILD_TRACING=on` or use the ``--command`` option to specify a + build command. Specifying build commands ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -200,6 +202,14 @@ commands that you can specify for compiled languages. codeql database create csharp-database --language=csharp --command='dotnet build /p:UseSharedCompilation=false /t:rebuild' +- Go project built using the ``COEQL_EXTRACTOR_GO_BUILD_TRACING=on`` environment variable:: + + CODEQL_EXTRACTOR_GO_BUILD_TRACING=on codeql database create go-database --language=go + +- Go project built using a custom build script:: + + codeql database create go-database --language=go --command='./scripts/build.sh' + - Java project built using Gradle:: codeql database create java-database --language=java --command='gradle clean test' diff --git a/docs/codeql/codeql-cli/index.rst b/docs/codeql/codeql-cli/index.rst index 07ec8431235..d7a8a407364 100644 --- a/docs/codeql/codeql-cli/index.rst +++ b/docs/codeql/codeql-cli/index.rst @@ -18,4 +18,4 @@ CodeQL CLI using-the-codeql-cli codeql-cli-reference - + CodeQL CLI manual diff --git a/docs/codeql/codeql-cli/using-custom-queries-with-the-codeql-cli.rst b/docs/codeql/codeql-cli/using-custom-queries-with-the-codeql-cli.rst index aa9e45927f1..51d63ed3709 100644 --- a/docs/codeql/codeql-cli/using-custom-queries-with-the-codeql-cli.rst +++ b/docs/codeql/codeql-cli/using-custom-queries-with-the-codeql-cli.rst @@ -33,8 +33,10 @@ following two properties to ensure that the results are interpreted correctly: - Query identifier (``@id``): a sequence of words composed of lowercase letters or digits, delimited by ``/`` or ``-``, identifying and classifying the query. -- Query type (``@kind``): identifies the query is an alert (``@kind problem``) - or a path (``@kind path-problem``). +- Query type (``@kind``): identifies the query as a simple alert (``@kind problem``), + an alert documented by a sequence of code locations (``@kind path-problem``), + for extractor troubleshooting (``@kind diagnostic``), or a summary metric + (``@kind metric`` and ``@tags summary``). For more information about these metadata properties, see ":ref:`Metadata for CodeQL queries `" and the `Query metadata style guide diff --git a/docs/codeql/codeql-for-visual-studio-code/customizing-settings.rst b/docs/codeql/codeql-for-visual-studio-code/customizing-settings.rst index 8197858d3ab..6b1b820b6aa 100644 --- a/docs/codeql/codeql-for-visual-studio-code/customizing-settings.rst +++ b/docs/codeql/codeql-for-visual-studio-code/customizing-settings.rst @@ -46,6 +46,8 @@ The query history **Format** setting controls how the extension lists queries in To override the default label, you can specify a different format for the query history items. +.. _configuring-settings-for-running-queries: + Configuring settings for running queries ----------------------------------------- @@ -53,6 +55,8 @@ There are a number of settings for **Running Queries**. If your queries run too .. include:: ../reusables/running-queries-debug.rst +To save query server logs in a custom location, edit the **Running Queries: Custom Log Directory** setting. If you use a custom log directory, the extension saves the logs permanently, instead of deleting them automatically after each workspace session. This is useful if you want to investigate these logs to improve the performance of your queries. + Configuring settings for testing queries ----------------------------------------- diff --git a/docs/codeql/codeql-for-visual-studio-code/troubleshooting-codeql-for-visual-studio-code.rst b/docs/codeql/codeql-for-visual-studio-code/troubleshooting-codeql-for-visual-studio-code.rst index 80fc5efea0f..3f50d29604b 100644 --- a/docs/codeql/codeql-for-visual-studio-code/troubleshooting-codeql-for-visual-studio-code.rst +++ b/docs/codeql/codeql-for-visual-studio-code/troubleshooting-codeql-for-visual-studio-code.rst @@ -38,6 +38,8 @@ You are most likely to need to restart the query server if you make external cha To see the logs from running a particular query, right-click the query in the Query History and select **Show Query Log**. If the log file is too large for the extension to open in the VS Code editor, the file will be displayed in your file explorer so you can open it with an external program. +By default, the extension deletes logs after each workspace session. To override this behavior, you can specify a custom directory for query server logs. For more information, see ":ref:`Customizing settings `." + Exploring problems with running tests ---------------------------------------------- diff --git a/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst b/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst index 09b821ad9e7..7d9ac42c3d8 100644 --- a/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst +++ b/docs/codeql/codeql-language-guides/data-flow-cheat-sheet-for-javascript.rst @@ -59,6 +59,7 @@ Classes and member predicates in the ``DataFlow::`` module: - `getStringValue `__ -- value of this node if it's is a string constant - `mayHaveBooleanValue `__ -- check if the value is ``true`` or ``false`` - `SourceNode `__ extends `Node `__ -- function call, parameter, object creation, or reference to a property or global variable + - `getALocalUse `__ -- find nodes whose value came from this node - `getACall `__ -- find calls with this as the callee - `getAnInstantiation `__ -- find ``new``-calls with this as the callee - `getAnInvocation `__ -- find calls or ``new``-calls with this as the callee @@ -130,9 +131,25 @@ System and Network - `FileSystemWriteAccess `__ -- writing to the contents of a file - `PersistentReadAccess `__ -- reading from persistent storage, like cookies - `PersistentWriteAccess `__ -- writing to persistent storage -- `RemoteFlowSource `__ -- source of untrusted user input - `SystemCommandExecution `__ -- execution of a system command +.. _data-flow-cheat-sheet-for-javascript--untrusted-data: + +Untrusted data +-------------- + +- `RemoteFlowSource `__ -- source of untrusted user input + - `isUserControlledObject `__ -- is the input deserialized to a JSON-like object? (as opposed to just being a string) +- `ClientSideRemoteFlowSource `__ extends `RemoteFlowSource `__ -- input specific to the browser environment + - `getKind `__ -- is this derived from the ``path``, ``fragment``, ``query``, ``url``, or ``name``? +- HTTP::`RequestInputAccess `__ extends `RemoteFlowSource `__ -- input from an incoming HTTP request + - `getKind `__ -- is this derived from a ``parameter``, ``header``, ``body``, ``url``, or ``cookie``? +- HTTP::`RequestHeaderAccess `__ extends `RequestInputAccess `__ -- access to a specific header + - `getAHeaderName `__ -- the name of a header being accessed + +Note: some `RemoteFlowSource `__ instances, such as input from a web socket, +belong to none of the specific subcategories above. + Files ----- @@ -164,6 +181,19 @@ String matching - x.\ `regexpMatch `__\ ("(?i).*escape.*") -- holds if x contains "escape" (case insensitive) +Access paths +------------ + +When multiple property accesses are chained together they form what's called an "access path". + +To identify nodes based on access paths, use the following predicates in `AccessPath `__ module: + +- AccessPath::`getAReferenceTo `__ -- find nodes that refer to the given access path +- AccessPath::`getAnAssignmentTo `__ -- finds nodes that are assigned to the given access path +- AccessPath::`getAnAliasedSourceNode `__ -- finds nodes that refer to the same access path + +``getAReferenceTo`` and ``getAnAssignmentTo`` have a 1-argument version for global access paths, and a 2-argument version for access paths starting at a given node. + Type tracking ------------- diff --git a/docs/codeql/codeql-language-guides/specifying-additional-remote-flow-sources-for-javascript.rst b/docs/codeql/codeql-language-guides/specifying-additional-remote-flow-sources-for-javascript.rst index fabe58bf5c9..cc3fe5e9469 100644 --- a/docs/codeql/codeql-language-guides/specifying-additional-remote-flow-sources-for-javascript.rst +++ b/docs/codeql/codeql-language-guides/specifying-additional-remote-flow-sources-for-javascript.rst @@ -11,8 +11,8 @@ You can model potential sources of untrusted user input in your code without mak Specifying remote flow sources in external files is currently in beta and subject to change. -As mentioned in the :doc:`Data flow cheat sheet for JavaScript `, the CodeQL libraries for JavaScript -provide a class `RemoteFlowSource `__ to represent sources of untrusted user input, sometimes also referred to as remote flow +As mentioned in the :ref:`Data flow cheat sheet for JavaScript `, the CodeQL libraries for JavaScript +provide a class `RemoteFlowSource `__ to represent sources of untrusted user input, sometimes also referred to as remote flow sources. To model a new source of untrusted input, such as a previously unmodelled library API, you can diff --git a/docs/codeql/ql-language-reference/annotations.rst b/docs/codeql/ql-language-reference/annotations.rst index ec60e55bf9b..e801f0a95aa 100644 --- a/docs/codeql/ql-language-reference/annotations.rst +++ b/docs/codeql/ql-language-reference/annotations.rst @@ -259,8 +259,6 @@ This means that it is part of the output of the QL program. Compiler pragmas ================ -**Available for**: |characteristic predicates|, |member predicates|, |non-member predicates| - The following compiler pragmas affect the compilation and optimization of queries. You should avoid using these annotations unless you experience significant performance issues. @@ -284,6 +282,8 @@ predicates. ``pragma[inline]`` ------------------ +**Available for**: |characteristic predicates|, |member predicates|, |non-member predicates| + The ``pragma[inline]`` annotation tells the QL optimizer to always inline the annotated predicate into the places where it is called. This can be useful when a predicate body is very expensive to compute entirely, as it ensures that the predicate is evaluated with the other contextual information @@ -292,6 +292,8 @@ at the places where it is called. ``pragma[noinline]`` -------------------- +**Available for**: |characteristic predicates|, |member predicates|, |non-member predicates| + The ``pragma[noinline]`` annotation is used to prevent a predicate from being inlined into the place where it is called. In practice, this annotation is useful when you've already grouped certain variables together in a "helper" predicate, to ensure that the relation is evaluated @@ -301,6 +303,8 @@ work of the helper predicate, so it's a good idea to annotate it with ``pragma[n ``pragma[nomagic]`` ------------------- +**Available for**: |characteristic predicates|, |member predicates|, |non-member predicates| + The ``pragma[nomagic]`` annotation is used to prevent the QL optimizer from performing the "magic sets" optimization on a predicate. @@ -314,6 +318,8 @@ Note that ``nomagic`` implies ``noinline``. ``pragma[noopt]`` ----------------- +**Available for**: |characteristic predicates|, |member predicates|, |non-member predicates| + The ``pragma[noopt]`` annotation is used to prevent the QL optimizer from optimizing a predicate, except when it's absolutely necessary for compilation and evaluation to work. @@ -364,7 +370,33 @@ When you use this annotation, be aware of the following issues: succ.getSucc() = 3 ) } - + +``pragma[only_bind_out]`` +------------------------- + +**Available for**: |expressions| + +The ``pragma[only_bind_out]`` annotation lets you specify the direction in which the QL compiler should bind expressions. +This can be useful to improve performance in rare cases where the QL optimizer orders parts of the QL program in an inefficient way. + +For example, ``x = pragma[only_bind_out](y)`` is semantically equivalent to ``x = y``, but has different binding behavior. +``x = y`` binds ``x`` from ``y`` and vice versa, while ``x = pragma[only_bind_out](y)`` only binds ``x`` from ``y``. + +For more information, see ":ref:`Binding `." + +``pragma[only_bind_into]`` +-------------------------- + +**Available for**: |expressions| + +The ``pragma[only_bind_into]`` annotation lets you specify the direction in which the QL compiler should bind expressions. +This can be useful to improve performance in rare cases where the QL optimizer orders parts of the QL program in an inefficient way. + +For example, ``x = pragma[only_bind_into](y)`` is semantically equivalent to ``x = y``, but has different binding behavior. +``x = y`` binds ``x`` from ``y`` and vice versa, while ``x = pragma[only_bind_into](y)`` only binds ``y`` from ``x``. + +For more information, see ":ref:`Binding `." + .. _language: Language pragmas @@ -413,4 +445,5 @@ The ``bindingset`` annotation takes a comma-separated list of variables. .. |fields| replace:: :ref:`fields ` .. |modules| replace:: :ref:`modules ` .. |aliases| replace:: :ref:`aliases ` -.. |algebraic datatypes| replace:: :ref:`algebraic datatypes ` \ No newline at end of file +.. |algebraic datatypes| replace:: :ref:`algebraic datatypes ` +.. |expressions| replace:: :ref:`expressions ` diff --git a/docs/codeql/ql-language-reference/expressions.rst b/docs/codeql/ql-language-reference/expressions.rst index a9facb0dec1..08a925b50b8 100644 --- a/docs/codeql/ql-language-reference/expressions.rst +++ b/docs/codeql/ql-language-reference/expressions.rst @@ -308,7 +308,10 @@ The following aggregates are available in QL: rank[4](int i | i = [5 .. 15] | i) - Note that the rank indices start at ``1``, so ``rank[0](...)`` returns no results. + .. pull-quote:: Note + + - Rank indices start at ``1``, so ``rank[0](...)`` has no result. + - ``rank[1](...)`` is the same as ``min(...)``. .. index:: strictconcat, strictcount, strictsum @@ -488,6 +491,108 @@ value for each value generated by the ````: value generated by the ````. Here, the aggregation function is applied to each of the resulting combinations. +Example of monotonic aggregates +------------------------------- + +Consider this query: + +.. code-block:: ql + + string getPerson() { result = "Alice" or + result = "Bob" or + result = "Charles" or + result = "Diane" + } + string getFruit(string p) { p = "Alice" and result = "Orange" or + p = "Alice" and result = "Apple" or + p = "Bob" and result = "Apple" or + p = "Charles" and result = "Apple" or + p = "Charles" and result = "Banana" + } + int getPrice(string f) { f = "Apple" and result = 100 or + f = "Orange" and result = 100 or + f = "Orange" and result = 1 + } + + predicate nonmono(string p, int cost) { + p = getPerson() and cost = sum(string f | f = getFruit(p) | getPrice(f)) + } + + language[monotonicAggregates] + predicate mono(string p, int cost) { + p = getPerson() and cost = sum(string f | f = getFruit(p) | getPrice(f)) + } + + from string variant, string person, int cost + where variant = "default" and nonmono(person, cost) or + variant = "monotonic" and mono(person, cost) + select variant, person, cost + order by variant, person + +The query produces these results: + ++-----------+---------+------+ +| variant | person | cost | ++===========+=========+======+ +| default | Alice | 201 | ++-----------+---------+------+ +| default | Bob | 100 | ++-----------+---------+------+ +| default | Charles | 100 | ++-----------+---------+------+ +| default | Diane | 0 | ++-----------+---------+------+ +| monotonic | Alice | 101 | ++-----------+---------+------+ +| monotonic | Alice | 200 | ++-----------+---------+------+ +| monotonic | Bob | 100 | ++-----------+---------+------+ +| monotonic | Diane | 0 | ++-----------+---------+------+ + +The two variants of the aggregate semantics differ in what happens +when ``getPrice(f)`` has either multiple results or no results +for a given ``f``. + +In this query, oranges are available at two different prices, and the +default ``sum`` aggregate returns a single line where Alice buys an +orange at a price of 100, another orange at a price of 1, and an apple +at a price of 100, totalling 201. On the other hand, in the the +*monotonic* semantics for ``sum``, Alice always buys one orange and +one apple, and a line of output is produced for each *way* she can +complete her shopping list. + +If there had been two different prices for apples too, the monotonic +``sum`` would have produced *four* output lines for Alice. + +Charles wants to buy a banana, which is not for sale at all. In the +default case, the sum produced for Charles includes the cost of the +apple he *can* buy, but there's no line for Charles in the monontonic +``sum`` output, because there *is no way* for Charles to buy one apple +plus one banana. + +(Diane buys no fruit at all, and in both variants her total cost +is 0. The ``strictsum`` aggregate would have excluded her from the +results in both cases). + +In actual QL practice, it is quite rare to use monotonic aggregates +with the *goal* of having multiple output lines, as in the "Alice" +case of this example. The more significant point is the "Charles" +case: As long as there's no price for bananas, no output is produced +for him. This means that if we later do learn of a banana price, we +don't need to *remove* any output tuple already produced. The +importance of this is that the monotonic aggregate behavior works well +with a fixpoint-based semantics for recursion, so it will be meaningul +to let the ``getPrice`` predicate be mutually recursive with the count +aggregate itself. (On the other hand, ``getFruit`` still cannot be +allowed to be recursive, because adding another fruit to someone's +shopping list would invalidate the total costs we already knew for +them). + +This opportunity to use recursion is the main practical reason for +requesting monotonic semantics of aggregates. + Recursive monotonic aggregates ------------------------------ diff --git a/docs/codeql/ql-language-reference/ql-language-specification.rst b/docs/codeql/ql-language-reference/ql-language-specification.rst index 0fe54210504..bd305bdbadc 100644 --- a/docs/codeql/ql-language-reference/ql-language-specification.rst +++ b/docs/codeql/ql-language-reference/ql-language-specification.rst @@ -69,25 +69,14 @@ of the active database schema (for example, ````). A ``qlpack.yml`` file defines a :ref:`QL pack `. -The content of a ``qlpack.yml`` file is described in the CodeQL CLI documentation. This file -will not be recognized when using legacy tools that are not based -on the CodeQL CLI (that is, LGTM.com, LGTM Enterprise, ODASA, CodeQL for -Eclipse, and CodeQL for Visual Studio). +The content of a ``qlpack.yml`` file is described in the CodeQL CLI documentation. If both a ``queries.xml`` and a ``qlpack.yml`` exist in the same directory, the latter takes precedence (and the former is assumed to exist for compatibility with older tooling). -In legacy QL tools that don't recognize ``qlpack.yml`` files, the default -value of the library path for -each supported language is hard-coded. The tools contain directories within the ODASA -distribution that define the default CodeQL libraries for the selected -language. Which language to use depends on the ``language`` attribute -of the ``queries.xml`` file if not overridden with a ``--language`` -option to the ODASA CLI. - -On the other hand, the CodeQL CLI and newer tools based on it (such as -GitHub Code Scanning and the CodeQL extension for Visual Studio Code) +The CodeQL CLI and newer tools based on it (such as, +GitHub code scanning and the CodeQL extension for Visual Studio Code) construct a library path using QL packs. For each QL pack added to the library path, the QL packs named in its ``libraryPathDependencies`` will be subsequently added to the library @@ -1116,8 +1105,6 @@ A super expression may only occur in a QL program as the receiver expression for If a super expression includes a ``type``, then that type must be a class that the enclosing class inherits from. -If the super expression does not include a type, then the enclosing class must have a single declared base type, and that base type must be a class. - The value of a super expression is the same as the value of ``this`` in the named tuple. Casts @@ -1169,7 +1156,12 @@ A valid call with results *resolves* to a set of predicates. The ways a call can - If the call has no receiver and the predicate name is a selection identifier, then the qualifier is resolved as a module (see "`Module resolution <#module-resolution>`__"). The identifier is then resolved in the exported predicate environment of the qualifier module. -- If the call has a super expression as the receiver, then it resolves to a member predicate in a class the enclosing class inherits from. If the super expression is unqualified, then the super-class is the single class that the current class inherits from. If there is not exactly one such class, then the program is invalid. Otherwise the super-class is the class named by the qualifier of the super expression. The predicate is resolved by looking up its name and arity in the exported predicate environment of the super-class. +- If the call has a super expression as the receiver, then it resolves to a member predicate in a class that the enclosing class inherits from: + - If the super expression is unqualified and there is a single class that the current class inherits from, then the super-class is that class. + - If the super expression is unqualified and there are multiple classes that the current class inherits from, then the super-class is the domain type. + - Otherwise, the super-class is the class named by the qualifier of the super expression. + + The predicate is resolved by looking up its name and arity in the exported predicate environment of the super-class. - If the type of the receiver is the same as the enclosing class, the predicate is resolved by looking up its name and arity in the visible predicate environment of the class. diff --git a/docs/codeql/reusables/running-queries-debug.rst b/docs/codeql/reusables/running-queries-debug.rst index d7515fc4236..26e7162aa2b 100644 --- a/docs/codeql/reusables/running-queries-debug.rst +++ b/docs/codeql/reusables/running-queries-debug.rst @@ -1 +1 @@ -If you want to examine query performance, enable the **Running Queries: Debug** setting to include timing and tuple counts in the CodeQL Query Server logs shown in the Output view. The tuple count is useful because it indicates the size of the :ref:`predicates ` computed by the query. \ No newline at end of file +If you want to examine query performance, enable the **Running Queries: Debug** setting to include timing and tuple counts. This is shown in the logs in the CodeQL Query Server tab of the Output view. The tuple count is useful because it indicates the size of the :ref:`predicates ` computed by the query. \ No newline at end of file diff --git a/docs/codeql/support/index.rst b/docs/codeql/support/index.rst index f9cdf40e227..89812242fce 100644 --- a/docs/codeql/support/index.rst +++ b/docs/codeql/support/index.rst @@ -16,5 +16,4 @@ For details of the CodeQL libraries, see `CodeQL standard libraries `__. -* The process of creating a CodeQL database is much simpler and more streamlined. - There's no need to create ``projects`` or ``snapshots``---just check out the - code and build it using the CodeQL CLI ``codeql database create`` command. -* Queries are run against CodeQL databases using the CodeQL CLI ``codeql - database analyze`` command. - -For more information, see `Creating CodeQL databases -`__ and -`Analyzing databases with the CodeQL CLI `__. -For detailed guidance about equivalent commands, see `Overview of common commands -<#overview-of-common-commands>`__ below. - -.. _database-compatibiilty-notes: - -Database compatibility notes ----------------------------- - -A CodeQL database created by the CodeQL CLI serves the same purpose as a QL -snapshot created using ``odasa``. They both contain a code database to query and -usually a source reference for results display. However, they are not identical -formats and, if you use the legacy QL tools alongside the CodeQL tools, you need -to be aware of the following: - -* Existing QL snapshots, exported using the legacy CLI, can be used with the new - CodeQL tools. Unzip the snapshot and treat the directory as a database. If it - was built with an earlier version of the legacy CLI, you may need to upgrade - the database using ``codeql database upgrade``. For more information, see the - `database upgrade reference documentation - `__. - -* CodeQL databases are not directly compatible with CodeQL for Eclipse. - However, you can "bundle" a CodeQL database into the equivalent of a QL - exported snapshot by running:: - - codeql database bundle --include-uncompressed-source -o - - The resulting database can be imported into CodeQL for Eclipse. For more - information, see the `database bundle reference documentation `__. - -* .. include:: ../reusables/index-files-note.rst - -* CodeQL databases cannot be directly uploaded to an LGTM Enterprise instance. - For more information, see `Preparing CodeQL databases to upload to LGTM - `__ - in the LGTM admin help. - -Query suites ------------- - -CodeQL includes a new, more flexible, format for query suites. Legacy query -suite definitions are not compatible with the new CodeQL tools. For more -information about CodeQL query suites, see `Creating CodeQL query suites -`__. - -Overview of common commands ---------------------------- - -If you're switching from the legacy ODASA CLI to the new CodeQL CLI, -the table below shows which commands replace the most -common ODASA processes. - -+------------------------------------------+---------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``odasa`` command | Corresponding ``codeql`` command | Notes | -+==========================================+===================================================================================================+=========================================================================================================================================================================================================================================================================================================================================================================================================================================================================+ -| ``bootstrap`` | n/a | CodeQL analysis does not use ``project`` files during database creation. For more information about creating databases, see `Creating CodeQL databases `__. | -+------------------------------------------+---------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``addSnapshot``, ``addLatestSnapshot`` | n/a | To obtain the version of the code you want to analyze, just run your normal check-out commands. | -+------------------------------------------+---------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``buildSnapshot`` | `database create `__ | When creating a CodeQL database, you specfiy build commands in the command line, rather than in a project file. For more information, see `Creating CodeQL databases `__. | -+------------------------------------------+---------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``analyzeSnapshot`` | `database analyze `__ | For more information, see `Analyzing databases with the CodeQL CLI `__. | -+------------------------------------------+---------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``archiveSnapshot`` | `database cleanup `__ | Use ``database cleanup`` to reduce the size of a CodeQL database by deleting temporary data. | -+------------------------------------------+---------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``export`` | `database bundle `__ | You don't need to export databases before adding them to VS Code. However, you should "bundle" CodeQL databases before using them with LGTM Enterprise, CodeQL for Eclipse, or CodeQL for Visual Studio. For more information, see `Preparing CodeQL databases to upload to LGTM `__ in the LGTM admin help and the `Database compatibility notes <#database-compatibility-notes>`__. | -+------------------------------------------+---------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``prepareQueries`` | `query compile `__ | Queries are compiled when you run ``database analyze`` and other query-running commands. You can speed up compilation by running ``query compile`` separately using more threads. | -+------------------------------------------+---------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``qltest`` | `test run `__ | For more information about running regression tests, see `Testing custom queries `__. | -+------------------------------------------+---------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``runQuery`` | `query run `__ | Use ``query run`` to quickly view results in your terminal. To generate interpreted results that can be viewed in source code, use ``database analyze``. | -+------------------------------------------+---------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``upgrade`` | `database upgrade `__ | For more information, see `Upgrading CodeQL databases `__. | -+------------------------------------------+---------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ \ No newline at end of file diff --git a/docs/codeql/support/reusables/frameworks.rst b/docs/codeql/support/reusables/frameworks.rst index 22569cbdcc6..0205c4b2df9 100644 --- a/docs/codeql/support/reusables/frameworks.rst +++ b/docs/codeql/support/reusables/frameworks.rst @@ -23,6 +23,7 @@ C# built-in support ASP.NET, Web application framework ASP.NET Core, Web application framework ASP.NET Razor templates, Web application framework + Dapper, Database ORM EntityFramework, Database ORM EntityFramework Core, Database ORM Json.NET, Serialization @@ -128,9 +129,11 @@ JavaScript and TypeScript built-in support mssql, Database mysql, Database node, Runtime environment + nest.js, Server postgres, Database ramda, Utility library react, HTML framework + react native, HTML framework request, Network communicator sequelize, Database socket.io, Network communicator @@ -149,15 +152,22 @@ Python built-in support :widths: auto Name, Category + aiohttp.web, Web framework Django, Web framework Flask, Web framework Tornado, Web framework PyYAML, Serialization dill, Serialization + simplejson, Serialization + ujson, Serialization fabric, Utility library + idna, Utility library invoke, Utility library + multidict, Utility library + yarl, Utility library mysql-connector-python, Database - MySQLdb, Database + mysql-connector, Database + MySQL-python, Database psycopg2, Database sqlite3, Database cryptography, Cryptography library diff --git a/docs/codeql/support/reusables/versions-compilers.rst b/docs/codeql/support/reusables/versions-compilers.rst index a338791953b..74b9c196564 100644 --- a/docs/codeql/support/reusables/versions-compilers.rst +++ b/docs/codeql/support/reusables/versions-compilers.rst @@ -11,22 +11,24 @@ Microsoft extensions (up to VS 2019), Arm Compiler 5 [2]_","``.cpp``, ``.c++``, ``.cxx``, ``.hpp``, ``.hh``, ``.h++``, ``.hxx``, ``.c``, ``.cc``, ``.h``" - C#,C# up to 8.0,"Microsoft Visual Studio up to 2019 with .NET up to 4.8, + C#,C# up to 9.0,"Microsoft Visual Studio up to 2019 with .NET up to 4.8, - .NET Core up to 3.1","``.sln``, ``.csproj``, ``.cs``, ``.cshtml``, ``.xaml``" + .NET Core up to 3.1 + + .NET 5","``.sln``, ``.csproj``, ``.cs``, ``.cshtml``, ``.xaml``" Go (aka Golang), "Go up to 1.16", "Go 1.11 or more recent", ``.go`` - Java,"Java 7 to 15 [3]_","javac (OpenJDK and Oracle JDK), + Java,"Java 7 to 16 [3]_","javac (OpenJDK and Oracle JDK), Eclipse compiler for Java (ECJ) [4]_",``.java`` - JavaScript,ECMAScript 2019 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhm``, ``.xhtml``, ``.vue``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [5]_" - Python,"2.7, 3.5, 3.6, 3.7, 3.8",Not applicable,``.py`` - TypeScript [6]_,"2.6-3.7",Standard TypeScript compiler,"``.ts``, ``.tsx``" + JavaScript,ECMAScript 2021 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhm``, ``.xhtml``, ``.vue``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [5]_" + Python,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9",Not applicable,``.py`` + TypeScript [6]_,"2.6-4.2",Standard TypeScript compiler,"``.ts``, ``.tsx``" .. container:: footnote-group .. [1] Support for the clang-cl compiler is preliminary. .. [2] Support for the Arm Compiler (armcc) is preliminary. - .. [3] Builds that execute on Java 7 to 15 can be analyzed. The analysis understands Java 15 standard language features. + .. [3] Builds that execute on Java 7 to 16 can be analyzed. The analysis understands Java 15 standard language features. .. [4] ECJ is supported when the build invokes it via the Maven Compiler plugin or the Takari Lifecycle plugin. - .. [5] JSX and Flow code, YAML, JSON, HTML, and XML files may also be analyzed with JavaScript files. - .. [6] TypeScript analysis is performed by running the JavaScript extractor with TypeScript enabled. This is the default for LGTM. + .. [5] JSX and Flow code, YAML, JSON, HTML, and XML files may also be analyzed with JavaScript files. + .. [6] TypeScript analysis is performed by running the JavaScript extractor with TypeScript enabled. This is the default for LGTM. diff --git a/docs/codeql/writing-codeql-queries/about-codeql-queries.rst b/docs/codeql/writing-codeql-queries/about-codeql-queries.rst index 171d2596b3f..cd36acd2ac8 100644 --- a/docs/codeql/writing-codeql-queries/about-codeql-queries.rst +++ b/docs/codeql/writing-codeql-queries/about-codeql-queries.rst @@ -57,8 +57,10 @@ Query metadata is used to identify your custom queries when they are added to th Queries that are contributed to the open source repository, added to a query pack in LGTM, or used to analyze a database with the :ref:`CodeQL CLI ` must have a query type (``@kind``) specified. The ``@kind`` property indicates how to interpret and display the results of the query analysis: - - Alert query metadata must contain ``@kind problem``. - - Path query metadata must contain ``@kind path-problem``. + - Alert query metadata must contain ``@kind problem`` to identify the results as a simple alert. + - Path query metadata must contain ``@kind path-problem`` to identify the results as an alert documented by a sequence of code locations. + - Diagnostic query metadata must contain ``@kind diagnostic`` to identify the results as troubleshooting data about the extraction process. + - Summary query metadata must contain ``@kind metric`` and ``@tags summary`` to identify the results as summary metrics for the CodeQL database. When you define the ``@kind`` property of a custom query you must also ensure that the rest of your query has the correct structure in order to be valid, as described below. @@ -114,6 +116,8 @@ You can modify the alert message defined in the final column of the ``select`` s Select clauses for path queries (``@kind path-problem``) are crafted to display both an alert and the source and sink of an associated path graph. For more information, see ":doc:`Creating path queries `." +Select clauses for diagnostic queries (``@kind diagnostic``) and summary metric queries (``@kind metric`` and ``@tags summary``) have different requirements. For examples, see the `diagnostic queries `__ and the `summary metric queries `__ in the CodeQL repository. + Viewing the standard CodeQL queries *********************************** diff --git a/docs/codeql/writing-codeql-queries/find-the-thief.rst b/docs/codeql/writing-codeql-queries/find-the-thief.rst index 131423b8058..ff9ee49f5dc 100644 --- a/docs/codeql/writing-codeql-queries/find-the-thief.rst +++ b/docs/codeql/writing-codeql-queries/find-the-thief.rst @@ -39,11 +39,13 @@ You start asking some creative questions and making notes of the answers so you +------+--------------------------------------------------------------------+--------+ | (7) | Is the thief taller than 180cm and shorter than 190cm? | no | +------+--------------------------------------------------------------------+--------+ -| (8) | Is the thief the tallest person in the village? | no | +| (8) | Is the thief the oldest person in the village? | no | +------+--------------------------------------------------------------------+--------+ -| (9) | Is the thief shorter than the average villager? | yes | +| (9) | Is the thief the tallest person in the village? | no | +------+--------------------------------------------------------------------+--------+ -| (10) | Is the thief the oldest person in the eastern part of the village? | yes | +| (10) | Is the thief shorter than the average villager? | yes | ++------+--------------------------------------------------------------------+--------+ +| (11) | Is the thief the oldest person in the eastern part of the village? | yes | +------+--------------------------------------------------------------------+--------+ There is too much information to search through by hand, so you decide to use your newly acquired QL skills to help you with your investigation... diff --git a/docs/ql-libraries/dataflow/dataflow.md b/docs/ql-libraries/dataflow/dataflow.md index 3c46436a87a..ea3379f3967 100644 --- a/docs/ql-libraries/dataflow/dataflow.md +++ b/docs/ql-libraries/dataflow/dataflow.md @@ -98,10 +98,6 @@ Recommendations: also work, but the upside of `use-use` steps is that sources defined in terms of variable reads just work out of the box. It also makes certain barrier-implementations simpler. -* A predicate `DataFlowCallable Node::getEnclosingCallable()` is required, and in - order to ensure appropriate join-orders, it is important that the QL compiler knows - that this predicate is functional. It can therefore be necessary to enclose the body - of this predicate in a `unique` aggregate. The shared library does not use `localFlowStep` nor `localFlow` but users of `DataFlow.qll` may expect the existence of `DataFlow::localFlowStep` and diff --git a/docs/ql-style-guide.md b/docs/ql-style-guide.md index bea84aff36e..0b0efef27cc 100644 --- a/docs/ql-style-guide.md +++ b/docs/ql-style-guide.md @@ -3,7 +3,7 @@ ## Introduction This document describes how to format the code you contribute to this repository. It covers aspects such as layout, white-space, naming, and documentation. Adhering to consistent standards makes code easier to read and maintain. Of course, these are only guidelines, and can be overridden as the need arises on a case-by-case basis. Where existing code deviates from these guidelines, prefer consistency with the surrounding code. -Note, if you use [CodeQL for Visual Studio Code](https://help.semmle.com/codeql/codeql-for-vscode/procedures/about-codeql-for-vscode.html), you can autoformat your query in the editor. +Note, if you use [CodeQL for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/about-codeql-for-visual-studio-code/), you can autoformat your query in the editor. Words in *italic* are defined in the [Glossary](#glossary). @@ -166,7 +166,7 @@ private predicate foo(Expr e, Expr p) { ``` ## Naming -1. Use [PascalCase](http://wiki.c2.com/?PascalCase) for: +1. Use [PascalCase](https://wiki.c2.com/?PascalCase) for: - `class` names - `module` names - `newtype` names @@ -249,7 +249,7 @@ For more information about documenting the code that you contribute to this repo 1. The `and` and `else` keywords *may* be placed on the same line as the closing parenthesis. 1. The `and` and `else` keywords *may* be "cuddled": `) else (` 1. *Always* qualify *calls* to predicates of the same class with `this`. -2. *Prefer* postfix casts `a.(Expr)` to prefix casts `(Expr)a`. +1. *Prefer* postfix casts `a.(Expr)` to prefix casts `(Expr)a`. ### Examples @@ -350,16 +350,16 @@ For more information about documenting the code that you contribute to this repo | Phrase | Meaning | |-------------|----------| -| *[annotation](https://help.semmle.com/QL/ql-handbook/language.html#annotations)* | An additional specifier used to modify a declaration, such as `private`, `override`, `deprecated`, `pragma`, `bindingset`, or `cached`. | +| *[annotation](https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#annotations)* | An additional specifier used to modify a declaration, such as `private`, `override`, `deprecated`, `pragma`, `bindingset`, or `cached`. | | *body* | The text inside `{ }`, `( )`, or each section of an `if`-`then`-`else` or `from`-`where`-`select`. | | *binary operator* | An operator with two operands, such as comparison operators, `and`, `or`, `implies`, or arithmetic operators. | | *call* | A *formula* that invokes a predicate, e.g. `this.isStatic()` or `calls(a,b)`. | -| *[conjunct](https://help.semmle.com/QL/ql-handbook/language.html#conjunctions)* | A formula that is an operand to an `and`. | +| *[conjunct](https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#conjunctions)* | A formula that is an operand to an `and`. | | *declaration* | A class, module, predicate, field or newtype. | -| *[disjunct](https://help.semmle.com/QL/ql-handbook/language.html#disjunctions)* | A formula that is an operand to an `or`. | -| *[formula](https://help.semmle.com/QL/ql-handbook/language.html#formulas)* | A logical expression, such as `A = B`, a *call*, a *quantifier*, `and`, `or`, `not`, `in` or `instanceof`. | +| *[disjunct](https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#disjunctions)* | A formula that is an operand to an `or`. | +| *[formula](https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#formulas)* | A logical expression, such as `A = B`, a *call*, a *quantifier*, `and`, `or`, `not`, `in` or `instanceof`. | | *should/should not/avoid/prefer* | Adhere to this rule wherever possible, where it makes sense. | | *may/can* | This is a reasonable alternative, to be used with discretion. | | *must/always/do not* | Always adhere to this rule. | -| *[quantifier/aggregation](https://help.semmle.com/QL/ql-handbook/language.html#aggregations)* | `exists`, `count`, `strictcount`, `any`, `forall`, `forex` and so on. | +| *[quantifier/aggregation](https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#aggregations)* | `exists`, `count`, `strictcount`, `any`, `forall`, `forex` and so on. | | *variable* | A parameter to a predicate, a field, a from variable, or a variable introduced by a *quantifier* or *aggregation*. | diff --git a/docs/qldoc-style-guide.md b/docs/qldoc-style-guide.md index fde4d3ea2f8..b8329e7a338 100644 --- a/docs/qldoc-style-guide.md +++ b/docs/qldoc-style-guide.md @@ -124,11 +124,12 @@ Certain special predicates should be documented consistently. * The location spans column `startcolumn` of line `startline` to * column `endcolumn` of line `endline` in file `filepath`. * For more information, see - * [Locations](https://help.semmle.com/QL/learn-ql/locations.html). + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) { ... } ``` + ## QLDoc for classes 1. Document classes using a noun phrase of the form `A that .` diff --git a/docs/query-help-style-guide.md b/docs/query-help-style-guide.md index 1b6f81e683d..f7377b6c955 100644 --- a/docs/query-help-style-guide.md +++ b/docs/query-help-style-guide.md @@ -4,11 +4,12 @@ When you contribute a new [supported query](supported-queries.md) to this repository, or add a custom query for analysis in LGTM, you should also write a query help file. This file provides detailed information about the purpose and use of the query, which is available to users in LGTM (for example [here](https://lgtm.com/rules/1506093386171/)) and on the query homepages: -* [C/C++ queries](https://help.semmle.com/wiki/display/CCPPOBJ/) -* [C# queries](https://help.semmle.com/wiki/display/CSHARP/) -* [Java queries](https://help.semmle.com/wiki/display/JAVA/) -* [JavaScript queries](https://help.semmle.com/wiki/display/JS/) -* [Python queries](https://help.semmle.com/wiki/display/PYTHON/) +* [C/C++ queries](https://codeql.github.com/codeql-query-help/cpp/) +* [C# queries](https://codeql.github.com/codeql-query-help/csharp/) +* [Go queries](https://codeql.github.com/codeql-query-help/go/) +* [Java queries](https://codeql.github.com/codeql-query-help/java/) +* [JavaScript queries](https://codeql.github.com/codeql-query-help/javascript/) +* [Python queries](https://codeql.github.com/codeql-query-help/python/) ### Location and file name @@ -18,57 +19,57 @@ Query help files must have the same base name as the query they describe and mus Query help files are written using a custom XML format, and stored in a file with a `.qhelp` extension. The basic structure is as follows: -``` +```xml CONTAINS one or more section-level elements ``` -The header and single top-level `` element are both mandatory. +The header and single top-level `` element are both mandatory. ### Section-level elements Section-level elements are used to group the information within the query help file. All query help files should include at least the following section elements, in the order specified: -1. `overview`—a short summary of the issue that the query identifies, including an explanation of how it could affect the behavior of the program. -2. `recommendation`—information on how to fix the issue highlighted by the query. -3. `example`—an example of code showing the problem. Where possible, this section should also include a solution to the issue. -4. `references`—relevant references, such as authoritative sources on language semantics and best practice. +1. `overview`—a short summary of the issue that the query identifies, including an explanation of how it could affect the behavior of the program. +2. `recommendation`—information on how to fix the issue highlighted by the query. +3. `example`—an example of code showing the problem. Where possible, this section should also include a solution to the issue. +4. `references`—relevant references, such as authoritative sources on language semantics and best practice. -For further information about the other section-level, block, list and table elements supported by query help files, see [Query help files](https://help.semmle.com/QL/learn-ql/ql/writing-queries/query-help.html) on help.semmle.com. +For further information about the other section-level, block, list and table elements supported by query help files, see [Query help files](https://codeql.github.com/docs/writing-codeql-queries/query-help-files/) on codeql.github.com. ## English style You should write the overview and recommendation elements in simple English that is easy to follow. You should: -* Use simple sentence structures and avoid complex or academic language. -* Avoid colloquialisms and contractions. -* Use US English spelling throughout. -* Use words that are in common usage. +* Use simple sentence structures and avoid complex or academic language. +* Avoid colloquialisms and contractions. +* Use US English spelling throughout. +* Use words that are in common usage. ## Code examples Whenever possible, you should include a code example that helps to explain the issue you are highlighting. Any code examples that you include should adhere to the following guidelines: -* The example should be less than 20 lines, but it should still clearly illustrate the issue that the query identifies. If appropriate, then the example may also be runnable. -* Put the code example after the recommendation element where possible. Only include an example in the description element if absolutely necessary. -* If you are using an example to illustrate the solution to a problem, and the change required is minor, avoid repeating the whole example. It is preferable to either describe the change required or to include a smaller snippet of the corrected code. -* Clearly indicate which of the samples is an example of bad coding practice and which is recommended practice. -* Define the code examples in `src` files. The language is inferred from the file extension: +* The example should be less than 20 lines, but it should still clearly illustrate the issue that the query identifies. If appropriate, then the example may also be runnable. +* Put the code example after the recommendation element where possible. Only include an example in the description element if absolutely necessary. +* If you are using an example to illustrate the solution to a problem, and the change required is minor, avoid repeating the whole example. It is preferable to either describe the change required or to include a smaller snippet of the corrected code. +* Clearly indicate which of the samples is an example of bad coding practice and which is recommended practice. +* Define the code examples in `src` files. The language is inferred from the file extension: -``` - -

    This example highlights poor coding practice

    + ```xml + +

    This example highlights poor coding practice

    - + -

    This example shows how to fix the code

    +

    This example shows how to fix the code

    - -
    -``` + +
    + ``` Note, if any code words are included in the `overview` and `recommendation` sections, they should be formatted with ` ... ` for emphasis. @@ -90,7 +91,7 @@ Note, & symbols need to be replaced by \&. The symbol will be displayed corr ### Academic papers -If you are citing an academic paper, we recommend adopting the reference style of the journal that you are citing. For example: +If you are citing an academic paper, we recommend adopting the reference style of the journal that you are citing. For example: >S. R. Chidamber and C. F. Kemerer, _A metrics suite for object-oriented design_. IEEE Transactions on Software Engineering, 20(6):476-493, 1994. @@ -109,11 +110,11 @@ For example: If your query checks code for a CWE weakness, you should use the `@tags` element in the query file to reference the associated CWEs, as explained [here](query-metadata-style-guide.md). When you use these tags, a link to the appropriate entry from the [MITRE.org](https://cwe.mitre.org/scoring/index.html) site will automatically appear as a reference in the output HTML file. -## Query help example +## Query help example -The following example is a query help file for a query from the standard query suite for Java: +The following example is a query help file for a query from the standard query suite for Java: -``` +```xml diff --git a/docs/query-metadata-style-guide.md b/docs/query-metadata-style-guide.md index 21e5897e243..143564d49b0 100644 --- a/docs/query-metadata-style-guide.md +++ b/docs/query-metadata-style-guide.md @@ -8,27 +8,27 @@ This document outlines the structure of CodeQL query files. You should adopt thi Query files have the extension `.ql`. Each file has two distinct areas: -* Metadata area–displayed at the top of the file, contains the metadata that defines how results for the query are interpreted and gives a brief description of the purpose of the query. -* Query definition–defined using QL. The query includes a select statement, which defines the content and format of the results. For further information about writing QL, see the following topics: - * [Learning CodeQL](https://help.semmle.com/QL/learn-ql/index.html) - * [QL language reference](https://help.semmle.com/QL/ql-handbook/index.html) - * [CodeQL style guide](https://github.com/github/codeql/blob/main/docs/ql-style-guide.md) +* Metadata area–displayed at the top of the file, contains the metadata that defines how results for the query are interpreted and gives a brief description of the purpose of the query. +* Query definition–defined using QL. The query includes a select statement, which defines the content and format of the results. For further information about writing QL, see the following topics: + * [CodeQL documentation](https://codeql.github.com/docs/) + * [QL language reference](https://codeql.github.com/docs/ql-language-reference/) + * [CodeQL style guide](ql-style-guide.md) -For examples of query files for the languages supported by CodeQL, visit the following links: +For examples of query files for the languages supported by CodeQL, visit the following links: -* [C/C++ queries](https://help.semmle.com/wiki/display/CCPPOBJ/) -* [C# queries](https://help.semmle.com/wiki/display/CSHARP/) -* [Go queries](https://help.semmle.com/wiki/display/GO/) -* [Java queries](https://help.semmle.com/wiki/display/JAVA/) -* [JavaScript queries](https://help.semmle.com/wiki/display/JS/) -* [Python queries](https://help.semmle.com/wiki/display/PYTHON/) +* [C/C++ queries](https://codeql.github.com/codeql-query-help/cpp/) +* [C# queries](https://codeql.github.com/codeql-query-help/csharp/) +* [Go queries](https://codeql.github.com/codeql-query-help/go/) +* [Java queries](https://codeql.github.com/codeql-query-help/java/) +* [JavaScript queries](https://codeql.github.com/codeql-query-help/javascript/) +* [Python queries](https://codeql.github.com/codeql-query-help/python/) ## Metadata area Query file metadata contains important information that defines the identifier and purpose of the query. The metadata is included as the content of a valid [QLDoc](https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#qldoc) comment, on lines with leading whitespace followed by `*`, between an initial `/**` and a trailing `*/`. For example: -``` +```ql /** * @name Useless assignment to local variable * @description An assignment to a local variable that is not used later on, or whose value is always @@ -42,7 +42,7 @@ Query file metadata contains important information that defines the identifier a */ ``` -To help others use your query, and to ensure that the query works correctly on LGTM, you should include all of the required information outlined below in the metadata, and as much of the optional information as possible. For further information on query metadata see [Metadata for CodeQL queries](https://help.semmle.com/QL/learn-ql/ql/writing-queries/query-metadata.html) on help.semmle.com. +To help others use your query, and to ensure that the query works correctly on LGTM, you should include all of the required information outlined below in the metadata, and as much of the optional information as possible. For further information on query metadata see [Metadata for CodeQL queries](https://codeql.github.com/docs/writing-codeql-queries/metadata-for-codeql-queries/) on codeql.github.com. @@ -51,10 +51,10 @@ To help others use your query, and to ensure that the query works correctly on L You must specify an `@name` property for your query. This property defines the display name for the query. Query names should use sentence capitalization, but not include a full stop. For example: -* `@name Access to variable in enclosing class` -* `@name Array argument size mismatch` -* `@name Reference equality test on strings` -* `@name Return statement outside function` +* `@name Access to variable in enclosing class` +* `@name Array argument size mismatch` +* `@name Reference equality test on strings` +* `@name Return statement outside function` ### Query descriptions `@description` @@ -62,38 +62,38 @@ You must specify an `@name` property for your query. This property defines the d You must define an `@description` property for your query. This property defines a short help message. Query descriptions should be written as a sentence or short-paragraph of plain prose, with sentence capitalization and full stop. The preferred pattern for alert queries is "Syntax X causes behavior Y." Any code elements included in the description should be enclosed in single quotes. For example: -* `@description Using a format string with an incorrect format causes a 'System.FormatException'.` -* `@description Commented-out code makes the remaining code more difficult to read.` +* `@description Using a format string with an incorrect format causes a 'System.FormatException'.` +* `@description Commented-out code makes the remaining code more difficult to read.` ### Query ID `@id` You must specify an `@id` property for your query. It must be unique and should follow the standard CodeQL convention. That is, it should begin with the 'language code' for the language that the query analyzes followed by a forward slash. The following language codes are supported: -* C and C++: `cpp` -* C#: `cs` -* Go: `go` -* Java: `java` -* JavaScript and TypeScript: `js` -* Python: `py` +* C and C++: `cpp` +* C#: `cs` +* Go: `go` +* Java: `java` +* JavaScript and TypeScript: `js` +* Python: `py` The `@id` should consist of a short noun phrase that identifies the issue that the query highlights. For example: -* `@id cs/command-line-injection` -* `@id java/string-concatenation-in-loop` +* `@id cs/command-line-injection` +* `@id java/string-concatenation-in-loop` Further terms can be added to the `@id` to group queries that, for example, highlight similar issues or are of particular relevance to a certain framework. For example: -* `@id js/angular-js/missing-explicit-injection` -* `@id js/angular-js/duplicate-dependency` +* `@id js/angular-js/missing-explicit-injection` +* `@id js/angular-js/duplicate-dependency` Note, `@id` properties should be consistent for queries that highlight the same issue for different languages. For example, the following queries identify format strings that contain unsanitized input in Java and C++ code respectively: -* `@id java/tainted-format-string` -* `@id cpp/tainted-format-string` +* `@id java/tainted-format-string` +* `@id cpp/tainted-format-string` ### Query type `@kind` @@ -102,48 +102,48 @@ Note, `@id` properties should be consistent for queries that highlight the same -* alerts (`@kind problem`) -* alerts containing path information (`@kind path-problem`) +* alerts (`@kind problem`) +* alerts containing path information (`@kind path-problem`) Alert queries (`@kind problem` or `path-problem`) support two further properties. These are added by GitHub staff after the query has been tested, prior to deployment to LGTM. The following information is for reference: -* `@precision`–broadly indicates the proportion of query results that are true positives, while also considering their context and relevance: - * `low ` - * `medium ` - * `high ` - * `very-high` -* `@problem.severity`–defines the level of severity of the alert: - * `error`–an issue that is likely to cause incorrect program behavior, for example a crash or vulnerability. - * `warning`–an issue that indicates a potential problem in the code, or makes the code fragile if another (unrelated) part of code is changed. - * `recommendation`–an issue where the code behaves correctly, but it could be improved. +* `@precision`–broadly indicates the proportion of query results that are true positives, while also considering their context and relevance: + * `low` + * `medium` + * `high` + * `very-high` +* `@problem.severity`–defines the level of severity of the alert: + * `error`–an issue that is likely to cause incorrect program behavior, for example a crash or vulnerability. + * `warning`–an issue that indicates a potential problem in the code, or makes the code fragile if another (unrelated) part of code is changed. + * `recommendation`–an issue where the code behaves correctly, but it could be improved. -The values of `@precision` and `@problem.severity` assigned to a query that is part of the standard set determine how the results are displayed by LGTM. See [About alerts](https://help.semmle.com/lgtm-enterprise/user/help/about-alerts.html) and [Alert interest](https://lgtm.com/help/lgtm/alert-interest) for further information. For information about using custom queries in LGTM on a 'per-project' basis, see [Writing custom queries to include in LGTM analysis](https://lgtm.com/help/lgtm/writing-custom-queries) and [About adding custom queries](https://help.semmle.com/lgtm-enterprise/admin/help/about-adding-custom-queries.html). +The values of `@precision` and `@problem.severity` assigned to a query that is part of the standard set determine how the results are displayed by LGTM. See [About alerts](https://help.semmle.com/lgtm-enterprise/user/help/about-alerts.html) and [Alert interest](https://lgtm.com/help/lgtm/alert-interest) for further information. For information about using custom queries in LGTM on a 'per-project' basis, see [Writing custom queries to include in LGTM analysis](https://lgtm.com/help/lgtm/writing-custom-queries) and [About adding custom queries](https://help.semmle.com/lgtm-enterprise/admin/help/about-adding-custom-queries.html). ## Query tags `@tags` The `@tags` property is used to define categories that the query relates to. Each query should belong to one (or more, if necessary) of the following four top-level categories: -* `@tags correctness`–for queries that detect incorrect program behavior. -* `@tags maintainability`–for queries that detect patterns that make it harder for developers to make changes to the code. -* `@tags readability`–for queries that detect confusing patterns that make it harder for developers to read the code. -* `@tags security`–for queries that detect security weaknesses. See below for further information. +* `@tags correctness`–for queries that detect incorrect program behavior. +* `@tags maintainability`–for queries that detect patterns that make it harder for developers to make changes to the code. +* `@tags readability`–for queries that detect confusing patterns that make it harder for developers to read the code. +* `@tags security`–for queries that detect security weaknesses. See below for further information. There are also more specific `@tags` that can be added. See, the following pages for examples of the low-level tags: -* [C/C++ queries](https://help.semmle.com/wiki/display/CCPPOBJ/) -* [C# queries](https://help.semmle.com/wiki/display/CSHARP/) -* [Go queries](https://help.semmle.com/wiki/display/GO/) -* [Java queries](https://help.semmle.com/wiki/display/JAVA/) -* [JavaScript queries](https://help.semmle.com/wiki/display/JS/) -* [Python queries](https://help.semmle.com/wiki/display/PYTHON/) +* [C/C++ queries](https://codeql.github.com/codeql-query-help/cpp/) +* [C# queries](https://codeql.github.com/codeql-query-help/csharp/) +* [Go queries](https://codeql.github.com/codeql-query-help/go/) +* [Java queries](https://codeql.github.com/codeql-query-help/java/) +* [JavaScript queries](https://codeql.github.com/codeql-query-help/javascript/) +* [Python queries](https://codeql.github.com/codeql-query-help/python/) If necessary, you can also define your own low-level tags to categorize the queries specific to your project or organization. When creating your own tags, you should: -* Use all lower-case letters, including for acronyms and proper nouns, with no spaces. All characters apart from * and @ are accepted. -* Use a forward slash / to indicate a hierarchical relationship between tags if necessary. For example, a query with tag `foo/bar` is also interpreted as also having tag `foo`, but not `bar`. -* Use a single-word `@tags` name. Multiple words, separated with hyphens, can be used for clarity if necessary. +* Use all lower-case letters, including for acronyms and proper nouns, with no spaces. All characters apart from * and @ are accepted. +* Use a forward slash / to indicate a hierarchical relationship between tags if necessary. For example, a query with tag `foo/bar` is also interpreted as also having tag `foo`, but not `bar`. +* Use a single-word `@tags` name. Multiple words, separated with hyphens, can be used for clarity if necessary. #### Security query `@tags` @@ -155,7 +155,7 @@ If your query is a security query, use one or more `@tags` to associate it with ||`external/cwe/cwe-036` | ||`external/cwe/cwe-073` | -When you tag a query like this, the associated CWE pages from [MITRE.org](http://cwe.mitre.org/index.html) will automatically appear in the reference section of its associated qhelp file. +When you tag a query like this, the associated CWE pages from [MITRE.org](https://cwe.mitre.org/index.html) will automatically appear in the reference section of its associated qhelp file. ## QL area @@ -163,20 +163,20 @@ When you tag a query like this, the associated CWE pages from [MITRE.org](http:/ The select clause of each alert query defines the alert message that is displayed for each result found by the query. Alert messages are strings that concisely describe the problem that the alert is highlighting and, if possible, also provide some context. For consistency, alert messages should adhere to the following guidelines: -* Each message should be a complete, standalone sentence. That is, it should be capitalized and have proper punctuation, including a full stop. -* The message should factually describe the problem that is being highlighted–it should not contain recommendations about how to fix the problem or value judgements. -* Program element references should be in 'single quotes' to distinguish them from ordinary words. Quotes are not needed around substitutions ($@). -* Avoid constant alert message strings and include some context, if possible. For example, `The class 'Foo' is duplicated as 'Bar'.` is preferable to `This class is duplicated here.` -* Where you reference another program element, link to it if possible using a substitution (`$@`). Links should be used inline in the sentence, rather than as parenthesised lists or appositions. -* When a message contains multiple links, construct a sentence that has the most variable link (that is, the link with most targets) last. For further information, see [Defining the results of a query](https://help.semmle.com/QL/learn-ql/ql/writing-queries/select-statement.html). +* Each message should be a complete, standalone sentence. That is, it should be capitalized and have proper punctuation, including a full stop. +* The message should factually describe the problem that is being highlighted–it should not contain recommendations about how to fix the problem or value judgements. +* Program element references should be in 'single quotes' to distinguish them from ordinary words. Quotes are not needed around substitutions (`$@`). +* Avoid constant alert message strings and include some context, if possible. For example, `The class 'Foo' is duplicated as 'Bar'.` is preferable to `This class is duplicated here.` +* Where you reference another program element, link to it if possible using a substitution (`$@`). Links should be used inline in the sentence, rather than as parenthesised lists or appositions. +* When a message contains multiple links, construct a sentence that has the most variable link (that is, the link with most targets) last. For further information, see [Defining the results of a query](https://codeql.github.com/docs/writing-codeql-queries/defining-the-results-of-a-query/). For examples of select clauses and alert messages, see the query source files at the following pages: -* [C/C++ queries](https://help.semmle.com/wiki/display/CCPPOBJ/) -* [C# queries](https://help.semmle.com/wiki/display/CSHARP/) -* [Go queries](https://help.semmle.com/wiki/display/GO/) -* [Java queries](https://help.semmle.com/wiki/display/JAVA/) -* [JavaScript queries](https://help.semmle.com/wiki/display/JS/) -* [Python queries](https://help.semmle.com/wiki/display/PYTHON/) +* [C/C++ queries](https://codeql.github.com/codeql-query-help/cpp/) +* [C# queries](https://codeql.github.com/codeql-query-help/csharp/) +* [Go queries](https://codeql.github.com/codeql-query-help/go/) +* [Java queries](https://codeql.github.com/codeql-query-help/java/) +* [JavaScript queries](https://codeql.github.com/codeql-query-help/javascript/) +* [Python queries](https://codeql.github.com/codeql-query-help/python/) -For further information on query writing, see [CodeQL queries](https://help.semmle.com/QL/learn-ql/ql/writing-queries/writing-queries.html). For more information on learning CodeQL, see [Learning CodeQL](https://help.semmle.com/QL/learn-ql/index.html). +For further information on query writing, see [CodeQL queries](https://codeql.github.com/docs/writing-codeql-queries/codeql-queries/). For more information on learning CodeQL, see [CodeQL documentation](https://codeql.github.com/docs/). diff --git a/docs/supported-queries.md b/docs/supported-queries.md index 55530c2fa12..2d76906e82f 100644 --- a/docs/supported-queries.md +++ b/docs/supported-queries.md @@ -27,8 +27,8 @@ The process must begin with the first step and must conclude with the final step Add one or more unit tests for the query (and for any library changes you make) to the `ql//ql/test/experimental` directory. Tests for library changes go into the `library-tests` subdirectory, and tests for queries go into `query-tests` with their relative path mirroring the query's location under `ql//ql/src/experimental`. - - See the section on [Testing custom queries](https://help.semmle.com/codeql/codeql-cli/procedures/test-queries.html) in the [CodeQL documentation](https://help.semmle.com/codeql/) for more information. - - See [C/C++ CodeQL tests](/cpp/ql/test/README.md) for more information about contributing tests for C/C++ queries in particular. + - See the section on [Testing custom queries](https://help.semmle.com/codeql/codeql-cli/procedures/test-queries.html) in the [CodeQL documentation](https://codeql.github.com/docs/) for more information. + - See [C/C++ CodeQL tests](/cpp/ql/test/README.md) for more information about contributing tests for C/C++ queries in particular. 4. **Test for correctness on real-world code** @@ -46,10 +46,10 @@ The process must begin with the first step and must conclude with the final step QL performance profiling and tuning is an advanced topic, and some tasks will require assistance from GitHub employees. With that said, there are several things you can do. - - Understand [the evaluation model of QL](https://help.semmle.com/QL/ql-handbook/evaluation.html). It's more similar to SQL than to any mainstream programming language. + - Understand [the evaluation model of QL](https://codeql.github.com/docs/ql-language-reference/evaluation-of-ql-programs/). It's more similar to SQL than to any mainstream programming language. - Most performance tuning in QL boils down to computing as few tuples (rows of data) as possible. As a mental model, think of predicate evaluation as enumerating all combinations of parameters that satisfy the predicate body. This includes the implicit parameters `this` and `result`. - The major libraries in CodeQL are _cached_ and will only be computed once for the entire suite of queries. The first query that needs a cached _stage_ will trigger its evaluation. This means that query authors should usually only look at the run time of the last stage of evaluation. - - In [the settings for the VSCode extension](https://help.semmle.com/codeql/codeql-for-vscode/reference/settings.html), check the box "Running Queries: Debug" (`codeQL.runningQueries.debug`). Then find "CodeQL Query Server" in the VSCode Output panel (View -> Output) and capture the output when running the query. That output contains timing and tuple counts for all computed predicates. + - In [the settings for the VSCode extension](https://codeql.github.com/docs/codeql-for-visual-studio-code/customizing-settings/), check the box "Running Queries: Debug" (`codeQL.runningQueries.debug`). Then find "CodeQL Query Server" in the VSCode Output panel (View -> Output) and capture the output when running the query. That output contains timing and tuple counts for all computed predicates. - To clear the entire cache, invoke "CodeQL: Clear Cache" from the VSCode command palette. 6. **Make sure your query has the correct metadata** @@ -78,4 +78,4 @@ The process must begin with the first step and must conclude with the final step - The structure of an `experimental` subdirectory mirrors the structure of its parent directory, so this step may just be a matter of removing the `experimental/` prefix of the query and test paths. Be sure to also edit any references to the query path in tests. - Add the query to one of the legacy suite files in `ql//config/suites//` if it exists. Note that there are separate suite directories for C and C++, `c` and `cpp` respectively, and the query should be added to one or both as appropriate. - Add a release note to `change-notes//analysis-.md`. - - Your pull request will be flagged automatically for a review by the documentation team to ensure that the query help file is ready for wider use. + - Your pull request will be flagged automatically for a review by the documentation team to ensure that the query help file is ready for wider use. diff --git a/java/change-notes/2021-03-10-guava-base.md b/java/change-notes/2021-03-10-guava-base.md new file mode 100644 index 00000000000..f2bdd9fe95d --- /dev/null +++ b/java/change-notes/2021-03-10-guava-base.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Increased coverage of the Guava framework by modelling additional classes in the `com.google.common.base` package. This may result in more results for security queries on projects where the Guava framework is used. \ No newline at end of file diff --git a/java/change-notes/2021-03-11-commons-strbuilder.md b/java/change-notes/2021-03-11-commons-strbuilder.md new file mode 100644 index 00000000000..ce8f647ab0f --- /dev/null +++ b/java/change-notes/2021-03-11-commons-strbuilder.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Added support for the Apache Commons Lang and Commons Text StrBuilder class, and its successor TextStringBuilder. diff --git a/java/change-notes/2021-03-18-commons-tostring-builder.md b/java/change-notes/2021-03-18-commons-tostring-builder.md new file mode 100644 index 00000000000..41ccfb95237 --- /dev/null +++ b/java/change-notes/2021-03-18-commons-tostring-builder.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Added models for Apache Commons Lang's `ToStringBuilder` class. This may lead to more results from any data-flow query where ToStringBuilder operations fall between the relevant untrusted source and vulnerable sink. diff --git a/java/change-notes/2021-04-14-membertype.md b/java/change-notes/2021-04-14-membertype.md new file mode 100644 index 00000000000..910a5c91423 --- /dev/null +++ b/java/change-notes/2021-04-14-membertype.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* A CodeQL class `MemberType` is introduced to describe nested classes. Its `getQualifiedName` method returns `$`-delimited nested type names (for example, `mypackage.Outer$Middle$Inner`), where previously the same type would be named differently depending on whether it was addressed as a `NestedType` or a `Member`. diff --git a/java/change-notes/2021-04-26-xpath-injection-query.md b/java/change-notes/2021-04-26-xpath-injection-query.md new file mode 100644 index 00000000000..e049a9af764 --- /dev/null +++ b/java/change-notes/2021-04-26-xpath-injection-query.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The query "XPath injection" (`java/xml/xpath-injection`) has been promoted from experimental to the main query pack. Its results will now appear by default. This query was originally [submitted as an experimental query by @SpaceWhite](https://github.com/github/codeql/pull/2800) \ No newline at end of file diff --git a/java/change-notes/2021-05-03-guava-first-non-null.md b/java/change-notes/2021-05-03-guava-first-non-null.md new file mode 100644 index 00000000000..3cd307d9455 --- /dev/null +++ b/java/change-notes/2021-05-03-guava-first-non-null.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Increase coverage of the Guava framework by adding support for `com.google.common.base.MoreObjects#firstNonNull`. diff --git a/java/change-notes/2021-05-03-jackson-dataflow-deserialization.md b/java/change-notes/2021-05-03-jackson-dataflow-deserialization.md new file mode 100644 index 00000000000..475f8dbee4f --- /dev/null +++ b/java/change-notes/2021-05-03-jackson-dataflow-deserialization.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Increase coverage of dataflow through Jackson JSON deserialized objects. diff --git a/java/change-notes/2021-05-04-jexl-injection-query.md b/java/change-notes/2021-05-04-jexl-injection-query.md new file mode 100644 index 00000000000..4dad3c4a8f9 --- /dev/null +++ b/java/change-notes/2021-05-04-jexl-injection-query.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The query "Expression language injection (JEXL)" (`java/jexl-expression-injection`) has been promoted from experimental to the main query pack. Its results will now appear by default. This query was originally [submitted as an experimental query by @artem-smotrakov](https://github.com/github/codeql/pull/4965) \ No newline at end of file diff --git a/java/change-notes/2021-05-05-kryo-improvements.md b/java/change-notes/2021-05-05-kryo-improvements.md new file mode 100644 index 00000000000..dbacb10099b --- /dev/null +++ b/java/change-notes/2021-05-05-kryo-improvements.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* Add support for version 5 of the Kryo serialization/deserialization framework. +* Add support for detecting safe uses of Kryo utilizing `KryoPool.Builder`. [#4992](https://github.com/github/codeql/issues/4992) diff --git a/java/change-notes/2021-05-12-xxe-fp-fix.md b/java/change-notes/2021-05-12-xxe-fp-fix.md new file mode 100644 index 00000000000..dd42bc71256 --- /dev/null +++ b/java/change-notes/2021-05-12-xxe-fp-fix.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The query "Resolving XML external entity in user-controlled data" (`java/xxe`) has been improved to report fewer false positives when a Builder / Factory (e.g. an `XMLInputFactory`) is configured safely by using a boxed boolean as second argument to one or more of its configuration methods. diff --git a/java/change-notes/2021-05-14-close-resource-leaks-improvements.md b/java/change-notes/2021-05-14-close-resource-leaks-improvements.md new file mode 100644 index 00000000000..08a77840a1f --- /dev/null +++ b/java/change-notes/2021-05-14-close-resource-leaks-improvements.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The "Potential input resource leak" (`java/input-resource-leak`) and "Potential output resource leak" (`java/output-resource-leak`) queries no longer confuse `java.io` classes such as `Reader` with others that happen to share the same base name. Additionally the number of false positives has been reduced by recognizing `CharArrayReader` and `CharArrayWriter` as types that don't need to be closed. diff --git a/java/change-notes/2021-05-20-savedrequest-taintsources.md b/java/change-notes/2021-05-20-savedrequest-taintsources.md new file mode 100644 index 00000000000..d22cb000c64 --- /dev/null +++ b/java/change-notes/2021-05-20-savedrequest-taintsources.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* Invocations of methods from `org.springframework.security.web.savedrequest.SavedRequest` + have been added as sources of tainted data for all security queries. diff --git a/java/change-notes/2021-05-28-remove-senderror-xss-sink.md b/java/change-notes/2021-05-28-remove-senderror-xss-sink.md new file mode 100644 index 00000000000..5dd245bcb52 --- /dev/null +++ b/java/change-notes/2021-05-28-remove-senderror-xss-sink.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The query "Cross-site scripting" (`java/xss`) has been improved to report fewer false positives by removing the `javax.servlet.http.HttpServletResponse.sendError` sink since Servlet API implementations generally already escape the error message, preventing script injection. diff --git a/java/change-notes/2021-06-01-collection-flow.md b/java/change-notes/2021-06-01-collection-flow.md new file mode 100644 index 00000000000..5da2e78d1df --- /dev/null +++ b/java/change-notes/2021-06-01-collection-flow.md @@ -0,0 +1,5 @@ +lgtm,codescanning +* Data flow now tracks steps through collections and arrays more precisely. + That means that collection and array read steps are now matched up with + preceding store steps. This results in increased precision for all flow-based + queries, in particular most of the security queries. diff --git a/java/change-notes/2021-06-01-statement-toString.md b/java/change-notes/2021-06-01-statement-toString.md new file mode 100644 index 00000000000..2410214db80 --- /dev/null +++ b/java/change-notes/2021-06-01-statement-toString.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The CodeQL predicate `toString()` has been overridden for subclasses of `Stmt` to be more descriptive. diff --git a/java/change-notes/2021-06-11-tainted-key-read-steps.md b/java/change-notes/2021-06-11-tainted-key-read-steps.md new file mode 100644 index 00000000000..a0e03053c35 --- /dev/null +++ b/java/change-notes/2021-06-11-tainted-key-read-steps.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* Data flow now propagates taint from tainted Maps to read steps of their keys (e.g. `tainted.keySet()`). diff --git a/java/documentation/library-coverage/cwe-sink.csv b/java/documentation/library-coverage/cwe-sink.csv new file mode 100644 index 00000000000..796f237c313 --- /dev/null +++ b/java/documentation/library-coverage/cwe-sink.csv @@ -0,0 +1,8 @@ +CWE,Sink identifier,Label +CWE‑089,sql,SQL injection +CWE‑022,create-file,Path injection +CWE‑036,url-open-stream,Path traversal +CWE‑094,bean-validation,Code injection +CWE‑319,open-url,Cleartext transmission +CWE‑079,xss,Cross-site scripting +CWE‑090,ldap,LDAP injection \ No newline at end of file diff --git a/java/documentation/library-coverage/flow-model-coverage.csv b/java/documentation/library-coverage/flow-model-coverage.csv new file mode 100644 index 00000000000..464228bb022 --- /dev/null +++ b/java/documentation/library-coverage/flow-model-coverage.csv @@ -0,0 +1,42 @@ +package,sink,source,summary,sink:bean-validation,sink:create-file,sink:header-splitting,sink:information-leak,sink:ldap,sink:open-url,sink:set-hostname-verifier,sink:url-open-stream,sink:xpath,sink:xss,source:remote,summary:taint,summary:value +android.util,,16,,,,,,,,,,,,16,, +android.webkit,3,2,,,,,,,,,,,3,2,, +com.esotericsoftware.kryo.io,,,1,,,,,,,,,,,,1, +com.esotericsoftware.kryo5.io,,,1,,,,,,,,,,,,1, +com.fasterxml.jackson.databind,,,2,,,,,,,,,,,,2, +com.google.common.base,,,28,,,,,,,,,,,,22,6 +com.google.common.io,6,,69,,,,,,,,6,,,,68,1 +com.unboundid.ldap.sdk,17,,,,,,,17,,,,,,,, +java.beans,,,1,,,,,,,,,,,,1, +java.io,3,,20,,3,,,,,,,,,,20, +java.lang,,,1,,,,,,,,,,,,1, +java.net,2,3,4,,,,,,2,,,,,3,4, +java.nio,10,,2,,10,,,,,,,,,,2, +java.util,,,13,,,,,,,,,,,,13, +javax.naming.directory,1,,,,,,,1,,,,,,,, +javax.net.ssl,2,,,,,,,,,2,,,,,, +javax.servlet,4,21,2,,,3,1,,,,,,,21,2, +javax.validation,1,1,,1,,,,,,,,,,1,, +javax.ws.rs.core,1,,,,,1,,,,,,,,,, +javax.xml.transform.sax,,,4,,,,,,,,,,,,4, +javax.xml.transform.stream,,,2,,,,,,,,,,,,2, +javax.xml.xpath,3,,,,,,,,,,,3,,,, +org.apache.commons.codec,,,2,,,,,,,,,,,,2, +org.apache.commons.io,,,22,,,,,,,,,,,,22, +org.apache.commons.lang3,,,313,,,,,,,,,,,,299,14 +org.apache.commons.text,,,203,,,,,,,,,,,,203, +org.apache.directory.ldap.client.api,1,,,,,,,1,,,,,,,, +org.apache.hc.core5.function,,,1,,,,,,,,,,,,1, +org.apache.hc.core5.http,1,2,39,,,,,,,,,,1,2,39, +org.apache.hc.core5.net,,,2,,,,,,,,,,,,2, +org.apache.hc.core5.util,,,22,,,,,,,,,,,,18,4 +org.apache.http,2,3,66,,,,,,,,,,2,3,59,7 +org.dom4j,20,,,,,,,,,,,20,,,, +org.springframework.ldap.core,14,,,,,,,14,,,,,,,, +org.springframework.security.web.savedrequest,,6,,,,,,,,,,,,6,, +org.springframework.web.client,,3,,,,,,,,,,,,3,, +org.springframework.web.context.request,,8,,,,,,,,,,,,8,, +org.springframework.web.multipart,,12,,,,,,,,,,,,12,, +org.xml.sax,,,1,,,,,,,,,,,,1, +org.xmlpull.v1,,3,,,,,,,,,,,,3,, +play.mvc,,4,,,,,,,,,,,,4,, diff --git a/java/documentation/library-coverage/flow-model-coverage.rst b/java/documentation/library-coverage/flow-model-coverage.rst new file mode 100644 index 00000000000..b9b54aad158 --- /dev/null +++ b/java/documentation/library-coverage/flow-model-coverage.rst @@ -0,0 +1,19 @@ +Java framework & library support +================================ + +.. csv-table:: + :header-rows: 1 + :class: fullWidthTable + :widths: auto + + Framework / library,Package,Remote flow sources,Taint & value steps,Sinks (total),`CWE‑022` :sub:`Path injection`,`CWE‑036` :sub:`Path traversal`,`CWE‑079` :sub:`Cross-site scripting`,`CWE‑089` :sub:`SQL injection`,`CWE‑090` :sub:`LDAP injection`,`CWE‑094` :sub:`Code injection`,`CWE‑319` :sub:`Cleartext transmission` + Android,``android.*``,18,,3,,,3,,,, + Apache,``org.apache.*``,5,648,4,,,3,,1,, + `Apache Commons IO `_,``org.apache.commons.io``,,22,,,,,,,, + Google,``com.google.common.*``,,97,6,,6,,,,, + Java Standard Library,``java.*``,3,41,15,13,,,,,,2 + Java extensions,``javax.*``,22,8,12,,,,,1,1, + `Spring `_,``org.springframework.*``,29,,14,,,,,14,, + Others,"``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.databind``, ``com.unboundid.ldap.sdk``, ``org.dom4j``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``",7,5,37,,,,,17,, + Totals,,84,821,91,13,6,6,,33,1,2 + diff --git a/java/documentation/library-coverage/frameworks.csv b/java/documentation/library-coverage/frameworks.csv new file mode 100644 index 00000000000..0e39bc51836 --- /dev/null +++ b/java/documentation/library-coverage/frameworks.csv @@ -0,0 +1,8 @@ +Framework name,URL,Package prefix +Java Standard Library,,java.* +Google,,com.google.common.* +Apache,,org.apache.* +Apache Commons IO,https://commons.apache.org/proper/commons-io/,org.apache.commons.io +Android,,android.* +Spring,https://spring.io/,org.springframework.* +Java extensions,,javax.* \ No newline at end of file diff --git a/java/ql/src/Diagnostics/DiagnosticsReporting.qll b/java/ql/src/Diagnostics/DiagnosticsReporting.qll new file mode 100644 index 00000000000..72403ea120f --- /dev/null +++ b/java/ql/src/Diagnostics/DiagnosticsReporting.qll @@ -0,0 +1,84 @@ +/** + * Provides classes and predicates for reporting extractor diagnostics to end users. + */ + +import java + +/** Gets the SARIF severity level that indicates an error. */ +private int getErrorSeverity() { result = 2 } + +/** Gets the SARIF severity level that indicates a warning. */ +private int getWarnSeverity() { result = 1 } + +private predicate knownWarnings(@diagnostic d, string msg, int sev) { + exists(string filename | + diagnostics(d, 2, _, "Skipping Lombok-ed source file: " + filename, _, _) and + msg = "Use of Lombok detected. Skipping file: " + filename and + sev = getWarnSeverity() + ) +} + +private predicate knownErrors(@diagnostic d, string msg, int sev) { + exists(string numErr, Location l | + diagnostics(d, 6, _, numErr, _, l) and + msg = "Frontend errors in file: " + l.getFile().getAbsolutePath() + " (" + numErr + ")" and + sev = getErrorSeverity() + ) + or + exists(string filename, Location l | + diagnostics(d, 7, _, "Exception compiling file " + filename, _, l) and + msg = "Extraction incomplete in file: " + filename and + sev = getErrorSeverity() + ) + or + exists(string errMsg, Location l | + diagnostics(d, 8, _, errMsg, _, l) and + msg = "Severe error: " + errMsg and + sev = getErrorSeverity() + ) +} + +private predicate unknownErrors(@diagnostic d, string msg, int sev) { + not knownErrors(d, _, _) and + exists(Location l, File f, int diagSev | + diagnostics(d, diagSev, _, _, _, l) and l.getFile() = f and diagSev > 3 + | + exists(f.getRelativePath()) and + msg = "Unknown errors in file: " + f.getAbsolutePath() + " (" + diagSev + ")" and + sev = getErrorSeverity() + ) +} + +/** + * Holds if an extraction error or warning occurred that should be reported to end users, + * with the message `msg` and SARIF severity `sev`. + */ +predicate reportableDiagnostics(@diagnostic d, string msg, int sev) { + reportableWarnings(d, msg, sev) or reportableErrors(d, msg, sev) +} + +/** + * Holds if an extraction error occurred that should be reported to end users, + * with the message `msg` and SARIF severity `sev`. + */ +predicate reportableErrors(@diagnostic d, string msg, int sev) { + knownErrors(d, msg, sev) or unknownErrors(d, msg, sev) +} + +/** + * Holds if an extraction warning occurred that should be reported to end users, + * with the message `msg` and SARIF severity `sev`. + */ +predicate reportableWarnings(@diagnostic d, string msg, int sev) { knownWarnings(d, msg, sev) } + +/** + * Holds if compilation unit `f` is a source file that has + * no relevant extraction diagnostics associated with it. + */ +predicate successfullyExtracted(CompilationUnit f) { + not exists(@diagnostic d, Location l | + reportableDiagnostics(d, _, _) and diagnostics(d, _, _, _, _, l) and l.getFile() = f + ) and + exists(f.getRelativePath()) and + f.fromSource() +} diff --git a/java/ql/src/Diagnostics/ExtractionErrors.ql b/java/ql/src/Diagnostics/ExtractionErrors.ql new file mode 100644 index 00000000000..d9a3da2767a --- /dev/null +++ b/java/ql/src/Diagnostics/ExtractionErrors.ql @@ -0,0 +1,13 @@ +/** + * @name Extraction errors + * @description A list of extraction errors for files in the source code directory. + * @kind diagnostic + * @id java/diagnostics/extraction-errors + */ + +import java +import DiagnosticsReporting + +from string msg, int sev +where reportableErrors(_, msg, sev) +select msg, sev diff --git a/java/ql/src/Diagnostics/ExtractionWarnings.ql b/java/ql/src/Diagnostics/ExtractionWarnings.ql new file mode 100644 index 00000000000..52d173d469e --- /dev/null +++ b/java/ql/src/Diagnostics/ExtractionWarnings.ql @@ -0,0 +1,13 @@ +/** + * @name Extraction warnings + * @description A list of extraction warnings for files in the source code directory. + * @kind diagnostic + * @id java/diagnostics/extraction-warnings + */ + +import java +import DiagnosticsReporting + +from string msg, int sev +where reportableWarnings(_, msg, sev) +select msg, sev diff --git a/java/ql/src/Diagnostics/SuccessfullyExtractedFiles.ql b/java/ql/src/Diagnostics/SuccessfullyExtractedFiles.ql new file mode 100644 index 00000000000..161c9f17e42 --- /dev/null +++ b/java/ql/src/Diagnostics/SuccessfullyExtractedFiles.ql @@ -0,0 +1,14 @@ +/** + * @name Successfully extracted files + * @description A list of all files in the source code directory that + * were extracted without encountering an error in the file. + * @kind diagnostic + * @id java/diagnostics/successfully-extracted-files + */ + +import java +import DiagnosticsReporting + +from CompilationUnit f +where successfullyExtracted(f) +select f, "" diff --git a/java/ql/src/Frameworks/JavaEE/EJB/EjbContainerInterference.ql b/java/ql/src/Frameworks/JavaEE/EJB/EjbContainerInterference.ql index 79dd15510e8..6b08a14c244 100644 --- a/java/ql/src/Frameworks/JavaEE/EJB/EjbContainerInterference.ql +++ b/java/ql/src/Frameworks/JavaEE/EJB/EjbContainerInterference.ql @@ -7,6 +7,7 @@ * Such operations could interfere with the EJB container's operation. * @kind problem * @problem.severity error + * @security-severity 4.9 * @precision low * @id java/ejb/container-interference * @tags reliability diff --git a/java/ql/src/Frameworks/JavaEE/EJB/EjbFileIO.ql b/java/ql/src/Frameworks/JavaEE/EJB/EjbFileIO.ql index 51f0105cb07..62b2d5f48ec 100644 --- a/java/ql/src/Frameworks/JavaEE/EJB/EjbFileIO.ql +++ b/java/ql/src/Frameworks/JavaEE/EJB/EjbFileIO.ql @@ -5,6 +5,7 @@ * for enterprise components. * @kind problem * @problem.severity error + * @security-severity 4.9 * @precision low * @id java/ejb/file-io * @tags reliability diff --git a/java/ql/src/Frameworks/JavaEE/EJB/EjbNative.ql b/java/ql/src/Frameworks/JavaEE/EJB/EjbNative.ql index 16736a1669e..787ae9a72c5 100644 --- a/java/ql/src/Frameworks/JavaEE/EJB/EjbNative.ql +++ b/java/ql/src/Frameworks/JavaEE/EJB/EjbNative.ql @@ -4,6 +4,7 @@ * Such use could compromise security and system stability. * @kind problem * @problem.severity error + * @security-severity 4.9 * @precision low * @id java/ejb/native-code * @tags reliability diff --git a/java/ql/src/Frameworks/JavaEE/EJB/EjbReflection.ql b/java/ql/src/Frameworks/JavaEE/EJB/EjbReflection.ql index fa28edc7393..4e6eea2cbf1 100644 --- a/java/ql/src/Frameworks/JavaEE/EJB/EjbReflection.ql +++ b/java/ql/src/Frameworks/JavaEE/EJB/EjbReflection.ql @@ -4,6 +4,7 @@ * as this could compromise security. * @kind problem * @problem.severity error + * @security-severity 4.9 * @precision low * @id java/ejb/reflection * @tags external/cwe/cwe-573 diff --git a/java/ql/src/Frameworks/JavaEE/EJB/EjbSecurityConfiguration.ql b/java/ql/src/Frameworks/JavaEE/EJB/EjbSecurityConfiguration.ql index eb08dcb8060..4efde8d82bf 100644 --- a/java/ql/src/Frameworks/JavaEE/EJB/EjbSecurityConfiguration.ql +++ b/java/ql/src/Frameworks/JavaEE/EJB/EjbSecurityConfiguration.ql @@ -5,6 +5,7 @@ * This functionality is reserved for the EJB container for security reasons. * @kind problem * @problem.severity error + * @security-severity 4.9 * @precision low * @id java/ejb/security-configuration-access * @tags external/cwe/cwe-573 diff --git a/java/ql/src/Frameworks/JavaEE/EJB/EjbSerialization.ql b/java/ql/src/Frameworks/JavaEE/EJB/EjbSerialization.ql index d6e5c1fc9b1..02c493c7a70 100644 --- a/java/ql/src/Frameworks/JavaEE/EJB/EjbSerialization.ql +++ b/java/ql/src/Frameworks/JavaEE/EJB/EjbSerialization.ql @@ -4,6 +4,7 @@ * the Java serialization protocol, since their use could compromise security. * @kind problem * @problem.severity error + * @security-severity 4.9 * @precision low * @id java/ejb/substitution-in-serialization * @tags external/cwe/cwe-573 diff --git a/java/ql/src/Frameworks/JavaEE/EJB/EjbSetSocketOrUrlFactory.ql b/java/ql/src/Frameworks/JavaEE/EJB/EjbSetSocketOrUrlFactory.ql index 4a1524ee1b3..8011b3c1d22 100644 --- a/java/ql/src/Frameworks/JavaEE/EJB/EjbSetSocketOrUrlFactory.ql +++ b/java/ql/src/Frameworks/JavaEE/EJB/EjbSetSocketOrUrlFactory.ql @@ -5,6 +5,7 @@ * compromise security or interfere with the EJB container's operation. * @kind problem * @problem.severity error + * @security-severity 4.9 * @precision low * @id java/ejb/socket-or-stream-handler-factory * @tags reliability diff --git a/java/ql/src/Likely Bugs/Arithmetic/InformationLoss.ql b/java/ql/src/Likely Bugs/Arithmetic/InformationLoss.ql index aec8e9bc94c..52bb9f04289 100644 --- a/java/ql/src/Likely Bugs/Arithmetic/InformationLoss.ql +++ b/java/ql/src/Likely Bugs/Arithmetic/InformationLoss.ql @@ -5,6 +5,7 @@ * numeric errors such as overflows. * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision very-high * @id java/implicit-cast-in-compound-assignment * @tags reliability diff --git a/java/ql/src/Likely Bugs/Arithmetic/RandomUsedOnce.ql b/java/ql/src/Likely Bugs/Arithmetic/RandomUsedOnce.ql index 7f87d6bd062..fb1a44b2222 100644 --- a/java/ql/src/Likely Bugs/Arithmetic/RandomUsedOnce.ql +++ b/java/ql/src/Likely Bugs/Arithmetic/RandomUsedOnce.ql @@ -4,6 +4,7 @@ * guarantee an evenly distributed sequence of random numbers. * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision medium * @id java/random-used-once * @tags reliability diff --git a/java/ql/src/Likely Bugs/Concurrency/StartInConstructor.qhelp b/java/ql/src/Likely Bugs/Concurrency/StartInConstructor.qhelp index 1d0d5b0a1fb..1401c92b0d6 100644 --- a/java/ql/src/Likely Bugs/Concurrency/StartInConstructor.qhelp +++ b/java/ql/src/Likely Bugs/Concurrency/StartInConstructor.qhelp @@ -41,7 +41,7 @@ initialized. This results in the program outputting "hello my friend".

  • IBM developerWorks: - Don't start threads from within constructors. + Don't start threads from within constructors.
  • diff --git a/java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.ql b/java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.ql index 40fb18543d1..4a3fc548085 100644 --- a/java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.ql +++ b/java/ql/src/Likely Bugs/Concurrency/UnreleasedLock.ql @@ -4,6 +4,7 @@ * may cause a deadlock. * @kind problem * @problem.severity error + * @security-severity 6.9 * @precision medium * @id java/unreleased-lock * @tags reliability diff --git a/java/ql/src/Likely Bugs/Frameworks/Swing/ThreadSafety.qhelp b/java/ql/src/Likely Bugs/Frameworks/Swing/ThreadSafety.qhelp index af79f45d59c..060db52dd61 100644 --- a/java/ql/src/Likely Bugs/Frameworks/Swing/ThreadSafety.qhelp +++ b/java/ql/src/Likely Bugs/Frameworks/Swing/ThreadSafety.qhelp @@ -94,7 +94,7 @@ D. Flanagan, Java Foundation Classes in a Nutshell, p.28. O'Reilly, 199
  • Java Developer's Journal: -Building Thread-Safe GUIs with Swing. +Building Thread-Safe GUIs with Swing.
  • The Java Tutorials: diff --git a/java/ql/src/Likely Bugs/Likely Typos/StringBufferCharInit.qhelp b/java/ql/src/Likely Bugs/Likely Typos/StringBufferCharInit.qhelp index 48004a1f118..d76c09e872b 100644 --- a/java/ql/src/Likely Bugs/Likely Typos/StringBufferCharInit.qhelp +++ b/java/ql/src/Likely Bugs/Likely Typos/StringBufferCharInit.qhelp @@ -41,7 +41,7 @@ J. Bloch and N. Gafter, Java Puzzlers: Traps, Pitfalls, and Corner Cases
  • -NetBeans IDE: Java Hints +NetBeans IDE: Java Hints
  • PMD: Rule StringBufferInstantiationWithChar diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.qhelp b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.qhelp index 4d06bef040e..e5574bfc179 100644 --- a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.qhelp +++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.qhelp @@ -14,7 +14,7 @@ but not closed may cause a resource leak.

    Ensure that the resource is always closed to avoid a resource leak. Note that, because of exceptions, it is safest to close a resource in a finally block. (However, this is unnecessary for -subclasses of StringReader and ByteArrayInputStream.) +subclasses of CharArrayReader, StringReader and ByteArrayInputStream.)

    For Java 7 or later, the recommended way to close resources that implement java.lang.AutoCloseable @@ -51,7 +51,7 @@ In this case, the inner expression needs to be assigned to a local variable and

  • - IBM developerWorks: Java theory and practice: Good housekeeping practices. + IBM developerWorks: Java theory and practice: Good housekeeping practices.
  • The Java Tutorials: The try-with-resources Statement. diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql index 9d62237bd71..e93c59b0879 100644 --- a/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql +++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql @@ -17,16 +17,16 @@ import CloseType predicate readerType(RefType t) { exists(RefType sup | sup = t.getASupertype*() | - sup.hasName("Reader") or - sup.hasName("InputStream") or + sup.hasQualifiedName("java.io", ["Reader", "InputStream"]) or sup.hasQualifiedName("java.util.zip", "ZipFile") ) } predicate safeReaderType(RefType t) { exists(RefType sup | sup = t.getASupertype*() | - sup.hasName("StringReader") or - sup.hasName("ByteArrayInputStream") or + sup.hasQualifiedName("java.io", ["CharArrayReader", "StringReader", "ByteArrayInputStream"]) + or + // Note: It is unclear which specific class this is supposed to match sup.hasName("StringInputStream") ) } diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseSql.qhelp b/java/ql/src/Likely Bugs/Resource Leaks/CloseSql.qhelp index 8f89d16ef1d..eea0513dc8e 100644 --- a/java/ql/src/Likely Bugs/Resource Leaks/CloseSql.qhelp +++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseSql.qhelp @@ -40,7 +40,7 @@ by the code that created it or by a server shutdown procedure, as appropriate. - IBM developerWorks: Java theory and practice: Good housekeeping practices. + IBM developerWorks: Java theory and practice: Good housekeeping practices.
  • The Java Tutorials: The try-with-resources Statement. diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.qhelp b/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.qhelp index 36e834ecefd..84a50f914f7 100644 --- a/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.qhelp +++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.qhelp @@ -14,7 +14,7 @@ but not properly closed later may cause a resource leak.

    Ensure that the resource is always closed to avoid a resource leak. Note that, because of exceptions, it is safest to close a resource properly in a finally block. (However, this is unnecessary for -subclasses of StringWriter and ByteArrayOutputStream.)

    +subclasses of CharArrayWriter, StringWriter and ByteArrayOutputStream.)

    For Java 7 or later, the recommended way to close resources that implement java.lang.AutoCloseable is to declare them within a try-with-resources statement, so that they are closed implicitly.

    @@ -50,7 +50,7 @@ In this case, the inner expression needs to be assigned to a local variable and
  • - IBM developerWorks: Java theory and practice: Good housekeeping practices. + IBM developerWorks: Java theory and practice: Good housekeeping practices.
  • The Java Tutorials: The try-with-resources Statement. diff --git a/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql b/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql index 113f3bd3267..a0714fe3a2f 100644 --- a/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql +++ b/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql @@ -17,15 +17,13 @@ import CloseType predicate writerType(RefType t) { exists(RefType sup | sup = t.getASupertype*() | - sup.hasName("Writer") or - sup.hasName("OutputStream") + sup.hasQualifiedName("java.io", ["Writer", "OutputStream"]) ) } predicate safeWriterType(RefType t) { exists(RefType sup | sup = t.getASupertype*() | - sup.hasName("StringWriter") or - sup.hasName("ByteArrayOutputStream") + sup.hasQualifiedName("java.io", ["CharArrayWriter", "StringWriter", "ByteArrayOutputStream"]) ) } diff --git a/java/ql/src/Metrics/RefTypes/TEfferentCoupling.qhelp b/java/ql/src/Metrics/RefTypes/TEfferentCoupling.qhelp index 40e9e0ef5de..535faedf81e 100644 --- a/java/ql/src/Metrics/RefTypes/TEfferentCoupling.qhelp +++ b/java/ql/src/Metrics/RefTypes/TEfferentCoupling.qhelp @@ -51,7 +51,7 @@ so the general technique is quite widely applicable.
  • -IBM developerWorks: Evolutionary architecture and emergent design: Emergent design through metrics. +IBM developerWorks: Evolutionary architecture and emergent design: Emergent design through metrics.
  • R. Martin, Agile Software Development: Principles, Patterns and Practices. Pearson, 2011. diff --git a/java/ql/src/Metrics/RefTypes/TEfferentSourceCoupling.qhelp b/java/ql/src/Metrics/RefTypes/TEfferentSourceCoupling.qhelp index ceb74639c1e..965fb2795fb 100644 --- a/java/ql/src/Metrics/RefTypes/TEfferentSourceCoupling.qhelp +++ b/java/ql/src/Metrics/RefTypes/TEfferentSourceCoupling.qhelp @@ -81,7 +81,7 @@ so the general technique is quite widely applicable.
  • -A. Glover. Code quality for software architects. Published online, 2006. +A. Glover. Code quality for software architects. Published online, 2006.
  • R. Martin. Agile Software Development: Principles, Patterns and Practices. Pearson, 2011. diff --git a/java/ql/src/Metrics/Summaries/LinesOfCode.ql b/java/ql/src/Metrics/Summaries/LinesOfCode.ql new file mode 100644 index 00000000000..c622f8b08ba --- /dev/null +++ b/java/ql/src/Metrics/Summaries/LinesOfCode.ql @@ -0,0 +1,14 @@ +/** + * @id java/summary/lines-of-code + * @name Total lines of code in the database + * @description The total number of lines of code across all files. This is a useful metric of the size of a database. + * For all files that were seen during the build, this query counts the lines of code, excluding whitespace + * or comments. + * @kind metric + * @tags summary + * lines-of-code + */ + +import java + +select sum(CompilationUnit f | f.fromSource() | f.getNumberOfLinesOfCode()) diff --git a/java/ql/src/Performance/InnerClassCouldBeStatic.ql b/java/ql/src/Performance/InnerClassCouldBeStatic.ql index a18d801549d..5dba77761c6 100644 --- a/java/ql/src/Performance/InnerClassCouldBeStatic.ql +++ b/java/ql/src/Performance/InnerClassCouldBeStatic.ql @@ -100,8 +100,7 @@ predicate potentiallyStatic(InnerClass c) { m = a.getEnclosingCallable() and m.getDeclaringType() = c ) and - not c instanceof AnonymousClass and - not c instanceof LocalClass and + c instanceof MemberType and forall( InnerClass other // If nested and non-static, ... | diff --git a/java/ql/src/Security/CWE/CWE-020/UntrustedDataToExternalAPI.ql b/java/ql/src/Security/CWE/CWE-020/UntrustedDataToExternalAPI.ql index 35c6a69c022..bf2e3f06f26 100644 --- a/java/ql/src/Security/CWE/CWE-020/UntrustedDataToExternalAPI.ql +++ b/java/ql/src/Security/CWE/CWE-020/UntrustedDataToExternalAPI.ql @@ -5,6 +5,7 @@ * @kind path-problem * @precision low * @problem.severity error + * @security-severity 5.9 * @tags security external/cwe/cwe-20 */ diff --git a/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql b/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql index 01d89cc8e06..ac3ec8664a2 100644 --- a/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql +++ b/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql @@ -3,6 +3,7 @@ * @description Accessing paths influenced by users can allow an attacker to access unexpected resources. * @kind path-problem * @problem.severity error + * @security-severity 6.4 * @precision high * @id java/path-injection * @tags security diff --git a/java/ql/src/Security/CWE/CWE-022/TaintedPathLocal.ql b/java/ql/src/Security/CWE/CWE-022/TaintedPathLocal.ql index a64f88997e8..90b77661500 100644 --- a/java/ql/src/Security/CWE/CWE-022/TaintedPathLocal.ql +++ b/java/ql/src/Security/CWE/CWE-022/TaintedPathLocal.ql @@ -3,6 +3,7 @@ * @description Accessing paths influenced by users can allow an attacker to access unexpected resources. * @kind path-problem * @problem.severity recommendation + * @security-severity 6.4 * @precision medium * @id java/path-injection-local * @tags security diff --git a/java/ql/src/Security/CWE/CWE-022/ZipSlip.ql b/java/ql/src/Security/CWE/CWE-022/ZipSlip.ql index 7d74f8b79ac..1a70b273d84 100644 --- a/java/ql/src/Security/CWE/CWE-022/ZipSlip.ql +++ b/java/ql/src/Security/CWE/CWE-022/ZipSlip.ql @@ -6,6 +6,7 @@ * @kind path-problem * @id java/zipslip * @problem.severity error + * @security-severity 6.4 * @precision high * @tags security * external/cwe/cwe-022 @@ -17,6 +18,7 @@ import semmle.code.java.dataflow.SSA import semmle.code.java.dataflow.TaintTracking import DataFlow import PathGraph +private import semmle.code.java.dataflow.ExternalFlow /** * A method that returns the name of an archive entry. @@ -33,34 +35,6 @@ class ArchiveEntryNameMethod extends Method { } } -/** - * An expression that will be treated as the destination of a write. - */ -class WrittenFileName extends Expr { - WrittenFileName() { - // Constructors that write to their first argument. - exists(ConstructorCall ctr | this = ctr.getArgument(0) | - exists(Class c | ctr.getConstructor() = c.getAConstructor() | - c.hasQualifiedName("java.io", "FileOutputStream") or - c.hasQualifiedName("java.io", "RandomAccessFile") or - c.hasQualifiedName("java.io", "FileWriter") - ) - ) - or - // Methods that write to their n'th argument - exists(MethodAccess call, int n | this = call.getArgument(n) | - call.getMethod().getDeclaringType().hasQualifiedName("java.nio.file", "Files") and - ( - call.getMethod().getName().regexpMatch("new.*Reader|newOutputStream|create.*") and n = 0 - or - call.getMethod().hasName("copy") and n = 1 - or - call.getMethod().hasName("move") and n = 1 - ) - ) - } -} - /** * Holds if `n1` to `n2` is a dataflow step that converts between `String`, * `File`, and `Path`. @@ -151,7 +125,7 @@ class ZipSlipConfiguration extends TaintTracking::Configuration { source.asExpr().(MethodAccess).getMethod() instanceof ArchiveEntryNameMethod } - override predicate isSink(Node sink) { sink.asExpr() instanceof WrittenFileName } + override predicate isSink(Node sink) { sink instanceof FileCreationSink } override predicate isAdditionalTaintStep(Node n1, Node n2) { filePathStep(n1, n2) or fileTaintStep(n1, n2) @@ -173,6 +147,13 @@ class ZipSlipConfiguration extends TaintTracking::Configuration { } } +/** + * A sink that represents a file creation, such as a file write, copy or move operation. + */ +private class FileCreationSink extends DataFlow::Node { + FileCreationSink() { sinkNode(this, "create-file") } +} + from PathNode source, PathNode sink where any(ZipSlipConfiguration c).hasFlowPath(source, sink) select source.getNode(), source, sink, diff --git a/java/ql/src/Security/CWE/CWE-078/ExecRelative.ql b/java/ql/src/Security/CWE/CWE-078/ExecRelative.ql index 80cd9c0dee3..f7d844f225a 100644 --- a/java/ql/src/Security/CWE/CWE-078/ExecRelative.ql +++ b/java/ql/src/Security/CWE/CWE-078/ExecRelative.ql @@ -4,6 +4,7 @@ * malicious changes in the PATH environment variable. * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision medium * @id java/relative-path-command * @tags security diff --git a/java/ql/src/Security/CWE/CWE-078/ExecTainted.ql b/java/ql/src/Security/CWE/CWE-078/ExecTainted.ql index 7b191e76241..fc409bcfd5a 100644 --- a/java/ql/src/Security/CWE/CWE-078/ExecTainted.ql +++ b/java/ql/src/Security/CWE/CWE-078/ExecTainted.ql @@ -4,6 +4,7 @@ * changes in the strings. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id java/command-line-injection * @tags security diff --git a/java/ql/src/Security/CWE/CWE-078/ExecTaintedLocal.ql b/java/ql/src/Security/CWE/CWE-078/ExecTaintedLocal.ql index f7c49dbd4f6..c98a45a06d8 100644 --- a/java/ql/src/Security/CWE/CWE-078/ExecTaintedLocal.ql +++ b/java/ql/src/Security/CWE/CWE-078/ExecTaintedLocal.ql @@ -4,6 +4,7 @@ * changes in the strings. * @kind path-problem * @problem.severity recommendation + * @security-severity 5.9 * @precision medium * @id java/command-line-injection-local * @tags security diff --git a/java/ql/src/Security/CWE/CWE-078/ExecUnescaped.ql b/java/ql/src/Security/CWE/CWE-078/ExecUnescaped.ql index a0fa793872f..a3b88b40ae7 100644 --- a/java/ql/src/Security/CWE/CWE-078/ExecUnescaped.ql +++ b/java/ql/src/Security/CWE/CWE-078/ExecUnescaped.ql @@ -4,6 +4,7 @@ * insertion of special characters in the strings. * @kind problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id java/concatenated-command-line * @tags security diff --git a/java/ql/src/Security/CWE/CWE-079/XSS.ql b/java/ql/src/Security/CWE/CWE-079/XSS.ql index ae7cec3277d..d864d24a95e 100644 --- a/java/ql/src/Security/CWE/CWE-079/XSS.ql +++ b/java/ql/src/Security/CWE/CWE-079/XSS.ql @@ -4,6 +4,7 @@ * allows for a cross-site scripting vulnerability. * @kind path-problem * @problem.severity error + * @security-severity 2.9 * @precision high * @id java/xss * @tags security diff --git a/java/ql/src/Security/CWE/CWE-079/XSSLocal.ql b/java/ql/src/Security/CWE/CWE-079/XSSLocal.ql index a11a3ade0fd..44ab185b3b5 100644 --- a/java/ql/src/Security/CWE/CWE-079/XSSLocal.ql +++ b/java/ql/src/Security/CWE/CWE-079/XSSLocal.ql @@ -4,6 +4,7 @@ * allows for a cross-site scripting vulnerability. * @kind path-problem * @problem.severity recommendation + * @security-severity 2.9 * @precision medium * @id java/xss-local * @tags security diff --git a/java/ql/src/Security/CWE/CWE-089/SqlTainted.ql b/java/ql/src/Security/CWE/CWE-089/SqlTainted.ql index 86e98754c14..d002bb96ce1 100644 --- a/java/ql/src/Security/CWE/CWE-089/SqlTainted.ql +++ b/java/ql/src/Security/CWE/CWE-089/SqlTainted.ql @@ -4,6 +4,7 @@ * malicious code by the user. * @kind path-problem * @problem.severity error + * @security-severity 6.4 * @precision high * @id java/sql-injection * @tags security diff --git a/java/ql/src/Security/CWE/CWE-089/SqlTaintedLocal.ql b/java/ql/src/Security/CWE/CWE-089/SqlTaintedLocal.ql index 56c052e7b1b..ada846dcf47 100644 --- a/java/ql/src/Security/CWE/CWE-089/SqlTaintedLocal.ql +++ b/java/ql/src/Security/CWE/CWE-089/SqlTaintedLocal.ql @@ -4,6 +4,7 @@ * malicious code by the user. * @kind path-problem * @problem.severity recommendation + * @security-severity 6.4 * @precision medium * @id java/sql-injection-local * @tags security diff --git a/java/ql/src/Security/CWE/CWE-089/SqlUnescaped.ql b/java/ql/src/Security/CWE/CWE-089/SqlUnescaped.ql index c6fd810b74c..9ddd5def883 100644 --- a/java/ql/src/Security/CWE/CWE-089/SqlUnescaped.ql +++ b/java/ql/src/Security/CWE/CWE-089/SqlUnescaped.ql @@ -4,6 +4,7 @@ * characters is vulnerable to insertion of malicious code. * @kind problem * @problem.severity error + * @security-severity 6.4 * @precision high * @id java/concatenated-sql-query * @tags security diff --git a/java/ql/src/Security/CWE/CWE-090/LdapInjection.ql b/java/ql/src/Security/CWE/CWE-090/LdapInjection.ql index 6b5b37f1093..5a7ab632a55 100644 --- a/java/ql/src/Security/CWE/CWE-090/LdapInjection.ql +++ b/java/ql/src/Security/CWE/CWE-090/LdapInjection.ql @@ -4,6 +4,7 @@ * malicious LDAP code by the user. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id java/ldap-injection * @tags security diff --git a/java/ql/src/Security/CWE/CWE-094/InsecureBeanValidation.ql b/java/ql/src/Security/CWE/CWE-094/InsecureBeanValidation.ql index 6b8ab085132..f865d7d16b1 100644 --- a/java/ql/src/Security/CWE/CWE-094/InsecureBeanValidation.ql +++ b/java/ql/src/Security/CWE/CWE-094/InsecureBeanValidation.ql @@ -3,6 +3,7 @@ * @description User-controlled data may be evaluated as a Java EL expression, leading to arbitrary code execution. * @kind path-problem * @problem.severity error + * @security-severity 10.0 * @precision high * @id java/insecure-bean-validation * @tags security @@ -13,6 +14,7 @@ import java import semmle.code.java.dataflow.TaintTracking import semmle.code.java.dataflow.FlowSources import DataFlow::PathGraph +private import semmle.code.java.dataflow.ExternalFlow /** * A message interpolator Type that perform Expression Language (EL) evaluations @@ -50,19 +52,6 @@ class SetMessageInterpolatorCall extends MethodAccess { predicate isSafe() { not this.getAnArgument().getType() instanceof ELMessageInterpolatorType } } -/** - * A method named `buildConstraintViolationWithTemplate` declared on a subtype - * of `javax.validation.ConstraintValidatorContext`. - */ -class BuildConstraintViolationWithTemplateMethod extends Method { - BuildConstraintViolationWithTemplateMethod() { - this.getDeclaringType() - .getASupertype*() - .hasQualifiedName("javax.validation", "ConstraintValidatorContext") and - this.hasName("buildConstraintViolationWithTemplate") - } -} - /** * Taint tracking BeanValidationConfiguration describing the flow of data from user input * to the argument of a method that builds constraint error messages. @@ -72,12 +61,15 @@ class BeanValidationConfig extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - override predicate isSink(DataFlow::Node sink) { - exists(MethodAccess ma | - ma.getMethod() instanceof BuildConstraintViolationWithTemplateMethod and - sink.asExpr() = ma.getArgument(0) - ) - } + override predicate isSink(DataFlow::Node sink) { sink instanceof BeanValidationSink } +} + +/** + * A bean validation sink, such as method `buildConstraintViolationWithTemplate` + * declared on a subtype of `javax.validation.ConstraintValidatorContext`. + */ +private class BeanValidationSink extends DataFlow::Node { + BeanValidationSink() { sinkNode(this, "bean-validation") } } from BeanValidationConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sink diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjection.qhelp b/java/ql/src/Security/CWE/CWE-094/JexlInjection.qhelp similarity index 100% rename from java/ql/src/experimental/Security/CWE/CWE-094/JexlInjection.qhelp rename to java/ql/src/Security/CWE/CWE-094/JexlInjection.qhelp diff --git a/java/ql/src/Security/CWE/CWE-094/JexlInjection.ql b/java/ql/src/Security/CWE/CWE-094/JexlInjection.ql new file mode 100644 index 00000000000..cf555aa3442 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-094/JexlInjection.ql @@ -0,0 +1,38 @@ +/** + * @name Expression language injection (JEXL) + * @description Evaluation of a user-controlled JEXL expression + * may lead to arbitrary code execution. + * @kind path-problem + * @problem.severity error + * @security-severity 10.0 + * @precision high + * @id java/jexl-expression-injection + * @tags security + * external/cwe/cwe-094 + */ + +import java +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.security.JexlInjection +import DataFlow::PathGraph + +/** + * A taint-tracking configuration for unsafe user input + * that is used to construct and evaluate a JEXL expression. + * It supports both JEXL 2 and 3. + */ +class JexlInjectionConfig extends TaintTracking::Configuration { + JexlInjectionConfig() { this = "JexlInjectionConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof JexlEvaluationSink } + + override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + any(JexlInjectionAdditionalTaintStep c).step(node1, node2) + } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, JexlInjectionConfig conf +where conf.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "JEXL injection from $@.", source.getNode(), "this user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/SaferJexlExpressionEvaluationWithSandbox.java b/java/ql/src/Security/CWE/CWE-094/SaferJexlExpressionEvaluationWithSandbox.java similarity index 100% rename from java/ql/src/experimental/Security/CWE/CWE-094/SaferJexlExpressionEvaluationWithSandbox.java rename to java/ql/src/Security/CWE/CWE-094/SaferJexlExpressionEvaluationWithSandbox.java diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/SaferJexlExpressionEvaluationWithUberspectSandbox.java b/java/ql/src/Security/CWE/CWE-094/SaferJexlExpressionEvaluationWithUberspectSandbox.java similarity index 100% rename from java/ql/src/experimental/Security/CWE/CWE-094/SaferJexlExpressionEvaluationWithUberspectSandbox.java rename to java/ql/src/Security/CWE/CWE-094/SaferJexlExpressionEvaluationWithUberspectSandbox.java diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/UnsafeJexlExpressionEvaluation.java b/java/ql/src/Security/CWE/CWE-094/UnsafeJexlExpressionEvaluation.java similarity index 100% rename from java/ql/src/experimental/Security/CWE/CWE-094/UnsafeJexlExpressionEvaluation.java rename to java/ql/src/Security/CWE/CWE-094/UnsafeJexlExpressionEvaluation.java diff --git a/java/ql/src/Security/CWE/CWE-1104/MavenPomDependsOnBintray.ql b/java/ql/src/Security/CWE/CWE-1104/MavenPomDependsOnBintray.ql index 936da80a9d9..84d9ba734e2 100644 --- a/java/ql/src/Security/CWE/CWE-1104/MavenPomDependsOnBintray.ql +++ b/java/ql/src/Security/CWE/CWE-1104/MavenPomDependsOnBintray.ql @@ -3,6 +3,7 @@ * @description Using a deprecated artifact repository may eventually give attackers access for a supply chain attack. * @kind problem * @problem.severity error + * @security-severity 6.5 * @precision very-high * @id java/maven/dependency-upon-bintray * @tags security diff --git a/java/ql/src/Security/CWE/CWE-113/NettyResponseSplitting.ql b/java/ql/src/Security/CWE/CWE-113/NettyResponseSplitting.ql index 0193093e72c..76fa154ae7b 100644 --- a/java/ql/src/Security/CWE/CWE-113/NettyResponseSplitting.ql +++ b/java/ql/src/Security/CWE/CWE-113/NettyResponseSplitting.ql @@ -5,6 +5,7 @@ * an HTTP header. * @kind problem * @problem.severity error + * @security-severity 3.6 * @precision high * @id java/netty-http-response-splitting * @tags security diff --git a/java/ql/src/Security/CWE/CWE-113/ResponseSplitting.ql b/java/ql/src/Security/CWE/CWE-113/ResponseSplitting.ql index add36e91963..92468d61936 100644 --- a/java/ql/src/Security/CWE/CWE-113/ResponseSplitting.ql +++ b/java/ql/src/Security/CWE/CWE-113/ResponseSplitting.ql @@ -4,6 +4,7 @@ * makes code vulnerable to attack by header splitting. * @kind path-problem * @problem.severity error + * @security-severity 3.6 * @precision high * @id java/http-response-splitting * @tags security diff --git a/java/ql/src/Security/CWE/CWE-113/ResponseSplittingLocal.ql b/java/ql/src/Security/CWE/CWE-113/ResponseSplittingLocal.ql index 7a748276aba..ff9a379d1f7 100644 --- a/java/ql/src/Security/CWE/CWE-113/ResponseSplittingLocal.ql +++ b/java/ql/src/Security/CWE/CWE-113/ResponseSplittingLocal.ql @@ -4,6 +4,7 @@ * makes code vulnerable to attack by header splitting. * @kind path-problem * @problem.severity recommendation + * @security-severity 3.6 * @precision medium * @id java/http-response-splitting-local * @tags security diff --git a/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayConstruction.ql b/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayConstruction.ql index b3d9b9f1884..ca5d05db10c 100644 --- a/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayConstruction.ql +++ b/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayConstruction.ql @@ -3,6 +3,7 @@ * @description Using unvalidated external input as the argument to a construction of an array can lead to index out of bound exceptions. * @kind path-problem * @problem.severity warning + * @security-severity 5.9 * @precision medium * @id java/improper-validation-of-array-construction * @tags security diff --git a/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayConstructionCodeSpecified.ql b/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayConstructionCodeSpecified.ql index 16519955c6d..f3471027561 100644 --- a/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayConstructionCodeSpecified.ql +++ b/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayConstructionCodeSpecified.ql @@ -4,6 +4,7 @@ * a construction of an array can lead to index out of bound exceptions. * @kind path-problem * @problem.severity recommendation + * @security-severity 5.9 * @precision medium * @id java/improper-validation-of-array-construction-code-specified * @tags security diff --git a/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayConstructionLocal.ql b/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayConstructionLocal.ql index 6938946ce0c..94e06109da4 100644 --- a/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayConstructionLocal.ql +++ b/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayConstructionLocal.ql @@ -4,6 +4,7 @@ * a construction of an array can lead to index out of bound exceptions. * @kind path-problem * @problem.severity recommendation + * @security-severity 5.9 * @precision medium * @id java/improper-validation-of-array-construction-local * @tags security diff --git a/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayIndex.ql b/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayIndex.ql index 9f0d9fa92a2..5fe23b564d6 100644 --- a/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayIndex.ql +++ b/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayIndex.ql @@ -3,6 +3,7 @@ * @description Using external input as an index to an array, without proper validation, can lead to index out of bound exceptions. * @kind path-problem * @problem.severity warning + * @security-severity 5.9 * @precision medium * @id java/improper-validation-of-array-index * @tags security diff --git a/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayIndexCodeSpecified.ql b/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayIndexCodeSpecified.ql index 9d0098cab63..99c819533cb 100644 --- a/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayIndexCodeSpecified.ql +++ b/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayIndexCodeSpecified.ql @@ -4,6 +4,7 @@ * proper validation, can lead to index out of bound exceptions. * @kind path-problem * @problem.severity recommendation + * @security-severity 5.9 * @precision medium * @id java/improper-validation-of-array-index-code-specified * @tags security diff --git a/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayIndexLocal.ql b/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayIndexLocal.ql index 37e68292f66..c246bcff158 100644 --- a/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayIndexLocal.ql +++ b/java/ql/src/Security/CWE/CWE-129/ImproperValidationOfArrayIndexLocal.ql @@ -4,6 +4,7 @@ * proper validation, can lead to index out of bound exceptions. * @kind path-problem * @problem.severity recommendation + * @security-severity 5.9 * @precision medium * @id java/improper-validation-of-array-index-local * @tags security diff --git a/java/ql/src/Security/CWE/CWE-134/ExternallyControlledFormatString.ql b/java/ql/src/Security/CWE/CWE-134/ExternallyControlledFormatString.ql index 7a9e3a2baab..0208955e10f 100644 --- a/java/ql/src/Security/CWE/CWE-134/ExternallyControlledFormatString.ql +++ b/java/ql/src/Security/CWE/CWE-134/ExternallyControlledFormatString.ql @@ -3,6 +3,7 @@ * @description Using external input in format strings can lead to exceptions or information leaks. * @kind path-problem * @problem.severity error + * @security-severity 6.9 * @precision high * @id java/tainted-format-string * @tags security diff --git a/java/ql/src/Security/CWE/CWE-134/ExternallyControlledFormatStringLocal.ql b/java/ql/src/Security/CWE/CWE-134/ExternallyControlledFormatStringLocal.ql index 72f2c4e4bd7..5dd2f629393 100644 --- a/java/ql/src/Security/CWE/CWE-134/ExternallyControlledFormatStringLocal.ql +++ b/java/ql/src/Security/CWE/CWE-134/ExternallyControlledFormatStringLocal.ql @@ -3,6 +3,7 @@ * @description Using external input in format strings can lead to exceptions or information leaks. * @kind path-problem * @problem.severity recommendation + * @security-severity 6.9 * @precision medium * @id java/tainted-format-string-local * @tags security diff --git a/java/ql/src/Security/CWE/CWE-190/ArithmeticTainted.ql b/java/ql/src/Security/CWE/CWE-190/ArithmeticTainted.ql index 958698d46eb..261c6891039 100644 --- a/java/ql/src/Security/CWE/CWE-190/ArithmeticTainted.ql +++ b/java/ql/src/Security/CWE/CWE-190/ArithmeticTainted.ql @@ -4,6 +4,7 @@ * overflows. * @kind path-problem * @problem.severity warning + * @security-severity 5.9 * @precision medium * @id java/tainted-arithmetic * @tags security diff --git a/java/ql/src/Security/CWE/CWE-190/ArithmeticTaintedLocal.ql b/java/ql/src/Security/CWE/CWE-190/ArithmeticTaintedLocal.ql index 3b6da268508..54e3fa1b10a 100644 --- a/java/ql/src/Security/CWE/CWE-190/ArithmeticTaintedLocal.ql +++ b/java/ql/src/Security/CWE/CWE-190/ArithmeticTaintedLocal.ql @@ -4,6 +4,7 @@ * overflows. * @kind path-problem * @problem.severity recommendation + * @security-severity 5.9 * @precision medium * @id java/tainted-arithmetic-local * @tags security diff --git a/java/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql b/java/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql index c0814e42ef7..ad67e3b55bc 100644 --- a/java/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql +++ b/java/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql @@ -4,6 +4,7 @@ * overflows. * @kind path-problem * @problem.severity warning + * @security-severity 5.9 * @precision medium * @id java/uncontrolled-arithmetic * @tags security diff --git a/java/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.ql b/java/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.ql index 0264b9d9b27..0ffcbec38e6 100644 --- a/java/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.ql +++ b/java/ql/src/Security/CWE/CWE-190/ArithmeticWithExtremeValues.ql @@ -4,6 +4,7 @@ * is then used in an arithmetic expression, this may result in an overflow. * @kind path-problem * @problem.severity recommendation + * @security-severity 5.9 * @precision medium * @id java/extreme-value-arithmetic * @tags security diff --git a/java/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql b/java/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql index 03138948178..c6e72a94b65 100644 --- a/java/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql +++ b/java/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql @@ -4,6 +4,7 @@ * to behave unexpectedly. * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision medium * @id java/comparison-with-wider-type * @tags reliability diff --git a/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql b/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql index c9c1e2917c0..bf96ee8d1bb 100644 --- a/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql +++ b/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql @@ -5,6 +5,7 @@ * that are useful to an attacker for developing a subsequent exploit. * @kind problem * @problem.severity error + * @security-severity 3.6 * @precision high * @id java/stack-trace-exposure * @tags security @@ -15,7 +16,7 @@ import java import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.TaintTracking -import semmle.code.java.security.XSS +import semmle.code.java.security.InformationLeak /** * One of the `printStackTrace()` overloads on `Throwable`. @@ -79,19 +80,18 @@ predicate stackTraceExpr(Expr exception, MethodAccess stackTraceString) { printStackCall.getAnArgument() = printWriter and printStackCall.getQualifier() = exception and stackTraceString.getQualifier() = stringWriterVar.getAnAccess() and - stackTraceString.getMethod().getName() = "toString" and - stackTraceString.getMethod().getNumberOfParameters() = 0 + stackTraceString.getMethod() instanceof ToStringMethod ) } -class StackTraceStringToXssSinkFlowConfig extends TaintTracking::Configuration { - StackTraceStringToXssSinkFlowConfig() { - this = "StackTraceExposure::StackTraceStringToXssSinkFlowConfig" +class StackTraceStringToHttpResponseSinkFlowConfig extends TaintTracking::Configuration { + StackTraceStringToHttpResponseSinkFlowConfig() { + this = "StackTraceExposure::StackTraceStringToHttpResponseSinkFlowConfig" } override predicate isSource(DataFlow::Node src) { stackTraceExpr(_, src.asExpr()) } - override predicate isSink(DataFlow::Node sink) { sink instanceof XssSink } + override predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink } } /** @@ -106,8 +106,8 @@ predicate printsStackExternally(MethodAccess call, Expr stackTrace) { /** * A stringified stack trace flows to an external sink. */ -predicate stringifiedStackFlowsExternally(XssSink externalExpr, Expr stackTrace) { - exists(MethodAccess stackTraceString, StackTraceStringToXssSinkFlowConfig conf | +predicate stringifiedStackFlowsExternally(DataFlow::Node externalExpr, Expr stackTrace) { + exists(MethodAccess stackTraceString, StackTraceStringToHttpResponseSinkFlowConfig conf | stackTraceExpr(stackTrace, stackTraceString) and conf.hasFlow(DataFlow::exprNode(stackTraceString), externalExpr) ) @@ -124,21 +124,21 @@ class GetMessageFlowSource extends MethodAccess { } } -class GetMessageFlowSourceToXssSinkFlowConfig extends TaintTracking::Configuration { - GetMessageFlowSourceToXssSinkFlowConfig() { - this = "StackTraceExposure::GetMessageFlowSourceToXssSinkFlowConfig" +class GetMessageFlowSourceToHttpResponseSinkFlowConfig extends TaintTracking::Configuration { + GetMessageFlowSourceToHttpResponseSinkFlowConfig() { + this = "StackTraceExposure::GetMessageFlowSourceToHttpResponseSinkFlowConfig" } override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof GetMessageFlowSource } - override predicate isSink(DataFlow::Node sink) { sink instanceof XssSink } + override predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink } } /** * A call to `getMessage()` that then flows to a servlet response. */ -predicate getMessageFlowsExternally(XssSink externalExpr, GetMessageFlowSource getMessage) { - any(GetMessageFlowSourceToXssSinkFlowConfig conf) +predicate getMessageFlowsExternally(DataFlow::Node externalExpr, GetMessageFlowSource getMessage) { + any(GetMessageFlowSourceToHttpResponseSinkFlowConfig conf) .hasFlow(DataFlow::exprNode(getMessage), externalExpr) } diff --git a/java/ql/src/Security/CWE/CWE-297/UnsafeHostnameVerification.ql b/java/ql/src/Security/CWE/CWE-297/UnsafeHostnameVerification.ql index 9c060565f28..d7a5f374953 100644 --- a/java/ql/src/Security/CWE/CWE-297/UnsafeHostnameVerification.ql +++ b/java/ql/src/Security/CWE/CWE-297/UnsafeHostnameVerification.ql @@ -3,6 +3,7 @@ * @description Marking a certificate as valid for a host without checking the certificate hostname allows an attacker to perform a machine-in-the-middle attack. * @kind path-problem * @problem.severity error + * @security-severity 4.9 * @precision high * @id java/unsafe-hostname-verification * @tags security @@ -15,6 +16,7 @@ import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.FlowSources import semmle.code.java.security.Encryption import DataFlow::PathGraph +private import semmle.code.java.dataflow.ExternalFlow /** * Holds if `m` always returns `true` ignoring any exceptional flow. @@ -49,14 +51,7 @@ class TrustAllHostnameVerifierConfiguration extends DataFlow::Configuration { source.asExpr().(ClassInstanceExpr).getConstructedType() instanceof TrustAllHostnameVerifier } - override predicate isSink(DataFlow::Node sink) { - exists(MethodAccess ma, Method m | - (m instanceof SetDefaultHostnameVerifierMethod or m instanceof SetHostnameVerifierMethod) and - ma.getMethod() = m - | - ma.getArgument(0) = sink.asExpr() - ) - } + override predicate isSink(DataFlow::Node sink) { sink instanceof HostnameVerifierSink } override predicate isBarrier(DataFlow::Node barrier) { // ignore nodes that are in functions that intentionally disable hostname verification @@ -84,6 +79,13 @@ class TrustAllHostnameVerifierConfiguration extends DataFlow::Configuration { } } +/** + * A sink that sets the `HostnameVerifier` on `HttpsURLConnection`. + */ +private class HostnameVerifierSink extends DataFlow::Node { + HostnameVerifierSink() { sinkNode(this, "set-hostname-verifier") } +} + bindingset[result] private string getAFlagName() { result diff --git a/java/ql/src/Security/CWE/CWE-312/CleartextStorageClass.ql b/java/ql/src/Security/CWE/CWE-312/CleartextStorageClass.ql index 41919152c6a..00527d4ab60 100644 --- a/java/ql/src/Security/CWE/CWE-312/CleartextStorageClass.ql +++ b/java/ql/src/Security/CWE/CWE-312/CleartextStorageClass.ql @@ -3,6 +3,7 @@ * @description Storing sensitive information in cleartext can expose it to an attacker. * @kind problem * @problem.severity recommendation + * @security-severity 5.9 * @precision medium * @id java/cleartext-storage-in-class * @tags security diff --git a/java/ql/src/Security/CWE/CWE-312/CleartextStorageCookie.ql b/java/ql/src/Security/CWE/CWE-312/CleartextStorageCookie.ql index 60d5a246979..7a3c55379de 100644 --- a/java/ql/src/Security/CWE/CWE-312/CleartextStorageCookie.ql +++ b/java/ql/src/Security/CWE/CWE-312/CleartextStorageCookie.ql @@ -3,6 +3,7 @@ * @description Storing sensitive information in cleartext can expose it to an attacker. * @kind problem * @problem.severity error + * @security-severity 2.9 * @precision high * @id java/cleartext-storage-in-cookie * @tags security diff --git a/java/ql/src/Security/CWE/CWE-312/CleartextStorageProperties.ql b/java/ql/src/Security/CWE/CWE-312/CleartextStorageProperties.ql index 7a9626f94dd..7f05192357d 100644 --- a/java/ql/src/Security/CWE/CWE-312/CleartextStorageProperties.ql +++ b/java/ql/src/Security/CWE/CWE-312/CleartextStorageProperties.ql @@ -3,6 +3,7 @@ * @description Storing sensitive information in cleartext can expose it to an attacker. * @kind problem * @problem.severity warning + * @security-severity 6.4 * @precision medium * @id java/cleartext-storage-in-properties * @tags security diff --git a/java/ql/src/Security/CWE/CWE-319/HttpsUrls.ql b/java/ql/src/Security/CWE/CWE-319/HttpsUrls.ql index 306bf27ab9c..544197f0252 100644 --- a/java/ql/src/Security/CWE/CWE-319/HttpsUrls.ql +++ b/java/ql/src/Security/CWE/CWE-319/HttpsUrls.ql @@ -3,6 +3,7 @@ * @description Non-HTTPS connections can be intercepted by third parties. * @kind path-problem * @problem.severity recommendation + * @security-severity 5.2 * @precision medium * @id java/non-https-url * @tags security @@ -13,9 +14,10 @@ import java import semmle.code.java.dataflow.TaintTracking import semmle.code.java.frameworks.Networking import DataFlow::PathGraph +private import semmle.code.java.dataflow.ExternalFlow -class HTTPString extends StringLiteral { - HTTPString() { +class HttpString extends StringLiteral { + HttpString() { // Avoid matching "https" here. exists(string s | this.getRepresentedString() = s | ( @@ -30,26 +32,12 @@ class HTTPString extends StringLiteral { } } -class URLOpenMethod extends Method { - URLOpenMethod() { - this.getDeclaringType().getQualifiedName() = "java.net.URL" and - ( - this.getName() = "openConnection" or - this.getName() = "openStream" - ) - } -} +class HttpStringToUrlOpenMethodFlowConfig extends TaintTracking::Configuration { + HttpStringToUrlOpenMethodFlowConfig() { this = "HttpsUrls::HttpStringToUrlOpenMethodFlowConfig" } -class HTTPStringToURLOpenMethodFlowConfig extends TaintTracking::Configuration { - HTTPStringToURLOpenMethodFlowConfig() { this = "HttpsUrls::HTTPStringToURLOpenMethodFlowConfig" } + override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof HttpString } - override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof HTTPString } - - override predicate isSink(DataFlow::Node sink) { - exists(MethodAccess m | - sink.asExpr() = m.getQualifier() and m.getMethod() instanceof URLOpenMethod - ) - } + override predicate isSink(DataFlow::Node sink) { sink instanceof UrlOpenSink } override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { exists(UrlConstructorCall u | @@ -63,10 +51,17 @@ class HTTPStringToURLOpenMethodFlowConfig extends TaintTracking::Configuration { } } -from DataFlow::PathNode source, DataFlow::PathNode sink, MethodAccess m, HTTPString s +/** + * A sink that represents a URL opening method call, such as a call to `java.net.URL.openConnection()`. + */ +private class UrlOpenSink extends DataFlow::Node { + UrlOpenSink() { sinkNode(this, "open-url") } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, MethodAccess m, HttpString s where source.getNode().asExpr() = s and sink.getNode().asExpr() = m.getQualifier() and - any(HTTPStringToURLOpenMethodFlowConfig c).hasFlowPath(source, sink) + any(HttpStringToUrlOpenMethodFlowConfig c).hasFlowPath(source, sink) select m, source, sink, "URL may have been constructed with HTTP protocol, using $@.", s, "this source" diff --git a/java/ql/src/Security/CWE/CWE-319/UseSSL.ql b/java/ql/src/Security/CWE/CWE-319/UseSSL.ql index 070c766ba10..b4fabf15940 100644 --- a/java/ql/src/Security/CWE/CWE-319/UseSSL.ql +++ b/java/ql/src/Security/CWE/CWE-319/UseSSL.ql @@ -3,6 +3,7 @@ * @description Non-SSL connections can be intercepted by third parties. * @kind problem * @problem.severity recommendation + * @security-severity 5.2 * @precision medium * @id java/non-ssl-connection * @tags security diff --git a/java/ql/src/Security/CWE/CWE-319/UseSSLSocketFactories.ql b/java/ql/src/Security/CWE/CWE-319/UseSSLSocketFactories.ql index b594f9d8fc1..6fa51d4caf2 100644 --- a/java/ql/src/Security/CWE/CWE-319/UseSSLSocketFactories.ql +++ b/java/ql/src/Security/CWE/CWE-319/UseSSLSocketFactories.ql @@ -4,6 +4,7 @@ * third parties. * @kind problem * @problem.severity recommendation + * @security-severity 5.2 * @precision medium * @id java/non-ssl-socket-factory * @tags security diff --git a/java/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql b/java/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql index d67637b0a48..194d7ecf7d5 100644 --- a/java/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql +++ b/java/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql @@ -3,6 +3,7 @@ * @description Using broken or weak cryptographic algorithms can allow an attacker to compromise security. * @kind path-problem * @problem.severity warning + * @security-severity 5.2 * @precision high * @id java/weak-cryptographic-algorithm * @tags security diff --git a/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql b/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql index c84abe1ff75..fac3d66f2b8 100644 --- a/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql +++ b/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql @@ -3,6 +3,7 @@ * @description Using broken or weak cryptographic algorithms can allow an attacker to compromise security. * @kind path-problem * @problem.severity warning + * @security-severity 5.2 * @precision medium * @id java/potentially-weak-cryptographic-algorithm * @tags security @@ -33,9 +34,8 @@ class InsecureAlgoLiteral extends ShortStringLiteral { } predicate objectToString(MethodAccess ma) { - exists(Method m | + exists(ToStringMethod m | m = ma.getMethod() and - m.hasName("toString") and m.getDeclaringType() instanceof TypeObject and variableTrack(ma.getQualifier()).getType().getErasure() instanceof TypeObject ) diff --git a/java/ql/src/Security/CWE/CWE-335/PredictableSeed.ql b/java/ql/src/Security/CWE/CWE-335/PredictableSeed.ql index e0a53b59977..2cd7bbae1dd 100644 --- a/java/ql/src/Security/CWE/CWE-335/PredictableSeed.ql +++ b/java/ql/src/Security/CWE/CWE-335/PredictableSeed.ql @@ -3,9 +3,11 @@ * @description Using a predictable seed in a pseudo-random number generator can lead to predictability of the numbers generated by it. * @kind problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id java/predictable-seed * @tags security + * external/cwe/cwe-335 */ import java diff --git a/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.ql b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.ql index 8eda68ebe70..6a456fbae32 100644 --- a/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.ql +++ b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.ql @@ -3,6 +3,7 @@ * @description Using a vulnerable version of JHipster to generate random numbers makes it easier for attackers to take over accounts. * @kind problem * @problem.severity error + * @security-severity 5.9 * @precision very-high * @id java/jhipster-prng * @tags security diff --git a/java/ql/src/Security/CWE/CWE-352/SpringCSRFProtection.ql b/java/ql/src/Security/CWE/CWE-352/SpringCSRFProtection.ql index 354dee75d83..d7fe7b8611e 100644 --- a/java/ql/src/Security/CWE/CWE-352/SpringCSRFProtection.ql +++ b/java/ql/src/Security/CWE/CWE-352/SpringCSRFProtection.ql @@ -4,6 +4,7 @@ * a Cross-Site Request Forgery (CSRF) attack. * @kind problem * @problem.severity error + * @security-severity 6.4 * @precision high * @id java/spring-disabled-csrf-protection * @tags security diff --git a/java/ql/src/Security/CWE/CWE-367/TOCTOURace.ql b/java/ql/src/Security/CWE/CWE-367/TOCTOURace.ql index d88b946e44d..740dd6ba314 100644 --- a/java/ql/src/Security/CWE/CWE-367/TOCTOURace.ql +++ b/java/ql/src/Security/CWE/CWE-367/TOCTOURace.ql @@ -4,6 +4,7 @@ * if the state may be changed between the check and use. * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision medium * @id java/toctou-race-condition * @tags security diff --git a/java/ql/src/Security/CWE/CWE-421/SocketAuthRace.ql b/java/ql/src/Security/CWE/CWE-421/SocketAuthRace.ql index 7b2870a1b28..d4301a3d620 100644 --- a/java/ql/src/Security/CWE/CWE-421/SocketAuthRace.ql +++ b/java/ql/src/Security/CWE/CWE-421/SocketAuthRace.ql @@ -3,6 +3,7 @@ * @description Opening a socket after authenticating via a different channel may allow an attacker to connect to the port first. * @kind problem * @problem.severity warning + * @security-severity 10.0 * @precision medium * @id java/socket-auth-race-condition * @tags security diff --git a/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.qhelp b/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.qhelp index f1831ac5c21..e9600f11b93 100644 --- a/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.qhelp +++ b/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.qhelp @@ -69,7 +69,7 @@ Or How I Learned to Start Worrying and Hate Java Object Deserialization.
  • Alvaro Muñoz & Christian Schneider, RSAConference 2016: -Serial Killer: Silently Pwning Your Java Endpoints. +Serial Killer: Silently Pwning Your Java Endpoints.
  • SnakeYaml documentation on deserialization: diff --git a/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.ql b/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.ql index 27ababae1d4..8fbeb16d63c 100644 --- a/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.ql +++ b/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.ql @@ -4,6 +4,7 @@ * execute arbitrary code. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id java/unsafe-deserialization * @tags security diff --git a/java/ql/src/Security/CWE/CWE-601/UrlRedirect.ql b/java/ql/src/Security/CWE/CWE-601/UrlRedirect.ql index 455f6add626..7d072091245 100644 --- a/java/ql/src/Security/CWE/CWE-601/UrlRedirect.ql +++ b/java/ql/src/Security/CWE/CWE-601/UrlRedirect.ql @@ -4,6 +4,7 @@ * may cause redirection to malicious web sites. * @kind path-problem * @problem.severity error + * @security-severity 2.7 * @precision high * @id java/unvalidated-url-redirection * @tags security diff --git a/java/ql/src/Security/CWE/CWE-601/UrlRedirectLocal.ql b/java/ql/src/Security/CWE/CWE-601/UrlRedirectLocal.ql index e060d15ab9f..4f60a15d8a6 100644 --- a/java/ql/src/Security/CWE/CWE-601/UrlRedirectLocal.ql +++ b/java/ql/src/Security/CWE/CWE-601/UrlRedirectLocal.ql @@ -4,6 +4,7 @@ * may cause redirection to malicious web sites. * @kind path-problem * @problem.severity recommendation + * @security-severity 2.7 * @precision medium * @id java/unvalidated-url-redirection-local * @tags security diff --git a/java/ql/src/Security/CWE/CWE-611/XXE.qhelp b/java/ql/src/Security/CWE/CWE-611/XXE.qhelp index 93d420f7495..c2557883080 100644 --- a/java/ql/src/Security/CWE/CWE-611/XXE.qhelp +++ b/java/ql/src/Security/CWE/CWE-611/XXE.qhelp @@ -56,11 +56,11 @@ OWASP guidance on parsing xml files:
  • Paper by Timothy Morgen: -XML Schema, DTD, and Entity Attacks +XML Schema, DTD, and Entity Attacks
  • Out-of-band data retrieval: Timur Yunusov & Alexey Osipov, Black hat EU 2013: -XML Out-Of-Band Data Retrieval. +XML Out-Of-Band Data Retrieval.
  • Denial of service attack (Billion laughs): diff --git a/java/ql/src/Security/CWE/CWE-611/XXE.ql b/java/ql/src/Security/CWE/CWE-611/XXE.ql index 432cc6d38d7..dc277337769 100644 --- a/java/ql/src/Security/CWE/CWE-611/XXE.ql +++ b/java/ql/src/Security/CWE/CWE-611/XXE.ql @@ -4,6 +4,7 @@ * references may lead to disclosure of confidential data or denial of service. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id java/xxe * @tags security diff --git a/java/ql/src/Security/CWE/CWE-614/InsecureCookie.ql b/java/ql/src/Security/CWE/CWE-614/InsecureCookie.ql index 8a8bc656aba..ef6d143ece8 100644 --- a/java/ql/src/Security/CWE/CWE-614/InsecureCookie.ql +++ b/java/ql/src/Security/CWE/CWE-614/InsecureCookie.ql @@ -4,6 +4,7 @@ * interception. * @kind problem * @problem.severity error + * @security-severity 2.9 * @precision high * @id java/insecure-cookie * @tags security diff --git a/java/ql/src/experimental/Security/CWE/CWE-643/XPathInjection.java b/java/ql/src/Security/CWE/CWE-643/XPathInjection.java similarity index 83% rename from java/ql/src/experimental/Security/CWE/CWE-643/XPathInjection.java rename to java/ql/src/Security/CWE/CWE-643/XPathInjection.java index 95a38a5a7ac..093ac32fa6a 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-643/XPathInjection.java +++ b/java/ql/src/Security/CWE/CWE-643/XPathInjection.java @@ -58,9 +58,19 @@ try { // Bad Dom4j org.dom4j.io.SAXReader reader = new org.dom4j.io.SAXReader(); org.dom4j.Document document = reader.read(new InputSource(new StringReader(xmlStr))); - isExist = document.selectSingleNode("/users/user[@name='" + user + "' and @pass='" + pass + "']").hasContent(); + isExist = document.selectSingleNode("/users/user[@name='" + user + "' and @pass='" + pass + "']") != null; // or document.selectNodes System.out.println(isExist); + + // Good Dom4j + org.jaxen.SimpleVariableContext svc = new org.jaxen.SimpleVariableContext(); + svc.setVariableValue("user", user); + svc.setVariableValue("pass", pass); + String xpathString = "/users/user[@name=$user and @pass=$pass]"; + org.dom4j.XPath safeXPath = document.createXPath(xpathString); + safeXPath.setVariableContext(svc); + isExist = safeXPath.selectSingleNode(document) != null; + System.out.println(isExist); } } catch (ParserConfigurationException e) { diff --git a/java/ql/src/experimental/Security/CWE/CWE-643/XPathInjection.qhelp b/java/ql/src/Security/CWE/CWE-643/XPathInjection.qhelp similarity index 56% rename from java/ql/src/experimental/Security/CWE/CWE-643/XPathInjection.qhelp rename to java/ql/src/Security/CWE/CWE-643/XPathInjection.qhelp index 91d110b80aa..31441d70a18 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-643/XPathInjection.qhelp +++ b/java/ql/src/Security/CWE/CWE-643/XPathInjection.qhelp @@ -5,14 +5,14 @@

    If an XPath expression is built using string concatenation, and the components of the concatenation -include user input, a user is likely to be able to create a malicious XPath expression. +include user input, it makes it very easy for a user to create a malicious XPath expression.

    -If user input must be included in an XPath expression, pre-compile the query and use variable -references to include the user input. +If user input must be included in an XPath expression, either sanitize the data or pre-compile the query +and use variable references to include the user input.

    XPath injection can also be prevented by using XQuery. @@ -22,23 +22,23 @@ XPath injection can also be prevented by using XQuery.

    -In the first, second, and third example, the code accepts a name and password specified by the user, and uses this +In the first three examples, the code accepts a name and password specified by the user, and uses this unvalidated and unsanitized value in an XPath expression. This is vulnerable to the user providing special characters or string sequences that change the meaning of the XPath expression to search for different values.

    -In the fourth example, the code utilizes setXPathVariableResolver which prevents XPath Injection. +In the fourth example, the code uses setXPathVariableResolver which prevents XPath injection.

    -The fifth example is a dom4j XPath injection example. +The final two examples are for dom4j. They show an example of XPath injection and one method of preventing it.

  • OWASP: Testing for XPath Injection.
  • -
  • OWASP: XPath Injection.
  • +
  • OWASP: XPath Injection.
  • diff --git a/java/ql/src/experimental/Security/CWE/CWE-643/XPathInjection.ql b/java/ql/src/Security/CWE/CWE-643/XPathInjection.ql similarity index 64% rename from java/ql/src/experimental/Security/CWE/CWE-643/XPathInjection.ql rename to java/ql/src/Security/CWE/CWE-643/XPathInjection.ql index e5a29df46d5..0dd73370569 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-643/XPathInjection.ql +++ b/java/ql/src/Security/CWE/CWE-643/XPathInjection.ql @@ -4,6 +4,7 @@ * malicious code by the user. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id java/xml/xpath-injection * @tags security @@ -13,7 +14,7 @@ import java import semmle.code.java.dataflow.FlowSources import semmle.code.java.dataflow.TaintTracking -import semmle.code.java.security.XmlParsers +import semmle.code.java.security.XPath import DataFlow::PathGraph class XPathInjectionConfiguration extends TaintTracking::Configuration { @@ -24,20 +25,6 @@ class XPathInjectionConfiguration extends TaintTracking::Configuration { override predicate isSink(DataFlow::Node sink) { sink instanceof XPathInjectionSink } } -class XPathInjectionSink extends DataFlow::ExprNode { - XPathInjectionSink() { - exists(Method m, MethodAccess ma | ma.getMethod() = m | - m.getDeclaringType().hasQualifiedName("javax.xml.xpath", "XPath") and - (m.hasName("evaluate") or m.hasName("compile")) and - ma.getArgument(0) = this.getExpr() - or - m.getDeclaringType().hasQualifiedName("org.dom4j", "Node") and - (m.hasName("selectNodes") or m.hasName("selectSingleNode")) and - ma.getArgument(0) = this.getExpr() - ) - } -} - from DataFlow::PathNode source, DataFlow::PathNode sink, XPathInjectionConfiguration c where c.hasFlowPath(source, sink) select sink.getNode(), source, sink, "$@ flows to here and is used in an XPath expression.", diff --git a/java/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.ql b/java/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.ql index f1449012363..662e2c487ab 100644 --- a/java/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.ql +++ b/java/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.ql @@ -3,6 +3,7 @@ * @description Certain standard library routines are dangerous to call. * @kind problem * @problem.severity warning + * @security-severity 10.0 * @precision medium * @id java/potentially-dangerous-function * @tags reliability diff --git a/java/ql/src/Security/CWE/CWE-681/NumericCastTainted.ql b/java/ql/src/Security/CWE/CWE-681/NumericCastTainted.ql index 1f54800a091..0518b99e221 100644 --- a/java/ql/src/Security/CWE/CWE-681/NumericCastTainted.ql +++ b/java/ql/src/Security/CWE/CWE-681/NumericCastTainted.ql @@ -4,6 +4,7 @@ * can cause unexpected truncation. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id java/tainted-numeric-cast * @tags security diff --git a/java/ql/src/Security/CWE/CWE-681/NumericCastTaintedLocal.ql b/java/ql/src/Security/CWE/CWE-681/NumericCastTaintedLocal.ql index 9dadb0ae443..519e4c398ee 100644 --- a/java/ql/src/Security/CWE/CWE-681/NumericCastTaintedLocal.ql +++ b/java/ql/src/Security/CWE/CWE-681/NumericCastTaintedLocal.ql @@ -4,6 +4,7 @@ * can cause unexpected truncation. * @kind path-problem * @problem.severity recommendation + * @security-severity 5.9 * @precision medium * @id java/tainted-numeric-cast-local * @tags security diff --git a/java/ql/src/Security/CWE/CWE-732/ReadingFromWorldWritableFile.ql b/java/ql/src/Security/CWE/CWE-732/ReadingFromWorldWritableFile.ql index f87733f0e9f..3797f11dfd5 100644 --- a/java/ql/src/Security/CWE/CWE-732/ReadingFromWorldWritableFile.ql +++ b/java/ql/src/Security/CWE/CWE-732/ReadingFromWorldWritableFile.ql @@ -4,6 +4,7 @@ * the file may be modified or removed by external actors. * @kind problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id java/world-writable-file-read * @tags security diff --git a/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsApiCall.ql b/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsApiCall.ql index 4fb3f14c993..5300e3864ef 100644 --- a/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsApiCall.ql +++ b/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsApiCall.ql @@ -3,6 +3,7 @@ * @description Using a hard-coded credential in a call to a sensitive Java API may compromise security. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision medium * @id java/hardcoded-credential-api-call * @tags security @@ -19,14 +20,14 @@ class HardcodedCredentialApiCallConfiguration extends DataFlow::Configuration { override predicate isSource(DataFlow::Node n) { n.asExpr() instanceof HardcodedExpr and - not n.asExpr().getEnclosingCallable().getName() = "toString" + not n.asExpr().getEnclosingCallable() instanceof ToStringMethod } override predicate isSink(DataFlow::Node n) { n.asExpr() instanceof CredentialsApiSink } override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { node1.asExpr().getType() instanceof TypeString and - exists(MethodAccess ma | ma.getMethod().getName().regexpMatch("getBytes|toCharArray") | + exists(MethodAccess ma | ma.getMethod().hasName(["getBytes", "toCharArray"]) | node2.asExpr() = ma and ma.getQualifier() = node1.asExpr() ) diff --git a/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsComparison.ql b/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsComparison.ql index 0d955b4ed08..8583f4bef39 100644 --- a/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsComparison.ql +++ b/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsComparison.ql @@ -3,6 +3,7 @@ * @description Comparing a parameter to a hard-coded credential may compromise security. * @kind problem * @problem.severity error + * @security-severity 5.9 * @precision low * @id java/hardcoded-credential-comparison * @tags security diff --git a/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsSourceCall.ql b/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsSourceCall.ql index 93d0dde665a..724916f1511 100644 --- a/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsSourceCall.ql +++ b/java/ql/src/Security/CWE/CWE-798/HardcodedCredentialsSourceCall.ql @@ -3,6 +3,7 @@ * @description Using a hard-coded credential in a sensitive call may compromise security. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision low * @id java/hardcoded-credential-sensitive-call * @tags security diff --git a/java/ql/src/Security/CWE/CWE-798/HardcodedPasswordField.ql b/java/ql/src/Security/CWE/CWE-798/HardcodedPasswordField.ql index 7c0ca38263e..4464268a5ec 100644 --- a/java/ql/src/Security/CWE/CWE-798/HardcodedPasswordField.ql +++ b/java/ql/src/Security/CWE/CWE-798/HardcodedPasswordField.ql @@ -3,6 +3,7 @@ * @description Hard-coding a password string may compromise security. * @kind problem * @problem.severity error + * @security-severity 5.9 * @precision low * @id java/hardcoded-password-field * @tags security diff --git a/java/ql/src/Security/CWE/CWE-807/ConditionalBypass.ql b/java/ql/src/Security/CWE/CWE-807/ConditionalBypass.ql index fd813dd3dd8..6f1c7275bb4 100644 --- a/java/ql/src/Security/CWE/CWE-807/ConditionalBypass.ql +++ b/java/ql/src/Security/CWE/CWE-807/ConditionalBypass.ql @@ -4,6 +4,7 @@ * passing through authentication systems. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision medium * @id java/user-controlled-bypass * @tags security diff --git a/java/ql/src/Security/CWE/CWE-807/TaintedPermissionsCheck.ql b/java/ql/src/Security/CWE/CWE-807/TaintedPermissionsCheck.ql index 542b98b157d..7eb03c1ecd7 100644 --- a/java/ql/src/Security/CWE/CWE-807/TaintedPermissionsCheck.ql +++ b/java/ql/src/Security/CWE/CWE-807/TaintedPermissionsCheck.ql @@ -4,6 +4,7 @@ * permissions being granted. * @kind path-problem * @problem.severity error + * @security-severity 5.9 * @precision high * @id java/tainted-permissions-check * @tags security diff --git a/java/ql/src/Security/CWE/CWE-829/InsecureDependencyResolution.ql b/java/ql/src/Security/CWE/CWE-829/InsecureDependencyResolution.ql index 50c2dc1e05e..d3b833eaf72 100644 --- a/java/ql/src/Security/CWE/CWE-829/InsecureDependencyResolution.ql +++ b/java/ql/src/Security/CWE/CWE-829/InsecureDependencyResolution.ql @@ -3,6 +3,7 @@ * @description Non-HTTPS connections can be intercepted by third parties. * @kind problem * @problem.severity error + * @security-severity 5.9 * @precision very-high * @id java/maven/non-https-url * @tags security diff --git a/java/ql/src/Security/CWE/CWE-833/LockOrderInconsistency.ql b/java/ql/src/Security/CWE/CWE-833/LockOrderInconsistency.ql index 795c5e1c925..241965f4b09 100644 --- a/java/ql/src/Security/CWE/CWE-833/LockOrderInconsistency.ql +++ b/java/ql/src/Security/CWE/CWE-833/LockOrderInconsistency.ql @@ -3,6 +3,7 @@ * @description Acquiring multiple locks in a different order may cause deadlock. * @kind problem * @problem.severity recommendation + * @security-severity 6.9 * @precision medium * @id java/lock-order-inconsistency * @tags security diff --git a/java/ql/src/Security/CWE/CWE-835/InfiniteLoop.ql b/java/ql/src/Security/CWE/CWE-835/InfiniteLoop.ql index cc02dfb3f09..4fe1c38c6d5 100644 --- a/java/ql/src/Security/CWE/CWE-835/InfiniteLoop.ql +++ b/java/ql/src/Security/CWE/CWE-835/InfiniteLoop.ql @@ -5,6 +5,7 @@ * looping. * @kind problem * @problem.severity warning + * @security-severity 3.6 * @precision medium * @id java/unreachable-exit-in-loop * @tags security diff --git a/java/ql/src/Violations of Best Practice/Dead Code/FinalizerNullsFields.qhelp b/java/ql/src/Violations of Best Practice/Dead Code/FinalizerNullsFields.qhelp index f85888f651c..5be6edda989 100644 --- a/java/ql/src/Violations of Best Practice/Dead Code/FinalizerNullsFields.qhelp +++ b/java/ql/src/Violations of Best Practice/Dead Code/FinalizerNullsFields.qhelp @@ -77,7 +77,7 @@ severely affect performance, and you should avoid defining finalize
  • IBM developerWorks: - Java theory and practice: Explicit nulling. + Java theory and practice: Explicit nulling.
  • Oracle Technology Network: diff --git a/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToStringToString.ql b/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToStringToString.ql index 40a4e36d70c..c1395669405 100644 --- a/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToStringToString.ql +++ b/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToStringToString.ql @@ -10,9 +10,8 @@ import java -from MethodAccess ma, Method tostring +from MethodAccess ma, ToStringMethod tostring where - tostring.hasName("toString") and tostring.getDeclaringType() instanceof TypeString and ma.getMethod() = tostring select ma, "Redundant call to 'toString' on a String object." diff --git a/java/ql/src/Violations of Best Practice/Undesirable Calls/DefaultToString.ql b/java/ql/src/Violations of Best Practice/Undesirable Calls/DefaultToString.ql index 7d9ef5b85f9..c6eaf5af2cb 100644 --- a/java/ql/src/Violations of Best Practice/Undesirable Calls/DefaultToString.ql +++ b/java/ql/src/Violations of Best Practice/Undesirable Calls/DefaultToString.ql @@ -14,20 +14,13 @@ import java import semmle.code.java.StringFormat predicate explicitToStringCall(Expr e) { - exists(MethodAccess ma, Method toString | toString = ma.getMethod() | - e = ma.getQualifier() and - toString.getName() = "toString" and - toString.getNumberOfParameters() = 0 and - not toString.isStatic() + exists(MethodAccess ma | + ma.getMethod() instanceof ToStringMethod and + e = ma.getQualifier() ) } -predicate directlyDeclaresToString(Class c) { - exists(Method m | m.getDeclaringType() = c | - m.getName() = "toString" and - m.getNumberOfParameters() = 0 - ) -} +predicate directlyDeclaresToString(Class c) { any(ToStringMethod m).getDeclaringType() = c } predicate inheritsObjectToString(Class t) { not directlyDeclaresToString(t.getSourceDeclaration()) and diff --git a/java/ql/src/Violations of Best Practice/legacy/ParameterAssignment.qhelp b/java/ql/src/Violations of Best Practice/legacy/ParameterAssignment.qhelp index a4a39d81bc6..260f8f7c9c9 100644 --- a/java/ql/src/Violations of Best Practice/legacy/ParameterAssignment.qhelp +++ b/java/ql/src/Violations of Best Practice/legacy/ParameterAssignment.qhelp @@ -41,7 +41,7 @@ Help - Eclipse Platform:
  • Java Basics: -Methods 4 - Local variables. +Methods 4 - Local variables.
  • diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.qhelp b/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.qhelp new file mode 100644 index 00000000000..e201156728a --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.qhelp @@ -0,0 +1,47 @@ + + + +

    Spring Boot is a popular framework that facilitates the development of stand-alone applications +and micro services. Spring Boot Actuator helps to expose production-ready support features against +Spring Boot applications.

    + +

    Endpoints of Spring Boot Actuator allow to monitor and interact with a Spring Boot application. +Exposing unprotected actuator endpoints through configuration files can lead to information disclosure +or even remote code execution vulnerability.

    + +

    Rather than programmatically permitting endpoint requests or enforcing access control, frequently +developers simply leave management endpoints publicly accessible in the application configuration file +application.properties without enforcing access control through Spring Security.

    +
    + + +

    Declare the Spring Boot Starter Security module in XML configuration or programmatically enforce +security checks on management endpoints using Spring Security. Otherwise accessing management endpoints +on a different HTTP port other than the port that the web application is listening on also helps to +improve the security.

    +
    + + +

    The following examples show both 'BAD' and 'GOOD' configurations. In the 'BAD' configuration, +no security module is declared and sensitive management endpoints are exposed. In the 'GOOD' configuration, +security is enforced and only endpoints requiring exposure are exposed.

    + + + +
    + + +
  • + Spring Boot documentation: + Spring Boot Actuator: Production-ready Features +
  • +
  • + VERACODE Blog: + Exploiting Spring Boot Actuators +
  • +
  • + HackerOne Report: + Spring Actuator endpoints publicly available, leading to account takeover +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql b/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql new file mode 100644 index 00000000000..e6965959d13 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql @@ -0,0 +1,116 @@ +/** + * @name Insecure Spring Boot Actuator Configuration + * @description Exposed Spring Boot Actuator through configuration files without declarative or procedural + * security enforcement leads to information leak or even remote code execution. + * @kind problem + * @id java/insecure-spring-actuator-config + * @tags security + * external/cwe-016 + */ + +/* + * Note this query requires properties files to be indexed before it can produce results. + * If creating your own database with the CodeQL CLI, you should run + * `codeql database index-files --language=properties ...` + * If using lgtm.com, you should add `properties_files: true` to the index block of your + * lgtm.yml file (see https://lgtm.com/help/lgtm/java-extraction) + */ + +import java +import semmle.code.configfiles.ConfigFiles +import semmle.code.xml.MavenPom + +/** The parent node of the `org.springframework.boot` group. */ +class SpringBootParent extends Parent { + SpringBootParent() { this.getGroup().getValue() = "org.springframework.boot" } +} + +/** Class of Spring Boot dependencies. */ +class SpringBootPom extends Pom { + SpringBootPom() { this.getParentElement() instanceof SpringBootParent } + + /** Holds if the Spring Boot Actuator module `spring-boot-starter-actuator` is used in the project. */ + predicate isSpringBootActuatorUsed() { + this.getADependency().getArtifact().getValue() = "spring-boot-starter-actuator" + } + + /** + * Holds if the Spring Boot Security module is used in the project, which brings in other security + * related libraries. + */ + predicate isSpringBootSecurityUsed() { + this.getADependency().getArtifact().getValue() = "spring-boot-starter-security" + } +} + +/** The properties file `application.properties`. */ +class ApplicationProperties extends ConfigPair { + ApplicationProperties() { this.getFile().getBaseName() = "application.properties" } +} + +/** The configuration property `management.security.enabled`. */ +class ManagementSecurityConfig extends ApplicationProperties { + ManagementSecurityConfig() { this.getNameElement().getName() = "management.security.enabled" } + + /** Gets the whitespace-trimmed value of this property. */ + string getValue() { result = this.getValueElement().getValue().trim() } + + /** Holds if `management.security.enabled` is set to `false`. */ + predicate hasSecurityDisabled() { getValue() = "false" } + + /** Holds if `management.security.enabled` is set to `true`. */ + predicate hasSecurityEnabled() { getValue() = "true" } +} + +/** The configuration property `management.endpoints.web.exposure.include`. */ +class ManagementEndPointInclude extends ApplicationProperties { + ManagementEndPointInclude() { + this.getNameElement().getName() = "management.endpoints.web.exposure.include" + } + + /** Gets the whitespace-trimmed value of this property. */ + string getValue() { result = this.getValueElement().getValue().trim() } +} + +/** + * Holds if `ApplicationProperties` ap of a repository managed by `SpringBootPom` pom + * has a vulnerable configuration of Spring Boot Actuator management endpoints. + */ +predicate hasConfidentialEndPointExposed(SpringBootPom pom, ApplicationProperties ap) { + pom.isSpringBootActuatorUsed() and + not pom.isSpringBootSecurityUsed() and + ap.getFile() + .getParentContainer() + .getAbsolutePath() + .matches(pom.getFile().getParentContainer().getAbsolutePath() + "%") and // in the same sub-directory + exists(string springBootVersion | springBootVersion = pom.getParentElement().getVersionString() | + springBootVersion.regexpMatch("1\\.[0-4].*") and // version 1.0, 1.1, ..., 1.4 + not exists(ManagementSecurityConfig me | + me.hasSecurityEnabled() and me.getFile() = ap.getFile() + ) + or + springBootVersion.matches("1.5%") and // version 1.5 + exists(ManagementSecurityConfig me | me.hasSecurityDisabled() and me.getFile() = ap.getFile()) + or + springBootVersion.matches("2.%") and //version 2.x + exists(ManagementEndPointInclude mi | + mi.getFile() = ap.getFile() and + ( + mi.getValue() = "*" // all endpoints are enabled + or + mi.getValue() + .matches([ + "%dump%", "%trace%", "%logfile%", "%shutdown%", "%startup%", "%mappings%", "%env%", + "%beans%", "%sessions%" + ]) // confidential endpoints to check although all endpoints apart from '/health' and '/info' are considered sensitive by Spring + ) + ) + ) +} + +from SpringBootPom pom, ApplicationProperties ap, Dependency d +where + hasConfidentialEndPointExposed(pom, ap) and + d = pom.getADependency() and + d.getArtifact().getValue() = "spring-boot-starter-actuator" +select d, "Insecure configuration of Spring Boot Actuator exposes sensitive endpoints." diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/application.properties b/java/ql/src/experimental/Security/CWE/CWE-016/application.properties new file mode 100644 index 00000000000..4f5defdd948 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-016/application.properties @@ -0,0 +1,22 @@ +#management.endpoints.web.base-path=/admin + + +#### BAD: All management endpoints are accessible #### +# vulnerable configuration (spring boot 1.0 - 1.4): exposes actuators by default + +# vulnerable configuration (spring boot 1.5+): requires value false to expose sensitive actuators +management.security.enabled=false + +# vulnerable configuration (spring boot 2+): exposes health and info only by default, here overridden to expose everything +management.endpoints.web.exposure.include=* + + +#### GOOD: All management endpoints have access control #### +# safe configuration (spring boot 1.0 - 1.4): exposes actuators by default +management.security.enabled=true + +# safe configuration (spring boot 1.5+): requires value false to expose sensitive actuators +management.security.enabled=true + +# safe configuration (spring boot 2+): exposes health and info only by default, here overridden to expose one additional endpoint which we assume is intentional and safe. +management.endpoints.web.exposure.include=beans,info,health diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/pom_bad.xml b/java/ql/src/experimental/Security/CWE/CWE-016/pom_bad.xml new file mode 100644 index 00000000000..9dd5c9c188b --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-016/pom_bad.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + spring-boot-actuator-app + spring-boot-actuator-app + 1.0-SNAPSHOT + + + UTF-8 + 1.8 + 1.8 + + + + org.springframework.boot + spring-boot-starter-parent + 2.3.8.RELEASE + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-devtools + + + + + + + org.springframework.boot + spring-boot-test + + + + \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/pom_good.xml b/java/ql/src/experimental/Security/CWE/CWE-016/pom_good.xml new file mode 100644 index 00000000000..89f577f21e5 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-016/pom_good.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + spring-boot-actuator-app + spring-boot-actuator-app + 1.0-SNAPSHOT + + + UTF-8 + 1.8 + 1.8 + + + + org.springframework.boot + spring-boot-starter-parent + 2.3.8.RELEASE + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-devtools + + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.boot + spring-boot-test + + + + \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/ExecCommon.qll b/java/ql/src/experimental/Security/CWE/CWE-078/ExecCommon.qll new file mode 100644 index 00000000000..c0025043fce --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-078/ExecCommon.qll @@ -0,0 +1,32 @@ +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.security.ExternalProcess +import semmle.code.java.security.CommandArguments + +private class RemoteUserInputToArgumentToExecFlowConfig extends TaintTracking::Configuration { + RemoteUserInputToArgumentToExecFlowConfig() { + this = "ExecCommon::RemoteUserInputToArgumentToExecFlowConfig" + } + + override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof ArgumentToExec } + + override predicate isSanitizer(DataFlow::Node node) { + node.getType() instanceof PrimitiveType + or + node.getType() instanceof BoxedType + or + isSafeCommandArgument(node.asExpr()) + } +} + +/** + * Implementation of `ExecTainted.ql`. It is extracted to a QLL + * so that it can be excluded from `ExecUnescaped.ql` to avoid + * reporting overlapping results. + */ +predicate execTainted(DataFlow::PathNode source, DataFlow::PathNode sink, ArgumentToExec execArg) { + exists(RemoteUserInputToArgumentToExecFlowConfig conf | + conf.hasFlowPath(source, sink) and sink.getNode() = DataFlow::exprNode(execArg) + ) +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql b/java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql new file mode 100644 index 00000000000..b73203ecbbc --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql @@ -0,0 +1,24 @@ +/** + * @name Uncontrolled command line + * @description Using externally controlled strings in a command line is vulnerable to malicious + * changes in the strings. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/command-line-injection + * @tags security + * external/cwe/cwe-078 + * external/cwe/cwe-088 + */ + +import java +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.security.ExternalProcess +import ExecCommon +import JSchOSInjection +import DataFlow::PathGraph + +from DataFlow::PathNode source, DataFlow::PathNode sink, ArgumentToExec execArg +where execTainted(source, sink, execArg) +select execArg, source, sink, "$@ flows to here and is used in a command.", source.getNode(), + "User-provided value" diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qll b/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qll new file mode 100644 index 00000000000..ec1f4d0adfa --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qll @@ -0,0 +1,20 @@ +/** + * Provides classes for JSch OS command injection detection + */ + +import java + +/** The class `com.jcraft.jsch.ChannelExec`. */ +private class JSchChannelExec extends RefType { + JSchChannelExec() { this.hasQualifiedName("com.jcraft.jsch", "ChannelExec") } +} + +/** A method to set an OS Command for the execution. */ +private class ChannelExecSetCommandMethod extends Method, ExecCallable { + ChannelExecSetCommandMethod() { + this.hasName("setCommand") and + this.getDeclaringType() instanceof JSchChannelExec + } + + override int getAnExecutedArgument() { result = 0 } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjectionBad.java b/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjectionBad.java new file mode 100644 index 00000000000..ab4c3fb1c06 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjectionBad.java @@ -0,0 +1,17 @@ +public class JSchOSInjectionBad { + void jschOsExecution(HttpServletRequest request) { + String command = request.getParameter("command"); + + JSch jsch = new JSch(); + Session session = jsch.getSession("user", "sshHost", 22); + session.setPassword("password"); + session.connect(); + + Channel channel = session.openChannel("exec"); + // BAD - untrusted user data is used directly in a command + ((ChannelExec) channel).setCommand("ping " + command); + + channel.connect(); + } +} + diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjectionSanitized.java b/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjectionSanitized.java new file mode 100644 index 00000000000..b47a2b82ed7 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjectionSanitized.java @@ -0,0 +1,46 @@ +public class JSchOSInjectionSanitized { + void jschOsExecutionPing(HttpServletRequest request) { + String untrusted = request.getParameter("command"); + + //GOOD - Validate user the input. + if (!com.google.common.net.InetAddresses.isInetAddress(untrusted)) { + System.out.println("Invalid IP address"); + return; + } + + JSch jsch = new JSch(); + Session session = jsch.getSession("user", "host", 22); + session.setPassword("password"); + session.connect(); + + Channel channel = session.openChannel("exec"); + ((ChannelExec) channel).setCommand("ping " + untrusted); + + channel.connect(); + } + + void jschOsExecutionDig(HttpServletRequest request) { + String untrusted = request.getParameter("command"); + + //GOOD - check whether the user input doesn't contain dangerous shell characters. + String[] badChars = new String[] {"^", "~" ," " , "&", "|", ";", "$", ">", "<", "`", "\\", ",", "!", "{", "}", "(", ")", "@", "%", "#", "%0A", "%0a", "\n", "\r\n"}; + + for (String badChar : badChars) { + if (untrusted.contains(badChar)) { + System.out.println("Invalid host"); + return; + } + } + + JSch jsch = new JSch(); + Session session = jsch.getSession("user", "host", 22); + session.setPassword("password"); + session.connect(); + + Channel channel = session.openChannel("exec"); + ((ChannelExec) channel).setCommand("dig " + untrusted); + + channel.connect(); + } +} + diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/FlowUtils.qll b/java/ql/src/experimental/Security/CWE/CWE-094/FlowUtils.qll new file mode 100644 index 00000000000..5371c51aa8f --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/FlowUtils.qll @@ -0,0 +1,14 @@ +import java +import semmle.code.java.dataflow.FlowSources + +/** + * Holds if `fromNode` to `toNode` is a dataflow step that returns data from + * a bean by calling one of its getters. + */ +predicate hasGetterFlow(DataFlow::Node fromNode, DataFlow::Node toNode) { + exists(MethodAccess ma, Method m | ma.getMethod() = m | + m instanceof GetterMethod and + ma.getQualifier() = fromNode.asExpr() and + ma = toNode.asExpr() + ) +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjection.qhelp new file mode 100644 index 00000000000..9bf84f710dc --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjection.qhelp @@ -0,0 +1,81 @@ + + + + +

    +Apache Groovy is a powerful, optionally typed and dynamic language, +with static-typing and static compilation capabilities. + +It integrates smoothly with any Java program, +and immediately delivers to your application powerful features, +including scripting capabilities, Domain-Specific Language authoring, +runtime and compile-time meta-programming and functional programming. + +If a Groovy script is built using attacker-controlled data, +and then evaluated, then it may allow the attacker to achieve RCE. +

    +
    + + +

    +It is generally recommended to avoid using untrusted input in a Groovy evaluation. +If this is not possible, use a sandbox solution. Developers must also take care that Groovy +compile-time metaprogramming can also lead to RCE: it is possible to achieve RCE by compiling +a Groovy script (see the article "Abusing Meta Programming for Unauthenticated RCE!" linked below). + +Groovy's SecureASTCustomizer allows securing source code by controlling what code constructs are permitted. +This is typically done when using Groovy for its scripting or domain specific language (DSL) features. +The fundamental problem is that Groovy is a dynamic language, yet SecureASTCustomizer works by looking at Groovy AST statically. + +This makes it very easy for an attacker to bypass many of the intended checks +(see https://kohsuke.org/2012/04/27/groovy-secureastcustomizer-is-harmful/). +Therefore, besides SecureASTCustomizer, runtime checks are also necessary before calling Groovy methods +(see https://melix.github.io/blog/2015/03/sandboxing.html). + +It is also possible to use a block-list method, excluding unwanted classes from being loaded by the JVM. +This method is not always recommended, because block-lists can be bypassed by unexpected values. + +

    +
    + + +

    +The following example uses untrusted data to evaluate a Groovy script. +

    + + +

    +The following example uses classloader block-list approach to exclude loading dangerous classes. +

    + + +
    + + +
  • + Orange Tsai: + Abusing Meta Programming for Unauthenticated RCE!. +
  • +
  • + Cédric Champeau: + Improved sandboxing of Groovy scripts. +
  • +
  • + Kohsuke Kawaguchi: + Groovy SecureASTCustomizer is harmful. +
  • +
  • + Welk1n: + Groovy Injection payloads. +
  • +
  • + Charles Chan: + Secure Groovy Script Execution in a Sandbox. +
  • +
  • + Eugene: + Scripting and sandboxing in a JVM environment. +
  • +
    + +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjection.ql new file mode 100644 index 00000000000..0622c9cd7e3 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjection.ql @@ -0,0 +1,20 @@ +/** + * @name Groovy Language injection + * @description Evaluation of a user-controlled Groovy script + * may lead to arbitrary code execution. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/groovy-injection + * @tags security + * external/cwe/cwe-094 + */ + +import java +import DataFlow::PathGraph +import GroovyInjectionLib + +from DataFlow::PathNode source, DataFlow::PathNode sink, GroovyInjectionConfig conf +where conf.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Groovy Injection from $@.", source.getNode(), + "this user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjectionBad.java b/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjectionBad.java new file mode 100644 index 00000000000..8afe77d2a39 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjectionBad.java @@ -0,0 +1,27 @@ +public class GroovyInjection { + void injectionViaClassLoader(HttpServletRequest request) { + String script = request.getParameter("script"); + final GroovyClassLoader classLoader = new GroovyClassLoader(); + Class groovy = classLoader.parseClass(script); + GroovyObject groovyObj = (GroovyObject) groovy.newInstance(); + } + + void injectionViaEval(HttpServletRequest request) { + String script = request.getParameter("script"); + Eval.me(script); + } + + void injectionViaGroovyShell(HttpServletRequest request) { + GroovyShell shell = new GroovyShell(); + String script = request.getParameter("script"); + shell.evaluate(script); + } + + void injectionViaGroovyShellGroovyCodeSource(HttpServletRequest request) { + GroovyShell shell = new GroovyShell(); + String script = request.getParameter("script"); + GroovyCodeSource gcs = new GroovyCodeSource(script, "test", "Test"); + shell.evaluate(gcs); + } +} + diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjectionBlocklist.java b/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjectionBlocklist.java new file mode 100644 index 00000000000..61159a3137f --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjectionBlocklist.java @@ -0,0 +1,17 @@ +public class SandboxGroovyClassLoader extends ClassLoader { + public SandboxGroovyClassLoader(ClassLoader parent) { + super(parent); + } + + /* override `loadClass` here to prevent loading sensitive classes, such as `java.lang.Runtime`, `java.lang.ProcessBuilder`, `java.lang.System`, etc. */ + /* Note we must also block `groovy.transform.ASTTest`, `groovy.lang.GrabConfig` and `org.buildobjects.process.ProcBuilder` to prevent compile-time RCE. */ + + static void runWithSandboxGroovyClassLoader() throws Exception { + // GOOD: route all class-loading via sand-boxing classloader. + SandboxGroovyClassLoader classLoader = new GroovyClassLoader(new SandboxGroovyClassLoader()); + + Class scriptClass = classLoader.parseClass(untrusted.getQueryString()); + Object scriptInstance = scriptClass.newInstance(); + Object result = scriptClass.getDeclaredMethod("bar", new Class[]{}).invoke(scriptInstance, new Object[]{}); + } +} \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjectionLib.qll new file mode 100644 index 00000000000..61ddb9b9a1a --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/GroovyInjectionLib.qll @@ -0,0 +1,160 @@ +/** + * Provides classes and predicates for Groovy Code Injection + * taint-tracking configuration. + */ + +import java +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.dataflow.TaintTracking + +/** A data flow sink for Groovy expression injection vulnerabilities. */ +abstract private class GroovyInjectionSink extends DataFlow::ExprNode { } + +/** + * A taint-tracking configuration for unsafe user input + * that is used to evaluate a Groovy expression. + */ +class GroovyInjectionConfig extends TaintTracking::Configuration { + GroovyInjectionConfig() { this = "GroovyInjectionConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof GroovyInjectionSink } + + override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) { + groovyCodeSourceTaintStep(fromNode, toNode) + } +} + +/** The class `groovy.lang.GroovyShell`. */ +private class TypeGroovyShell extends RefType { + TypeGroovyShell() { this.hasQualifiedName("groovy.lang", "GroovyShell") } +} + +/** The class `groovy.lang.GroovyCodeSource`. */ +private class TypeGroovyCodeSource extends RefType { + TypeGroovyCodeSource() { this.hasQualifiedName("groovy.lang", "GroovyCodeSource") } +} + +/** + * Methods in the `GroovyShell` class that evaluate a Groovy expression. + */ +private class GroovyShellMethod extends Method { + GroovyShellMethod() { + this.getDeclaringType() instanceof TypeGroovyShell and + this.getName() in ["evaluate", "parse", "run"] + } +} + +private class GroovyShellMethodAccess extends MethodAccess { + GroovyShellMethodAccess() { this.getMethod() instanceof GroovyShellMethod } +} + +/** + * Holds if `fromNode` to `toNode` is a dataflow step from a tainted string to + * a `GroovyCodeSource` instance, i.e. `new GroovyCodeSource(tainted, ...)`. + */ +private predicate groovyCodeSourceTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) { + exists(ConstructorCall gcscc | + gcscc.getConstructedType() instanceof TypeGroovyCodeSource and + gcscc = toNode.asExpr() and + gcscc.getArgument(0) = fromNode.asExpr() + ) +} + +/** + * A sink for Groovy Injection via the `GroovyShell` class. + * + * ``` + * GroovyShell gs = new GroovyShell(); + * gs.evaluate(sink, ....) + * gs.run(sink, ....) + * gs.parse(sink,...) + * ``` + */ +private class GroovyShellSink extends GroovyInjectionSink { + GroovyShellSink() { + exists(GroovyShellMethodAccess ma, Argument firstArg | + ma.getArgument(0) = firstArg and + firstArg = this.asExpr() and + ( + firstArg.getType() instanceof TypeString or + firstArg.getType() instanceof TypeGroovyCodeSource + ) + ) + } +} + +/** The class `groovy.util.Eval`. */ +private class TypeEval extends RefType { + TypeEval() { this.hasQualifiedName("groovy.util", "Eval") } +} + +/** + * Methods in the `Eval` class that evaluate a Groovy expression. + */ +private class EvalMethod extends Method { + EvalMethod() { + this.getDeclaringType() instanceof TypeEval and + this.getName() in ["me", "x", "xy", "xyz"] + } +} + +private class EvalMethodAccess extends MethodAccess { + EvalMethodAccess() { this.getMethod() instanceof EvalMethod } + + Expr getArgumentExpr() { result = this.getArgument(this.getNumArgument() - 1) } +} + +/** + * A sink for Groovy Injection via the `Eval` class. + * + * ``` + * Eval.me(sink) + * Eval.me("p1", "p2", sink) + * Eval.x("p1", sink) + * Eval.xy("p1", "p2" sink) + * Eval.xyz("p1", "p2", "p3", sink) + * ``` + */ +private class EvalSink extends GroovyInjectionSink { + EvalSink() { exists(EvalMethodAccess ma | ma.getArgumentExpr() = this.asExpr()) } +} + +/** The class `groovy.lang.GroovyClassLoader`. */ +private class TypeGroovyClassLoader extends RefType { + TypeGroovyClassLoader() { this.hasQualifiedName("groovy.lang", "GroovyClassLoader") } +} + +/** + * A method in the `GroovyClassLoader` class that evaluates a Groovy expression. + */ +private class GroovyClassLoaderParseClassMethod extends Method { + GroovyClassLoaderParseClassMethod() { + this.getDeclaringType() instanceof TypeGroovyClassLoader and + this.hasName("parseClass") + } +} + +private class GroovyClassLoaderParseClassMethodAccess extends MethodAccess { + GroovyClassLoaderParseClassMethodAccess() { + this.getMethod() instanceof GroovyClassLoaderParseClassMethod + } +} + +/** + * A sink for Groovy Injection via the `GroovyClassLoader` class. + * + * ``` + * GroovyClassLoader classLoader = new GroovyClassLoader(); + * Class groovy = classLoader.parseClass(script); + * ``` + * + * Groovy supports compile-time metaprogramming, so just calling the `parseClass` + * method is enough to achieve RCE. + */ +private class GroovyClassLoadParseClassSink extends GroovyInjectionSink { + GroovyClassLoadParseClassSink() { + exists(GroovyClassLoaderParseClassMethodAccess ma | ma.getArgument(0) = this.asExpr()) + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoading.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoading.qhelp new file mode 100644 index 00000000000..216bdeaebf3 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoading.qhelp @@ -0,0 +1,42 @@ + + + +

    +It is dangerous to load Dex libraries from shared world-writable storage spaces. A malicious actor can replace a dex file with a maliciously crafted file +which when loaded by the app can lead to code execution. +

    +
    + + +

    + Loading a file from private storage instead of a world-writable one can prevent this issue, + because the attacker cannot access files stored there. +

    +
    + + +

    + The following example loads a Dex file from a shared world-writable location. in this case, + since the `/sdcard` directory is on external storage, anyone can read/write to the location. + bypassing all Android security policies. Hence, this is insecure. +

    + + +

    + The next example loads a Dex file stored inside the app's private storage. + This is not exploitable as nobody else except the app can access the data stored there. +

    + +
    + + +
  • + Android Documentation: + Data and file storage overview. +
  • +
  • + Android Documentation: + DexClassLoader. +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoading.ql b/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoading.ql new file mode 100644 index 00000000000..bae3ed63d70 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoading.ql @@ -0,0 +1,20 @@ +/** + * @name Insecure loading of an Android Dex File + * @description Loading a DEX library located in a world-writable location such as + * an SD card can lead to arbitrary code execution vulnerabilities. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/android-insecure-dex-loading + * @tags security + * external/cwe/cwe-094 + */ + +import java +import InsecureDexLoading +import DataFlow::PathGraph + +from DataFlow::PathNode source, DataFlow::PathNode sink, InsecureDexConfiguration conf +where conf.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Potential arbitrary code execution due to $@.", + source.getNode(), "a value loaded from a world-writable source." diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoading.qll b/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoading.qll new file mode 100644 index 00000000000..0ee0954216e --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoading.qll @@ -0,0 +1,100 @@ +import java +import semmle.code.java.dataflow.FlowSources + +/** + * A taint-tracking configuration detecting unsafe use of a + * `DexClassLoader` by an Android app. + */ +class InsecureDexConfiguration extends TaintTracking::Configuration { + InsecureDexConfiguration() { this = "Insecure Dex File Load" } + + override predicate isSource(DataFlow::Node source) { source instanceof InsecureDexSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof InsecureDexSink } + + override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { + flowStep(pred, succ) + } +} + +/** A data flow source for insecure Dex class loading vulnerabilities. */ +abstract class InsecureDexSource extends DataFlow::Node { } + +/** A data flow sink for insecure Dex class loading vulnerabilities. */ +abstract class InsecureDexSink extends DataFlow::Node { } + +private predicate flowStep(DataFlow::Node pred, DataFlow::Node succ) { + // propagate from a `java.io.File` via the `File.getAbsolutePath` call. + exists(MethodAccess m | + m.getMethod().getDeclaringType() instanceof TypeFile and + m.getMethod().hasName("getAbsolutePath") and + m.getQualifier() = pred.asExpr() and + m = succ.asExpr() + ) + or + // propagate from a `java.io.File` via the `File.toString` call. + exists(MethodAccess m | + m.getMethod().getDeclaringType() instanceof TypeFile and + m.getMethod().hasName("toString") and + m.getQualifier() = pred.asExpr() and + m = succ.asExpr() + ) + or + // propagate to newly created `File` if the parent directory of the new `File` is tainted + exists(ConstructorCall cc | + cc.getConstructedType() instanceof TypeFile and + cc.getArgument(0) = pred.asExpr() and + cc = succ.asExpr() + ) +} + +/** + * An argument to a `DexClassLoader` call taken as a sink for + * insecure Dex class loading vulnerabilities. + */ +private class DexClassLoader extends InsecureDexSink { + DexClassLoader() { + exists(ConstructorCall cc | + cc.getConstructedType().hasQualifiedName("dalvik.system", "DexClassLoader") + | + this.asExpr() = cc.getArgument(0) + ) + } +} + +/** + * A `File` instance which reads from an SD card + * taken as a source for insecure Dex class loading vulnerabilities. + */ +private class ExternalFile extends InsecureDexSource { + ExternalFile() { + exists(ConstructorCall cc, Argument a | + cc.getConstructedType() instanceof TypeFile and + a = cc.getArgument(0) and + a.(CompileTimeConstantExpr).getStringValue().matches("%sdcard%") + | + this.asExpr() = a + ) + } +} + +/** + * A directory or file which may be stored in an world writable directory + * taken as a source for insecure Dex class loading vulnerabilities. + */ +private class ExternalStorageDirSource extends InsecureDexSource { + ExternalStorageDirSource() { + exists(Method m | + m.getDeclaringType().hasQualifiedName("android.os", "Environment") and + m.hasName("getExternalStorageDirectory") + or + m.getDeclaringType().hasQualifiedName("android.content", "Context") and + m.hasName([ + "getExternalFilesDir", "getExternalFilesDirs", "getExternalMediaDirs", + "getExternalCacheDir", "getExternalCacheDirs" + ]) + | + this.asExpr() = m.getAReference() + ) + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoadingBad.java b/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoadingBad.java new file mode 100644 index 00000000000..869b6bc571c --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoadingBad.java @@ -0,0 +1,32 @@ + +import android.app.Application; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.os.Bundle; + +import dalvik.system.DexClassLoader; +import dalvik.system.DexFile; + +public class InsecureDexLoading extends Application { + @Override + public void onCreate() { + super.onCreate(); + updateChecker(); + } + + private void updateChecker() { + try { + File file = new File("/sdcard/updater.apk"); + if (file.exists() && file.isFile() && file.length() <= 1000) { + DexClassLoader cl = new DexClassLoader(file.getAbsolutePath(), getCacheDir().getAbsolutePath(), null, + getClassLoader()); + int version = (int) cl.loadClass("my.package.class").getDeclaredMethod("myMethod").invoke(null); + if (Build.VERSION.SDK_INT < version) { + Toast.makeText(this, "Loaded Dex!", Toast.LENGTH_LONG).show(); + } + } + } catch (Exception e) { + // ignore + } + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoadingGood.java b/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoadingGood.java new file mode 100644 index 00000000000..e45e3938f7b --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/InsecureDexLoadingGood.java @@ -0,0 +1,23 @@ +public class SecureDexLoading extends Application { + @Override + public void onCreate() { + super.onCreate(); + updateChecker(); + } + + private void updateChecker() { + try { + File file = new File(getCacheDir() + "/updater.apk"); + if (file.exists() && file.isFile() && file.length() <= 1000) { + DexClassLoader cl = new DexClassLoader(file.getAbsolutePath(), getCacheDir().getAbsolutePath(), null, + getClassLoader()); + int version = (int) cl.loadClass("my.package.class").getDeclaredMethod("myMethod").invoke(null); + if (Build.VERSION.SDK_INT < version) { + Toast.makeText(this, "Securely loaded Dex!", Toast.LENGTH_LONG).show(); + } + } + } catch (Exception e) { + // ignore + } + } +} \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.qhelp new file mode 100644 index 00000000000..a8d3cd0fe70 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.qhelp @@ -0,0 +1,61 @@ + + + + +

    +Jakarta Expression Language (EL) is an expression language for Java applications. +There is a single language specification and multiple implementations +such as Glassfish, Juel, Apache Commons EL, etc. +The language allows invocation of methods available in the JVM. +If an expression is built using attacker-controlled data, +and then evaluated, it may allow the attacker to run arbitrary code. +

    +
    + + +

    +It is generally recommended to avoid using untrusted data in an EL expression. +Before using untrusted data to build an EL expression, the data should be validated +to ensure it is not evaluated as expression language. If the EL implementation offers +configuring a sandbox for EL expressions, they should be run in a restrictive sandbox +that allows accessing only explicitly allowed classes. If the EL implementation +does not support sandboxing, consider using other expression language implementations +with sandboxing capabilities such as Apache Commons JEXL or the Spring Expression Language. +

    +
    + + +

    +The following example shows how untrusted data is used to build and run an expression +using the JUEL interpreter: +

    + + +

    +JUEL does not support running expressions in a sandbox. To prevent running arbitrary code, +incoming data has to be checked before including it in an expression. The next example +uses a Regex pattern to check whether a user tries to run an allowed expression or not: +

    + + +
    + + +
  • + Eclipse Foundation: + Jakarta Expression Language. +
  • +
  • + Jakarta EE documentation: + Jakarta Expression Language API +
  • +
  • + OWASP: + Expression Language Injection. +
  • +
  • + JUEL: + Home page +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.ql new file mode 100644 index 00000000000..8190ec3d61f --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.ql @@ -0,0 +1,20 @@ +/** + * @name Jakarta Expression Language injection + * @description Evaluation of a user-controlled expression + * may lead to arbitrary code execution. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/javaee-expression-injection + * @tags security + * external/cwe/cwe-094 + */ + +import java +import JakartaExpressionInjectionLib +import DataFlow::PathGraph + +from DataFlow::PathNode source, DataFlow::PathNode sink, JakartaExpressionInjectionConfig conf +where conf.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Jakarta Expression Language injection from $@.", + source.getNode(), "this user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjectionLib.qll new file mode 100644 index 00000000000..43090974364 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjectionLib.qll @@ -0,0 +1,108 @@ +import java +import FlowUtils +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.dataflow.TaintTracking + +/** + * A taint-tracking configuration for unsafe user input + * that is used to construct and evaluate an expression. + */ +class JakartaExpressionInjectionConfig extends TaintTracking::Configuration { + JakartaExpressionInjectionConfig() { this = "JakartaExpressionInjectionConfig" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof ExpressionEvaluationSink } + + override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) { + any(TaintPropagatingCall c).taintFlow(fromNode, toNode) or + hasGetterFlow(fromNode, toNode) + } +} + +/** + * A sink for Expresssion Language injection vulnerabilities, + * i.e. method calls that run evaluation of an expression. + */ +private class ExpressionEvaluationSink extends DataFlow::ExprNode { + ExpressionEvaluationSink() { + exists(MethodAccess ma, Method m, Expr taintFrom | + ma.getMethod() = m and taintFrom = this.asExpr() + | + m.getDeclaringType() instanceof ValueExpression and + m.hasName(["getValue", "setValue"]) and + ma.getQualifier() = taintFrom + or + m.getDeclaringType() instanceof MethodExpression and + m.hasName("invoke") and + ma.getQualifier() = taintFrom + or + m.getDeclaringType() instanceof LambdaExpression and + m.hasName("invoke") and + ma.getQualifier() = taintFrom + or + m.getDeclaringType() instanceof ELProcessor and + m.hasName(["eval", "getValue", "setValue"]) and + ma.getArgument(0) = taintFrom + or + m.getDeclaringType() instanceof ELProcessor and + m.hasName("setVariable") and + ma.getArgument(1) = taintFrom + ) + } +} + +/** + * Defines method calls that propagate tainted expressions. + */ +private class TaintPropagatingCall extends Call { + Expr taintFromExpr; + + TaintPropagatingCall() { + taintFromExpr = this.getArgument(1) and + ( + exists(Method m | this.(MethodAccess).getMethod() = m | + m.getDeclaringType() instanceof ExpressionFactory and + m.hasName(["createValueExpression", "createMethodExpression"]) and + taintFromExpr.getType() instanceof TypeString + ) + or + exists(Constructor c | this.(ConstructorCall).getConstructor() = c | + c.getDeclaringType() instanceof LambdaExpression and + taintFromExpr.getType() instanceof ValueExpression + ) + ) + } + + /** + * Holds if `fromNode` to `toNode` is a dataflow step that propagates + * tainted data. + */ + predicate taintFlow(DataFlow::Node fromNode, DataFlow::Node toNode) { + fromNode.asExpr() = taintFromExpr and toNode.asExpr() = this + } +} + +private class JakartaType extends RefType { + JakartaType() { getPackage().hasName(["javax.el", "jakarta.el"]) } +} + +private class ELProcessor extends JakartaType { + ELProcessor() { hasName("ELProcessor") } +} + +private class ExpressionFactory extends JakartaType { + ExpressionFactory() { hasName("ExpressionFactory") } +} + +private class ValueExpression extends JakartaType { + ValueExpression() { hasName("ValueExpression") } +} + +private class MethodExpression extends JakartaType { + MethodExpression() { hasName("MethodExpression") } +} + +private class LambdaExpression extends JakartaType { + LambdaExpression() { hasName("LambdaExpression") } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjection.ql deleted file mode 100644 index 2a23dd7368d..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjection.ql +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @name Expression language injection (JEXL) - * @description Evaluation of a user-controlled JEXL expression - * may lead to arbitrary code execution. - * @kind path-problem - * @problem.severity error - * @precision high - * @id java/jexl-expression-injection - * @tags security - * external/cwe/cwe-094 - */ - -import java -import JexlInjectionLib -import DataFlow::PathGraph - -from DataFlow::PathNode source, DataFlow::PathNode sink, JexlInjectionConfig conf -where conf.hasFlowPath(source, sink) -select sink.getNode(), source, sink, "JEXL injection from $@.", source.getNode(), "this user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjectionLib.qll deleted file mode 100644 index 561d7e46ae9..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjectionLib.qll +++ /dev/null @@ -1,288 +0,0 @@ -import java -import semmle.code.java.dataflow.FlowSources -import semmle.code.java.dataflow.TaintTracking - -/** - * A taint-tracking configuration for unsafe user input - * that is used to construct and evaluate a JEXL expression. - * It supports both JEXL 2 and 3. - */ -class JexlInjectionConfig extends TaintTracking::Configuration { - JexlInjectionConfig() { this = "JexlInjectionConfig" } - - override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } - - override predicate isSink(DataFlow::Node sink) { sink instanceof JexlEvaluationSink } - - override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) { - any(TaintPropagatingJexlMethodCall c).taintFlow(fromNode, toNode) or - returnsDataFromBean(fromNode, toNode) - } -} - -/** - * A sink for Expresssion Language injection vulnerabilities via Jexl, - * i.e. method calls that run evaluation of a JEXL expression. - * - * Creating a `Callable` from a tainted JEXL expression or script is considered as a sink - * although the tainted expression is not executed at this point. - * Here we assume that it will get executed at some point, - * maybe stored in an object field and then reached by a different flow. - */ -private class JexlEvaluationSink extends DataFlow::ExprNode { - JexlEvaluationSink() { - exists(MethodAccess ma, Method m, Expr taintFrom | - ma.getMethod() = m and taintFrom = this.asExpr() - | - m instanceof DirectJexlEvaluationMethod and ma.getQualifier() = taintFrom - or - m instanceof CreateJexlCallableMethod and ma.getQualifier() = taintFrom - or - m instanceof JexlEngineGetSetPropertyMethod and - taintFrom.getType() instanceof TypeString and - ma.getAnArgument() = taintFrom - ) - } -} - -/** - * Defines method calls that propagate tainted data via one of the methods - * from JEXL library. - */ -private class TaintPropagatingJexlMethodCall extends MethodAccess { - Expr taintFromExpr; - - TaintPropagatingJexlMethodCall() { - exists(Method m, RefType taintType | - this.getMethod() = m and - taintType = taintFromExpr.getType() - | - isUnsafeEngine(this.getQualifier()) and - ( - m instanceof CreateJexlScriptMethod and - taintFromExpr = this.getArgument(0) and - taintType instanceof TypeString - or - m instanceof CreateJexlExpressionMethod and - taintFromExpr = this.getAnArgument() and - taintType instanceof TypeString - or - m instanceof CreateJexlTemplateMethod and - (taintType instanceof TypeString or taintType instanceof Reader) and - taintFromExpr = this.getArgument([0, 1]) - ) - ) - } - - /** - * Holds if `fromNode` to `toNode` is a dataflow step that propagates - * tainted data. - */ - predicate taintFlow(DataFlow::Node fromNode, DataFlow::Node toNode) { - fromNode.asExpr() = taintFromExpr and toNode.asExpr() = this - } -} - -/** - * Holds if `expr` is a JEXL engine that is not configured with a sandbox. - */ -private predicate isUnsafeEngine(Expr expr) { - not exists(SandboxedJexlFlowConfig config | config.hasFlowTo(DataFlow::exprNode(expr))) -} - -/** - * A configuration for a tracking sandboxed JEXL engines. - */ -private class SandboxedJexlFlowConfig extends DataFlow2::Configuration { - SandboxedJexlFlowConfig() { this = "JexlInjection::SandboxedJexlFlowConfig" } - - override predicate isSource(DataFlow::Node node) { node instanceof SandboxedJexlSource } - - override predicate isSink(DataFlow::Node node) { - exists(MethodAccess ma, Method m | ma.getMethod() = m | - ( - m instanceof CreateJexlScriptMethod or - m instanceof CreateJexlExpressionMethod or - m instanceof CreateJexlTemplateMethod - ) and - ma.getQualifier() = node.asExpr() - ) - } - - override predicate isAdditionalFlowStep(DataFlow::Node fromNode, DataFlow::Node toNode) { - createsJexlEngine(fromNode, toNode) - } -} - -/** - * Defines a data flow source for JEXL engines configured with a sandbox. - */ -private class SandboxedJexlSource extends DataFlow::ExprNode { - SandboxedJexlSource() { - exists(MethodAccess ma, Method m | m = ma.getMethod() | - m.getDeclaringType() instanceof JexlBuilder and - m.hasName(["uberspect", "sandbox"]) and - m.getReturnType() instanceof JexlBuilder and - this.asExpr() = [ma, ma.getQualifier()] - ) - or - exists(ConstructorCall cc | - cc.getConstructedType() instanceof JexlEngine and - cc.getArgument(0).getType() instanceof JexlUberspect and - cc = this.asExpr() - ) - } -} - -/** - * Holds if `fromNode` to `toNode` is a dataflow step that creates one of the JEXL engines. - */ -private predicate createsJexlEngine(DataFlow::Node fromNode, DataFlow::Node toNode) { - exists(MethodAccess ma, Method m | m = ma.getMethod() | - (m.getDeclaringType() instanceof JexlBuilder or m.getDeclaringType() instanceof JexlEngine) and - m.hasName(["create", "createJxltEngine"]) and - ma.getQualifier() = fromNode.asExpr() and - ma = toNode.asExpr() - ) - or - exists(ConstructorCall cc | - cc.getConstructedType() instanceof UnifiedJexl and - cc.getArgument(0) = fromNode.asExpr() and - cc = toNode.asExpr() - ) -} - -/** - * Holds if `fromNode` to `toNode` is a dataflow step that returns data from - * a bean by calling one of its getters. - */ -private predicate returnsDataFromBean(DataFlow::Node fromNode, DataFlow::Node toNode) { - exists(MethodAccess ma, Method m | ma.getMethod() = m | - m instanceof GetterMethod and - ma.getQualifier() = fromNode.asExpr() and - ma = toNode.asExpr() - ) -} - -/** - * A methods in the `JexlEngine` class that gets or sets a property with a JEXL expression. - */ -private class JexlEngineGetSetPropertyMethod extends Method { - JexlEngineGetSetPropertyMethod() { - getDeclaringType() instanceof JexlEngine and - hasName(["getProperty", "setProperty"]) - } -} - -/** - * A method that triggers direct evaluation of JEXL expressions. - */ -private class DirectJexlEvaluationMethod extends Method { - DirectJexlEvaluationMethod() { - getDeclaringType() instanceof JexlExpression and hasName("evaluate") - or - getDeclaringType() instanceof JexlScript and hasName("execute") - or - getDeclaringType() instanceof JxltEngineExpression and hasName(["evaluate", "prepare"]) - or - getDeclaringType() instanceof JxltEngineTemplate and hasName("evaluate") - or - getDeclaringType() instanceof UnifiedJexlExpression and hasName(["evaluate", "prepare"]) - or - getDeclaringType() instanceof UnifiedJexlTemplate and hasName("evaluate") - } -} - -/** - * A method that creates a JEXL script. - */ -private class CreateJexlScriptMethod extends Method { - CreateJexlScriptMethod() { getDeclaringType() instanceof JexlEngine and hasName("createScript") } -} - -/** - * A method that creates a `Callable` for a JEXL expression or script. - */ -private class CreateJexlCallableMethod extends Method { - CreateJexlCallableMethod() { - (getDeclaringType() instanceof JexlExpression or getDeclaringType() instanceof JexlScript) and - hasName("callable") - } -} - -/** - * A method that creates a JEXL template. - */ -private class CreateJexlTemplateMethod extends Method { - CreateJexlTemplateMethod() { - (getDeclaringType() instanceof JxltEngine or getDeclaringType() instanceof UnifiedJexl) and - hasName("createTemplate") - } -} - -/** - * A method that creates a JEXL expression. - */ -private class CreateJexlExpressionMethod extends Method { - CreateJexlExpressionMethod() { - (getDeclaringType() instanceof JexlEngine or getDeclaringType() instanceof JxltEngine) and - hasName("createExpression") - or - getDeclaringType() instanceof UnifiedJexl and hasName("parse") - } -} - -private class JexlRefType extends RefType { - JexlRefType() { getPackage().hasName(["org.apache.commons.jexl2", "org.apache.commons.jexl3"]) } -} - -private class JexlExpression extends JexlRefType { - JexlExpression() { hasName(["Expression", "JexlExpression"]) } -} - -private class JexlScript extends JexlRefType { - JexlScript() { hasName(["Script", "JexlScript"]) } -} - -private class JexlBuilder extends JexlRefType { - JexlBuilder() { hasName("JexlBuilder") } -} - -private class JexlEngine extends JexlRefType { - JexlEngine() { hasName("JexlEngine") } -} - -private class JxltEngine extends JexlRefType { - JxltEngine() { hasName("JxltEngine") } -} - -private class UnifiedJexl extends JexlRefType { - UnifiedJexl() { hasName("UnifiedJEXL") } -} - -private class JexlUberspect extends Interface { - JexlUberspect() { - hasQualifiedName("org.apache.commons.jexl2.introspection", "Uberspect") or - hasQualifiedName("org.apache.commons.jexl3.introspection", "JexlUberspect") - } -} - -private class JxltEngineExpression extends NestedType { - JxltEngineExpression() { getEnclosingType() instanceof JxltEngine and hasName("Expression") } -} - -private class JxltEngineTemplate extends NestedType { - JxltEngineTemplate() { getEnclosingType() instanceof JxltEngine and hasName("Template") } -} - -private class UnifiedJexlExpression extends NestedType { - UnifiedJexlExpression() { getEnclosingType() instanceof UnifiedJexl and hasName("Expression") } -} - -private class UnifiedJexlTemplate extends NestedType { - UnifiedJexlTemplate() { getEnclosingType() instanceof UnifiedJexl and hasName("Template") } -} - -private class Reader extends RefType { - Reader() { hasQualifiedName("java.io", "Reader") } -} diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.java b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.java new file mode 100644 index 00000000000..5c1796e1f60 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.java @@ -0,0 +1,49 @@ +import org.python.util.PythonInterpreter; + +public class JythonInjection extends HttpServlet { + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + PythonInterpreter interpreter = null; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + interpreter = new PythonInterpreter(); + interpreter.setOut(out); + interpreter.setErr(out); + + // BAD: allow execution of arbitrary Python code + interpreter.exec(code); + out.flush(); + + response.getWriter().print(out.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + out.close(); + } + } + + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + PythonInterpreter interpreter = null; + + try { + interpreter = new PythonInterpreter(); + // BAD: allow execution of arbitrary Python code + PyObject py = interpreter.eval(code); + + response.getWriter().print(py.toString()); + } catch(PyException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + if (interpreter != null) { + interpreter.close(); + } + } + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.qhelp new file mode 100644 index 00000000000..8916296f93b --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.qhelp @@ -0,0 +1,34 @@ + + + + +

    Python has been the most widely used programming language in recent years, and Jython + (formerly known as JPython) is a popular Java implementation of Python. It allows + embedded Python scripting inside Java applications and provides an interactive interpreter + that can be used to interact with Java packages or with running Java applications. If an + expression is built using attacker-controlled data and then evaluated, it may allow the + attacker to run arbitrary code.

    +
    + + +

    In general, including user input in Jython expression should be avoided. If user input + must be included in an expression, it should be then evaluated in a safe context that + doesn't allow arbitrary code invocation.

    +
    + + +

    The following code could execute arbitrary code in Jython Interpreter

    + +
    + + +
  • + Jython Organization: Jython and Java Integration +
  • +
  • + PortSwigger: Python code injection +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql new file mode 100644 index 00000000000..c6a7f583b14 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql @@ -0,0 +1,114 @@ +/** + * @name Injection in Jython + * @description Evaluation of a user-controlled malicious expression in Java Python + * interpreter may lead to remote code execution. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/jython-injection + * @tags security + * external/cwe/cwe-094 + * external/cwe/cwe-095 + */ + +import java +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.frameworks.spring.SpringController +import DataFlow::PathGraph + +/** The class `org.python.util.PythonInterpreter`. */ +class PythonInterpreter extends RefType { + PythonInterpreter() { this.hasQualifiedName("org.python.util", "PythonInterpreter") } +} + +/** A method that evaluates, compiles or executes a Jython expression. */ +class InterpretExprMethod extends Method { + InterpretExprMethod() { + this.getDeclaringType().getAnAncestor*() instanceof PythonInterpreter and + getName().matches(["exec%", "run%", "eval", "compile"]) + } +} + +/** The class `org.python.core.BytecodeLoader`. */ +class BytecodeLoader extends RefType { + BytecodeLoader() { this.hasQualifiedName("org.python.core", "BytecodeLoader") } +} + +/** Holds if a Jython expression if evaluated, compiled or executed. */ +predicate runsCode(MethodAccess ma, Expr sink) { + exists(Method m | m = ma.getMethod() | + m instanceof InterpretExprMethod and + sink = ma.getArgument(0) + ) +} + +/** A method that loads Java class data. */ +class LoadClassMethod extends Method { + LoadClassMethod() { + this.getDeclaringType().getAnAncestor*() instanceof BytecodeLoader and + hasName(["makeClass", "makeCode"]) + } +} + +/** + * Holds if `ma` is a call to a class-loading method, and `sink` is the byte array + * representing the class to be loaded. + */ +predicate loadsClass(MethodAccess ma, Expr sink) { + exists(Method m, int i | m = ma.getMethod() | + m instanceof LoadClassMethod and + m.getParameter(i).getType() instanceof Array and // makeClass(java.lang.String name, byte[] data, ...) + sink = ma.getArgument(i) + ) +} + +/** The class `org.python.core.Py`. */ +class Py extends RefType { + Py() { this.hasQualifiedName("org.python.core", "Py") } +} + +/** A method declared on class `Py` or one of its descendants that compiles Python code. */ +class PyCompileMethod extends Method { + PyCompileMethod() { + this.getDeclaringType().getAnAncestor*() instanceof Py and + getName().matches("compile%") + } +} + +/** Holds if source code is compiled with `PyCompileMethod`. */ +predicate compile(MethodAccess ma, Expr sink) { + exists(Method m | m = ma.getMethod() | + m instanceof PyCompileMethod and + sink = ma.getArgument(0) + ) +} + +/** An expression loaded by Jython. */ +class CodeInjectionSink extends DataFlow::ExprNode { + MethodAccess methodAccess; + + CodeInjectionSink() { + runsCode(methodAccess, this.getExpr()) or + loadsClass(methodAccess, this.getExpr()) or + compile(methodAccess, this.getExpr()) + } + + MethodAccess getMethodAccess() { result = methodAccess } +} + +/** + * A taint configuration for tracking flow from `RemoteFlowSource` to a Jython method call + * `CodeInjectionSink` that executes injected code. + */ +class CodeInjectionConfiguration extends TaintTracking::Configuration { + CodeInjectionConfiguration() { this = "CodeInjectionConfiguration" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof CodeInjectionSink } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, CodeInjectionConfiguration conf +where conf.hasFlowPath(source, sink) +select sink.getNode().(CodeInjectionSink).getMethodAccess(), source, sink, "Jython evaluate $@.", + source.getNode(), "user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.java b/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.java new file mode 100644 index 00000000000..15adfbe4524 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/RhinoInjection.java @@ -0,0 +1,40 @@ +import org.mozilla.javascript.ClassShutter; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Scriptable; + +public class RhinoInjection extends HttpServlet { + + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + String code = request.getParameter("code"); + Context ctx = Context.enter(); + try { + { + // BAD: allow arbitrary Java and JavaScript code to be executed + Scriptable scope = ctx.initStandardObjects(); + } + + { + // GOOD: enable the safe mode + Scriptable scope = ctx.initSafeStandardObjects(); + } + + { + // GOOD: enforce a constraint on allowed classes + Scriptable scope = ctx.initStandardObjects(); + ctx.setClassShutter(new ClassShutter() { + public boolean visibleToScripts(String className) { + return className.startsWith("com.example."); + } + }); + } + + Object result = ctx.evaluateString(scope, code, "", 1, null); + response.getWriter().print(Context.toString(result)); + } catch(RhinoException ex) { + response.getWriter().println(ex.getMessage()); + } finally { + Context.exit(); + } + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/SaferExpressionEvaluationWithJuel.java b/java/ql/src/experimental/Security/CWE/CWE-094/SaferExpressionEvaluationWithJuel.java new file mode 100644 index 00000000000..3dfaaead68a --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/SaferExpressionEvaluationWithJuel.java @@ -0,0 +1,10 @@ +String input = getRemoteUserInput(); +String pattern = "(inside|outside)\\.(temperature|humidity)"; +if (!input.matches(pattern)) { + throw new IllegalArgumentException("Unexpected expression"); +} +String expression = "${" + input + "}"; +ExpressionFactory factory = new de.odysseus.el.ExpressionFactoryImpl(); +ValueExpression e = factory.createValueExpression(context, expression, Object.class); +SimpleContext context = getContext(); +Object result = e.getValue(context); diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptEngine.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptEngine.qhelp deleted file mode 100644 index 74159c562c5..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptEngine.qhelp +++ /dev/null @@ -1,26 +0,0 @@ - - - - -

    The ScriptEngine API has been available since the release of Java 6. -It allows applications to interact with scripts written in languages such as JavaScript.

    -
    - - -

    Use "Cloudbees Rhino Sandbox" or sandboxing with SecurityManager or use graalvm instead.

    -
    - - -

    The following code could execute random JavaScript code

    - - -
    - - -
  • -CERT coding standard: ScriptEngine code injection -
  • -
    -
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptEngine.ql b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptEngine.ql deleted file mode 100644 index 5e52a61b5c3..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptEngine.ql +++ /dev/null @@ -1,51 +0,0 @@ -/** - * @name ScriptEngine evaluation - * @description Malicious Javascript code could cause arbitrary command execution at the OS level - * @kind path-problem - * @problem.severity error - * @precision high - * @id java/unsafe-eval - * @tags security - * external/cwe/cwe-094 - */ - -import java -import semmle.code.java.dataflow.FlowSources -import DataFlow::PathGraph - -class ScriptEngineMethod extends Method { - ScriptEngineMethod() { - this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngine") and - this.hasName("eval") - } -} - -predicate scriptEngine(MethodAccess ma, Expr sink) { - exists(Method m | m = ma.getMethod() | - m instanceof ScriptEngineMethod and - sink = ma.getArgument(0) - ) -} - -class ScriptEngineSink extends DataFlow::ExprNode { - ScriptEngineSink() { scriptEngine(_, this.getExpr()) } - - MethodAccess getMethodAccess() { scriptEngine(result, this.getExpr()) } -} - -class ScriptEngineConfiguration extends TaintTracking::Configuration { - ScriptEngineConfiguration() { this = "ScriptEngineConfiguration" } - - override predicate isSource(DataFlow::Node source) { - source instanceof RemoteFlowSource - or - source instanceof LocalUserInput - } - - override predicate isSink(DataFlow::Node sink) { sink instanceof ScriptEngineSink } -} - -from DataFlow::PathNode source, DataFlow::PathNode sink, ScriptEngineConfiguration conf -where conf.hasFlowPath(source, sink) -select sink.getNode().(ScriptEngineSink).getMethodAccess(), source, sink, "ScriptEngine eval $@.", - source.getNode(), "user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.qhelp new file mode 100644 index 00000000000..2683cf9ad29 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.qhelp @@ -0,0 +1,52 @@ + + + + +

    The Java Scripting API has been available since the release of Java 6. It allows + applications to interact with scripts written in languages such as JavaScript. It serves + as an embedded scripting engine inside Java applications which allows Java-to-JavaScript + interoperability and provides a seamless integration between the two languages. If an + expression is built using attacker-controlled data, and then evaluated in a powerful + context, it may allow the attacker to run arbitrary code.

    +
    + + +

    In general, including user input in a Java Script Engine expression should be avoided. + If user input must be included in the expression, it should be then evaluated in a safe + context that doesn't allow arbitrary code invocation. Use "Cloudbees Rhino Sandbox" or + sandboxing with SecurityManager, which will be deprecated in a future release, or use + GraalVM instead.

    +
    + + +

    The following code could execute user-supplied JavaScript code in ScriptEngine

    + + + +

    The following example shows two ways of using Rhino expression. In the 'BAD' case, + an unsafe context is initialized with initStandardObjects that allows arbitrary + Java code to be executed. In the 'GOOD' case, a safe context is initialized with + initSafeStandardObjects or setClassShutter.

    + +
    + + +
  • +CERT coding standard: ScriptEngine code injection +
  • +
  • +GraalVM: Secure by Default +
  • +
  • + Mozilla Rhino: Rhino: JavaScript in Java +
  • +
  • + Rhino Sandbox: A sandbox to execute JavaScript code with Rhino in Java +
  • +
  • + GuardRails: Code Injection +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql new file mode 100644 index 00000000000..fb8bf867501 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql @@ -0,0 +1,146 @@ +/** + * @name Injection in Java Script Engine + * @description Evaluation of user-controlled data using the Java Script Engine may + * lead to remote code execution. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/unsafe-eval + * @tags security + * external/cwe/cwe-094 + */ + +import java +import semmle.code.java.dataflow.FlowSources +import DataFlow::PathGraph + +/** A method of ScriptEngine that allows code injection. */ +class ScriptEngineMethod extends Method { + ScriptEngineMethod() { + this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngine") and + this.hasName("eval") + or + this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "Compilable") and + this.hasName("compile") + or + this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngineFactory") and + this.hasName(["getProgram", "getMethodCallSyntax"]) + } +} + +/** The context class `org.mozilla.javascript.Context` of Rhino Java Script Engine. */ +class RhinoContext extends RefType { + RhinoContext() { this.hasQualifiedName("org.mozilla.javascript", "Context") } +} + +/** A method that evaluates a Rhino expression with `org.mozilla.javascript.Context`. */ +class RhinoEvaluateExpressionMethod extends Method { + RhinoEvaluateExpressionMethod() { + this.getDeclaringType().getAnAncestor*() instanceof RhinoContext and + this.hasName([ + "evaluateString", "evaluateReader", "compileFunction", "compileReader", "compileString" + ]) + } +} + +/** + * A method that compiles a Rhino expression with + * `org.mozilla.javascript.optimizer.ClassCompiler`. + */ +class RhinoCompileClassMethod extends Method { + RhinoCompileClassMethod() { + this.getDeclaringType() + .getASupertype*() + .hasQualifiedName("org.mozilla.javascript.optimizer", "ClassCompiler") and + this.hasName("compileToClassFiles") + } +} + +/** + * A method that defines a Java class from a Rhino expression with + * `org.mozilla.javascript.GeneratedClassLoader`. + */ +class RhinoDefineClassMethod extends Method { + RhinoDefineClassMethod() { + this.getDeclaringType() + .getASupertype*() + .hasQualifiedName("org.mozilla.javascript", "GeneratedClassLoader") and + this.hasName("defineClass") + } +} + +/** + * Holds if `ma` is a call to a `ScriptEngineMethod` and `sink` is an argument that + * will be executed. + */ +predicate isScriptArgument(MethodAccess ma, Expr sink) { + exists(ScriptEngineMethod m | + m = ma.getMethod() and + if m.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngineFactory") + then sink = ma.getArgument(_) // all arguments allow script injection + else sink = ma.getArgument(0) + ) +} + +/** + * Holds if a Rhino expression evaluation method is vulnerable to code injection. + */ +predicate evaluatesRhinoExpression(MethodAccess ma, Expr sink) { + exists(RhinoEvaluateExpressionMethod m | m = ma.getMethod() | + ( + if ma.getMethod().getName() = "compileReader" + then sink = ma.getArgument(0) // The first argument is the input reader + else sink = ma.getArgument(1) // The second argument is the JavaScript or Java input + ) and + not exists(MethodAccess ca | + ca.getMethod().hasName(["initSafeStandardObjects", "setClassShutter"]) and // safe mode or `ClassShutter` constraint is enforced + ma.getQualifier() = ca.getQualifier().(VarAccess).getVariable().getAnAccess() + ) + ) +} + +/** + * Holds if a Rhino expression compilation method is vulnerable to code injection. + */ +predicate compilesScript(MethodAccess ma, Expr sink) { + exists(RhinoCompileClassMethod m | m = ma.getMethod() | sink = ma.getArgument(0)) +} + +/** + * Holds if a Rhino class loading method is vulnerable to code injection. + */ +predicate definesRhinoClass(MethodAccess ma, Expr sink) { + exists(RhinoDefineClassMethod m | m = ma.getMethod() | sink = ma.getArgument(1)) +} + +/** A script injection sink. */ +class ScriptInjectionSink extends DataFlow::ExprNode { + MethodAccess methodAccess; + + ScriptInjectionSink() { + isScriptArgument(methodAccess, this.getExpr()) or + evaluatesRhinoExpression(methodAccess, this.getExpr()) or + compilesScript(methodAccess, this.getExpr()) or + definesRhinoClass(methodAccess, this.getExpr()) + } + + /** An access to the method associated with this sink. */ + MethodAccess getMethodAccess() { result = methodAccess } +} + +/** + * A taint tracking configuration that tracks flow from `RemoteFlowSource` to an argument + * of a method call that executes injected script. + */ +class ScriptInjectionConfiguration extends TaintTracking::Configuration { + ScriptInjectionConfiguration() { this = "ScriptInjectionConfiguration" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof ScriptInjectionSink } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, ScriptInjectionConfiguration conf +where conf.hasFlowPath(source, sink) +select sink.getNode().(ScriptInjectionSink).getMethodAccess(), source, sink, + "Java Script Engine evaluate $@.", source.getNode(), "user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-094/UnsafeExpressionEvaluationWithJuel.java b/java/ql/src/experimental/Security/CWE/CWE-094/UnsafeExpressionEvaluationWithJuel.java new file mode 100644 index 00000000000..27afa0fcb49 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-094/UnsafeExpressionEvaluationWithJuel.java @@ -0,0 +1,5 @@ +String expression = "${" + getRemoteUserInput() + "}"; +ExpressionFactory factory = new de.odysseus.el.ExpressionFactoryImpl(); +ValueExpression e = factory.createValueExpression(context, expression, Object.class); +SimpleContext context = getContext(); +Object result = e.getValue(context); \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.java b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.java new file mode 100644 index 00000000000..48d80707ff8 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.java @@ -0,0 +1,44 @@ +class SensitiveCookieNotHttpOnly { + // GOOD - Create a sensitive cookie with the `HttpOnly` flag set. + public void addCookie(String jwt_token, HttpServletRequest request, HttpServletResponse response) { + Cookie jwtCookie =new Cookie("jwt_token", jwt_token); + jwtCookie.setPath("/"); + jwtCookie.setMaxAge(3600*24*7); + jwtCookie.setHttpOnly(true); + response.addCookie(jwtCookie); + } + + // BAD - Create a sensitive cookie without the `HttpOnly` flag set. + public void addCookie2(String jwt_token, String userId, HttpServletRequest request, HttpServletResponse response) { + Cookie jwtCookie =new Cookie("jwt_token", jwt_token); + jwtCookie.setPath("/"); + jwtCookie.setMaxAge(3600*24*7); + response.addCookie(jwtCookie); + } + + // GOOD - Set a sensitive cookie header with the `HttpOnly` flag set. + public void addCookie3(String authId, HttpServletRequest request, HttpServletResponse response) { + response.addHeader("Set-Cookie", "token=" +authId + ";HttpOnly;Secure"); + } + + // BAD - Set a sensitive cookie header without the `HttpOnly` flag set. + public void addCookie4(String authId, HttpServletRequest request, HttpServletResponse response) { + response.addHeader("Set-Cookie", "token=" +authId + ";Secure"); + } + + // GOOD - Set a sensitive cookie header using the class `javax.ws.rs.core.Cookie` with the `HttpOnly` flag set through string concatenation. + public void addCookie5(String accessKey, HttpServletRequest request, HttpServletResponse response) { + response.setHeader("Set-Cookie", new NewCookie("session-access-key", accessKey, "/", null, null, 0, true) + ";HttpOnly"); + } + + // BAD - Set a sensitive cookie header using the class `javax.ws.rs.core.Cookie` without the `HttpOnly` flag set. + public void addCookie6(String accessKey, HttpServletRequest request, HttpServletResponse response) { + response.setHeader("Set-Cookie", new NewCookie("session-access-key", accessKey, "/", null, null, 0, true).toString()); + } + + // GOOD - Set a sensitive cookie header using the class `javax.ws.rs.core.Cookie` with the `HttpOnly` flag set through the constructor. + public void addCookie7(String accessKey, HttpServletRequest request, HttpServletResponse response) { + NewCookie accessKeyCookie = new NewCookie("session-access-key", accessKey, "/", null, null, 0, true, true); + response.setHeader("Set-Cookie", accessKeyCookie.toString()); + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.qhelp b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.qhelp new file mode 100644 index 00000000000..ee3e8a4181a --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.qhelp @@ -0,0 +1,27 @@ + + + + +

    Cross-Site Scripting (XSS) is categorized as one of the OWASP Top 10 Security Vulnerabilities. The HttpOnly flag directs compatible browsers to prevent client-side script from accessing cookies. Including the HttpOnly flag in the Set-Cookie HTTP response header for a sensitive cookie helps mitigate the risk associated with XSS where an attacker's script code attempts to read the contents of a cookie and exfiltrate information obtained.

    +
    + + +

    Use the HttpOnly flag when generating a cookie containing sensitive information to help mitigate the risk of client side script accessing the protected cookie.

    +
    + + +

    The following example shows two ways of generating sensitive cookies. In the 'BAD' cases, the HttpOnly flag is not set. In the 'GOOD' cases, the HttpOnly flag is set.

    + +
    + + +
  • + PortSwigger: + Cookie without HttpOnly flag set +
  • +
  • + OWASP: + HttpOnly +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql new file mode 100644 index 00000000000..4c0dc624d07 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-1004/SensitiveCookieNotHttpOnly.ql @@ -0,0 +1,221 @@ +/** + * @name Sensitive cookies without the HttpOnly response header set + * @description Sensitive cookies without the 'HttpOnly' flag set leaves session cookies vulnerable to + * an XSS attack. + * @kind path-problem + * @id java/sensitive-cookie-not-httponly + * @tags security + * external/cwe/cwe-1004 + */ + +/* + * Sketch of the structure of this query: we track cookie names that appear to be sensitive + * (e.g. `session` or `token`) to a `ServletResponse.addHeader(...)` or `.addCookie(...)` + * method that does not set the `httpOnly` flag. Subsidiary configurations + * `MatchesHttpOnlyConfiguration` and `SetHttpOnlyInCookieConfiguration` are used to establish + * when the `httpOnly` flag is likely to have been set, before configuration + * `MissingHttpOnlyConfiguration` establishes that a non-`httpOnly` cookie has a sensitive-seeming name. + */ + +import java +import semmle.code.java.dataflow.FlowSteps +import semmle.code.java.frameworks.Servlets +import semmle.code.java.dataflow.TaintTracking +import semmle.code.java.dataflow.TaintTracking2 +import DataFlow::PathGraph + +/** Gets a regular expression for matching common names of sensitive cookies. */ +string getSensitiveCookieNameRegex() { result = "(?i).*(auth|session|token|key|credential).*" } + +/** Gets a regular expression for matching CSRF cookies. */ +string getCsrfCookieNameRegex() { result = "(?i).*(csrf).*" } + +/** + * Holds if a string is concatenated with the name of a sensitive cookie. Excludes CSRF cookies since + * they are special cookies implementing the Synchronizer Token Pattern that can be used in JavaScript. + */ +predicate isSensitiveCookieNameExpr(Expr expr) { + exists(string s | s = expr.(CompileTimeConstantExpr).getStringValue() | + s.regexpMatch(getSensitiveCookieNameRegex()) and not s.regexpMatch(getCsrfCookieNameRegex()) + ) + or + isSensitiveCookieNameExpr(expr.(AddExpr).getAnOperand()) +} + +/** A sensitive cookie name. */ +class SensitiveCookieNameExpr extends Expr { + SensitiveCookieNameExpr() { isSensitiveCookieNameExpr(this) } +} + +/** A method call that sets a `Set-Cookie` header. */ +class SetCookieMethodAccess extends MethodAccess { + SetCookieMethodAccess() { + ( + this.getMethod() instanceof ResponseAddHeaderMethod or + this.getMethod() instanceof ResponseSetHeaderMethod + ) and + this.getArgument(0).(CompileTimeConstantExpr).getStringValue().toLowerCase() = "set-cookie" + } +} + +/** + * A taint configuration tracking flow from the text `httponly` to argument 1 of + * `SetCookieMethodAccess`. + */ +class MatchesHttpOnlyConfiguration extends TaintTracking2::Configuration { + MatchesHttpOnlyConfiguration() { this = "MatchesHttpOnlyConfiguration" } + + override predicate isSource(DataFlow::Node source) { + source.asExpr().(CompileTimeConstantExpr).getStringValue().toLowerCase().matches("%httponly%") + } + + override predicate isSink(DataFlow::Node sink) { + sink.asExpr() = any(SetCookieMethodAccess ma).getArgument(1) + } +} + +/** A class descended from `javax.servlet.http.Cookie` or `javax/jakarta.ws.rs.core.Cookie`. */ +class CookieClass extends RefType { + CookieClass() { + this.getASupertype*() + .hasQualifiedName(["javax.servlet.http", "javax.ws.rs.core", "jakarta.ws.rs.core"], "Cookie") + } +} + +/** Holds if `expr` is any boolean-typed expression other than literal `false`. */ +// Inlined because this could be a very large result set if computed out of context +pragma[inline] +predicate mayBeBooleanTrue(Expr expr) { + expr.getType() instanceof BooleanType and + not expr.(CompileTimeConstantExpr).getBooleanValue() = false +} + +/** Holds if the method call may set the `HttpOnly` flag. */ +predicate setsCookieHttpOnly(MethodAccess ma) { + ma.getMethod().getName() = "setHttpOnly" and + // any use of setHttpOnly(x) where x isn't false is probably safe + mayBeBooleanTrue(ma.getArgument(0)) +} + +/** Holds if `ma` removes a cookie. */ +predicate removesCookie(MethodAccess ma) { + ma.getMethod().getName() = "setMaxAge" and + ma.getArgument(0).(IntegerLiteral).getIntValue() = 0 +} + +/** + * Holds if the MethodAccess `ma` is a test method call indicated by: + * a) in a test directory such as `src/test/java` + * b) in a test package whose name has the word `test` + * c) in a test class whose name has the word `test` + * d) in a test class implementing a test framework such as JUnit or TestNG + */ +predicate isTestMethod(MethodAccess ma) { + exists(Method m | + m = ma.getEnclosingCallable() and + ( + m.getDeclaringType().getName().toLowerCase().matches("%test%") or // Simple check to exclude test classes to reduce FPs + m.getDeclaringType().getPackage().getName().toLowerCase().matches("%test%") or // Simple check to exclude classes in test packages to reduce FPs + exists(m.getLocation().getFile().getAbsolutePath().indexOf("/src/test/java")) or // Match test directory structure of build tools like maven + m instanceof TestMethod // Test method of a test case implementing a test framework such as JUnit or TestNG + ) + ) +} + +/** + * A taint configuration tracking flow of a method that sets the `HttpOnly` flag, + * or one that removes a cookie, to a `ServletResponse.addCookie` call. + */ +class SetHttpOnlyOrRemovesCookieConfiguration extends TaintTracking2::Configuration { + SetHttpOnlyOrRemovesCookieConfiguration() { this = "SetHttpOnlyOrRemovesCookieConfiguration" } + + override predicate isSource(DataFlow::Node source) { + source.asExpr() = + any(MethodAccess ma | setsCookieHttpOnly(ma) or removesCookie(ma)).getQualifier() + } + + override predicate isSink(DataFlow::Node sink) { + sink.asExpr() = + any(MethodAccess ma | ma.getMethod() instanceof ResponseAddCookieMethod).getArgument(0) + } +} + +/** + * A cookie that is added to an HTTP response and which doesn't have `httpOnly` set, used as a sink + * in `MissingHttpOnlyConfiguration`. + */ +class CookieResponseSink extends DataFlow::ExprNode { + CookieResponseSink() { + exists(MethodAccess ma | + ( + ma.getMethod() instanceof ResponseAddCookieMethod and + this.getExpr() = ma.getArgument(0) and + not exists(SetHttpOnlyOrRemovesCookieConfiguration cc | cc.hasFlowTo(this)) + or + ma instanceof SetCookieMethodAccess and + this.getExpr() = ma.getArgument(1) and + not exists(MatchesHttpOnlyConfiguration cc | cc.hasFlowTo(this)) // response.addHeader("Set-Cookie", "token=" +authId + ";HttpOnly;Secure") + ) and + not isTestMethod(ma) // Test class or method + ) + } +} + +/** Holds if `cie` is an invocation of a JAX-RS `NewCookie` constructor that sets `HttpOnly` to true. */ +predicate setsHttpOnlyInNewCookie(ClassInstanceExpr cie) { + cie.getConstructedType().hasQualifiedName(["javax.ws.rs.core", "jakarta.ws.rs.core"], "NewCookie") and + ( + cie.getNumArgument() = 6 and + mayBeBooleanTrue(cie.getArgument(5)) // NewCookie(Cookie cookie, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly) + or + cie.getNumArgument() = 8 and + cie.getArgument(6).getType() instanceof BooleanType and + mayBeBooleanTrue(cie.getArgument(7)) // NewCookie(String name, String value, String path, String domain, String comment, int maxAge, boolean secure, boolean httpOnly) + or + cie.getNumArgument() = 10 and + mayBeBooleanTrue(cie.getArgument(9)) // NewCookie(String name, String value, String path, String domain, int version, String comment, int maxAge, Date expiry, boolean secure, boolean httpOnly) + ) +} + +/** + * A taint configuration tracking flow from a sensitive cookie without the `HttpOnly` flag + * set to its HTTP response. + */ +class MissingHttpOnlyConfiguration extends TaintTracking::Configuration { + MissingHttpOnlyConfiguration() { this = "MissingHttpOnlyConfiguration" } + + override predicate isSource(DataFlow::Node source) { + source.asExpr() instanceof SensitiveCookieNameExpr + } + + override predicate isSink(DataFlow::Node sink) { sink instanceof CookieResponseSink } + + override predicate isSanitizer(DataFlow::Node node) { + // JAX-RS's `new NewCookie("session-access-key", accessKey, "/", null, null, 0, true, true)` and similar + setsHttpOnlyInNewCookie(node.asExpr()) + } + + override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { + exists( + ConstructorCall cc // new Cookie(...) + | + cc.getConstructedType() instanceof CookieClass and + pred.asExpr() = cc.getAnArgument() and + succ.asExpr() = cc + ) + or + exists( + MethodAccess ma // cookie.toString() + | + ma.getMethod().getName() = "toString" and + ma.getQualifier().getType() instanceof CookieClass and + pred.asExpr() = ma.getQualifier() and + succ.asExpr() = ma + ) + } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, MissingHttpOnlyConfiguration c +where c.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "$@ doesn't have the HttpOnly flag set.", source.getNode(), + "This sensitive cookie" diff --git a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql index 467d78ae1c4..9fa2fe596fd 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-297/InsecureLdapEndpoint.ql @@ -68,7 +68,7 @@ predicate isBooleanTrue(Expr expr) { or exists(MethodAccess ma | expr = ma and - ma.getMethod().hasName("toString") and + ma.getMethod() instanceof ToStringMethod and ma.getQualifier().(FieldAccess).getField().hasName("TRUE") and ma.getQualifier() .(FieldAccess) diff --git a/java/ql/src/experimental/Security/CWE/CWE-347/MissingJWTSignatureCheck.java b/java/ql/src/experimental/Security/CWE/CWE-347/MissingJWTSignatureCheck.java new file mode 100644 index 00000000000..1760b4d6097 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-347/MissingJWTSignatureCheck.java @@ -0,0 +1,34 @@ +public void badJwt(String token) { + Jwts.parserBuilder() + .setSigningKey("someBase64EncodedKey").build() + .parse(token); // BAD: Does not verify the signature +} + +public void badJwtHandler(String token) { + Jwts.parserBuilder() + .setSigningKey("someBase64EncodedKey").build() + .parse(plaintextJwt, new JwtHandlerAdapter>() { + @Override + public Jwt onPlaintextJwt(Jwt jwt) { + return jwt; + } + }); // BAD: The handler is called on an unverified JWT +} + +public void goodJwt(String token) { + Jwts.parserBuilder() + .setSigningKey("someBase64EncodedKey").build() + .parseClaimsJws(token) // GOOD: Verify the signature + .getBody(); +} + +public void goodJwtHandler(String token) { + Jwts.parserBuilder() + .setSigningKey("someBase64EncodedKey").build() + .parse(plaintextJwt, new JwtHandlerAdapter>() { + @Override + public Jws onPlaintextJws(Jws jws) { + return jws; + } + }); // GOOD: The handler is called on a verified JWS +} \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-347/MissingJWTSignatureCheck.qhelp b/java/ql/src/experimental/Security/CWE/CWE-347/MissingJWTSignatureCheck.qhelp new file mode 100644 index 00000000000..3b93936c21b --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-347/MissingJWTSignatureCheck.qhelp @@ -0,0 +1,39 @@ + + + + +

    A JSON Web Token (JWT) consists of three parts: header, payload, and signature. +The io.jsonwebtoken.jjwt library is one of many libraries used for working with JWTs. +It offers different methods for parsing tokens like parse, parseClaimsJws, and parsePlaintextJws. +The last two correctly verify that the JWT is properly signed. +This is done by computing the signature of the combination of header and payload and +comparing the locally computed signature with the signature part of the JWT. +

    +

    +Therefore it is necessary to provide the JwtParser with a key that is used for signature validation. +Unfortunately the parse method accepts a JWT whose signature is empty although a signing key has been set for the parser. +This means that an attacker can create arbitrary JWTs that will be accepted if this method is used. +

    +
    + + +

    Always verify the signature by using either the parseClaimsJws and parsePlaintextJws methods or +by overriding the onPlaintextJws or onClaimsJws of JwtHandlerAdapter. +

    + +
    + + +

    The following example shows four cases where a signing key is set for a parser. +In the first bad case the parse method is used which will not validate the signature. +The second bad case uses a JwtHandlerAdapter where the onPlaintextJwt method is overriden so it will not validate the signature. +The third and fourth good cases use parseClaimsJws method or override the onPlaintextJws method. +

    + + + +
    + +
  • zofrex: How I Found An alg=none JWT Vulnerability in the NHS Contact Tracing App.
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-347/MissingJWTSignatureCheck.ql b/java/ql/src/experimental/Security/CWE/CWE-347/MissingJWTSignatureCheck.ql new file mode 100644 index 00000000000..6d7462f1338 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-347/MissingJWTSignatureCheck.ql @@ -0,0 +1,200 @@ +/** + * @name Missing JWT signature check + * @description Not checking the JWT signature allows an attacker to forge their own tokens. + * @kind problem + * @problem.severity error + * @precision high + * @id java/missing-jwt-signature-check + * @tags security + * external/cwe/cwe-347 + */ + +import java +import semmle.code.java.dataflow.DataFlow + +/** The interface `io.jsonwebtoken.JwtParser`. */ +class TypeJwtParser extends Interface { + TypeJwtParser() { this.hasQualifiedName("io.jsonwebtoken", "JwtParser") } +} + +/** The interface `io.jsonwebtoken.JwtParser` or a type derived from it. */ +class TypeDerivedJwtParser extends RefType { + TypeDerivedJwtParser() { this.getASourceSupertype*() instanceof TypeJwtParser } +} + +/** The interface `io.jsonwebtoken.JwtParserBuilder`. */ +class TypeJwtParserBuilder extends Interface { + TypeJwtParserBuilder() { this.hasQualifiedName("io.jsonwebtoken", "JwtParserBuilder") } +} + +/** The interface `io.jsonwebtoken.JwtHandler`. */ +class TypeJwtHandler extends Interface { + TypeJwtHandler() { this.hasQualifiedName("io.jsonwebtoken", "JwtHandler") } +} + +/** The class `io.jsonwebtoken.JwtHandlerAdapter`. */ +class TypeJwtHandlerAdapter extends Class { + TypeJwtHandlerAdapter() { this.hasQualifiedName("io.jsonwebtoken", "JwtHandlerAdapter") } +} + +/** The `parse(token, handler)` method defined in `JwtParser`. */ +private class JwtParserParseHandlerMethod extends Method { + JwtParserParseHandlerMethod() { + this.hasName("parse") and + this.getDeclaringType() instanceof TypeJwtParser and + this.getNumberOfParameters() = 2 + } +} + +/** The `parse(token)`, `parseClaimsJwt(token)` and `parsePlaintextJwt(token)` methods defined in `JwtParser`. */ +private class JwtParserInsecureParseMethod extends Method { + JwtParserInsecureParseMethod() { + this.hasName(["parse", "parseClaimsJwt", "parsePlaintextJwt"]) and + this.getNumberOfParameters() = 1 and + this.getDeclaringType() instanceof TypeJwtParser + } +} + +/** The `on(Claims|Plaintext)Jwt` methods defined in `JwtHandler`. */ +private class JwtHandlerOnJwtMethod extends Method { + JwtHandlerOnJwtMethod() { + this.hasName(["onClaimsJwt", "onPlaintextJwt"]) and + this.getNumberOfParameters() = 1 and + this.getDeclaringType() instanceof TypeJwtHandler + } +} + +/** The `on(Claims|Plaintext)Jwt` methods defined in `JwtHandlerAdapter`. */ +private class JwtHandlerAdapterOnJwtMethod extends Method { + JwtHandlerAdapterOnJwtMethod() { + this.hasName(["onClaimsJwt", "onPlaintextJwt"]) and + this.getNumberOfParameters() = 1 and + this.getDeclaringType() instanceof TypeJwtHandlerAdapter + } +} + +/** + * Holds if `parseHandlerExpr` is an insecure `JwtHandler`. + * That is, it overrides a method from `JwtHandlerOnJwtMethod` and the override is not defined on `JwtHandlerAdapter`. + * `JwtHandlerAdapter`'s overrides are safe since they always throw an exception. + */ +private predicate isInsecureJwtHandler(Expr parseHandlerExpr) { + exists(RefType t | + parseHandlerExpr.getType() = t and + t.getASourceSupertype*() instanceof TypeJwtHandler and + exists(Method m | + m = t.getAMethod() and + m.getASourceOverriddenMethod+() instanceof JwtHandlerOnJwtMethod and + not m.getSourceDeclaration() instanceof JwtHandlerAdapterOnJwtMethod + ) + ) +} + +/** + * An access to an insecure parsing method. + * That is, either a call to a `parse(token)`, `parseClaimsJwt(token)` or `parsePlaintextJwt(token)` method or + * a call to a `parse(token, handler)` method where the `handler` is considered insecure. + */ +private class JwtParserInsecureParseMethodAccess extends MethodAccess { + JwtParserInsecureParseMethodAccess() { + this.getMethod().getASourceOverriddenMethod*() instanceof JwtParserInsecureParseMethod + or + this.getMethod().getASourceOverriddenMethod*() instanceof JwtParserParseHandlerMethod and + isInsecureJwtHandler(this.getArgument(1)) + } +} + +/** + * Holds if `signingMa` directly or indirectly sets a signing key for `expr`, which is a `JwtParser`. + * The `setSigningKey` and `setSigningKeyResolver` methods set a signing key for a `JwtParser`. + * Directly means code like this: + * ```java + * Jwts.parser().setSigningKey(key).parse(token); + * ``` + * Here the signing key is set directly on a `JwtParser`. + * Indirectly means code like this: + * ```java + * Jwts.parserBuilder().setSigningKey(key).build().parse(token); + * ``` + * In this case, the signing key is set on a `JwtParserBuilder` indirectly setting the key of `JwtParser` that is created by the call to `build`. + */ +private predicate isSigningKeySetter(Expr expr, MethodAccess signingMa) { + any(SigningToInsecureMethodAccessDataFlow s) + .hasFlow(DataFlow::exprNode(signingMa), DataFlow::exprNode(expr)) +} + +/** + * An expr that is a (sub-type of) `JwtParser` for which a signing key has been set and which is used as + * the qualifier to a `JwtParserInsecureParseMethodAccess`. + */ +private class JwtParserWithSigningKeyExpr extends Expr { + MethodAccess signingMa; + + JwtParserWithSigningKeyExpr() { + this.getType() instanceof TypeDerivedJwtParser and + isSigningKeySetter(this, signingMa) + } + + /** Gets the method access that sets the signing key for this parser. */ + MethodAccess getSigningMethodAccess() { result = signingMa } +} + +/** + * Models flow from `SigningKeyMethodAccess`es to qualifiers of `JwtParserInsecureParseMethodAccess`es. + * This is used to determine whether a `JwtParser` has a signing key set. + */ +private class SigningToInsecureMethodAccessDataFlow extends DataFlow::Configuration { + SigningToInsecureMethodAccessDataFlow() { this = "SigningToExprDataFlow" } + + override predicate isSource(DataFlow::Node source) { + source.asExpr() instanceof SigningKeyMethodAccess + } + + override predicate isSink(DataFlow::Node sink) { + any(JwtParserInsecureParseMethodAccess ma).getQualifier() = sink.asExpr() + } + + /** Models the builder style of `JwtParser` and `JwtParserBuilder`. */ + override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) { + ( + pred.asExpr().getType() instanceof TypeDerivedJwtParser or + pred.asExpr().getType().(RefType).getASourceSupertype*() instanceof TypeJwtParserBuilder + ) and + succ.asExpr().(MethodAccess).getQualifier() = pred.asExpr() + } +} + +/** An access to the `setSigningKey` or `setSigningKeyResolver` method (or an overridden method) defined in `JwtParser` and `JwtParserBuilder`. */ +private class SigningKeyMethodAccess extends MethodAccess { + SigningKeyMethodAccess() { + exists(Method m | + m.hasName(["setSigningKey", "setSigningKeyResolver"]) and + m.getNumberOfParameters() = 1 and + ( + m.getDeclaringType() instanceof TypeJwtParser or + m.getDeclaringType() instanceof TypeJwtParserBuilder + ) + | + m = this.getMethod().getASourceOverriddenMethod*() + ) + } +} + +/** + * Holds if the `MethodAccess` `ma` occurs in a test file. A test file is any file that + * is a direct or indirect child of a directory named `test`, ignoring case. + */ +private predicate isInTestFile(MethodAccess ma) { + exists(string lowerCasedAbsolutePath | + lowerCasedAbsolutePath = ma.getLocation().getFile().getAbsolutePath().toLowerCase() + | + lowerCasedAbsolutePath.matches("%/test/%") and + not lowerCasedAbsolutePath + .matches("%/ql/test/experimental/query-tests/security/CWE-347/%".toLowerCase()) + ) +} + +from JwtParserInsecureParseMethodAccess ma, JwtParserWithSigningKeyExpr parserExpr +where ma.getQualifier() = parserExpr and not isInTestFile(ma) +select ma, "A signing key is set $@, but the signature is not verified.", + parserExpr.getSigningMethodAccess(), "here" diff --git a/java/ql/src/experimental/Security/CWE/CWE-348/ClientSuppliedIpUsedInSecurityCheck.java b/java/ql/src/experimental/Security/CWE/CWE-348/ClientSuppliedIpUsedInSecurityCheck.java new file mode 100644 index 00000000000..93a860981d1 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-348/ClientSuppliedIpUsedInSecurityCheck.java @@ -0,0 +1,49 @@ +import javax.servlet.http.HttpServletRequest; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class ClientSuppliedIpUsedInSecurityCheck { + + @Autowired + private HttpServletRequest request; + + @GetMapping(value = "bad1") + public void bad1(HttpServletRequest request) { + String ip = getClientIP(); + if (!StringUtils.startsWith(ip, "192.168.")) { + new Exception("ip illegal"); + } + } + + @GetMapping(value = "bad2") + public void bad2(HttpServletRequest request) { + String ip = getClientIP(); + if (!"127.0.0.1".equals(ip)) { + new Exception("ip illegal"); + } + } + + @GetMapping(value = "good1") + @ResponseBody + public String good1(HttpServletRequest request) { + String ip = request.getHeader("X-FORWARDED-FOR"); + // Good: if this application runs behind a reverse proxy it may append the real remote IP to the end of any client-supplied X-Forwarded-For header. + ip = ip.split(",")[ip.split(",").length - 1]; + if (!StringUtils.startsWith(ip, "192.168.")) { + new Exception("ip illegal"); + } + return ip; + } + + protected String getClientIP() { + String xfHeader = request.getHeader("X-Forwarded-For"); + if (xfHeader == null) { + return request.getRemoteAddr(); + } + return xfHeader.split(",")[0]; + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-348/ClientSuppliedIpUsedInSecurityCheck.qhelp b/java/ql/src/experimental/Security/CWE/CWE-348/ClientSuppliedIpUsedInSecurityCheck.qhelp new file mode 100644 index 00000000000..fd62ab2968b --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-348/ClientSuppliedIpUsedInSecurityCheck.qhelp @@ -0,0 +1,35 @@ + + + +

    An original client IP address is retrieved from an http header (X-Forwarded-For or X-Real-IP or Proxy-Client-IP +etc.), which is used to ensure security. Attackers can forge the value of these identifiers to +bypass a ban-list, for example.

    + +
    + + +

    Do not trust the values of HTTP headers allegedly identifying the originating IP. If you are aware your application will run behind some reverse proxies then the last entry of a X-Forwarded-For header value may be more trustworthy than the rest of it because some reverse proxies append the IP address they observed to the end of any remote-supplied header.

    + +
    + + +

    The following examples show the bad case and the good case respectively. +In bad1 method and bad2 method, the client ip the X-Forwarded-For is split into comma-separated values, but the less-trustworthy first one is used. Both of these examples could be deceived by providing a forged HTTP header. The method +good1 similarly splits an X-Forwarded-For value, but uses the last, more-trustworthy entry.

    + + + +
    + + +
  • Dennis Schneider: +Prevent IP address spoofing with X-Forwarded-For header when using AWS ELB and Clojure Ring +
  • + +
  • Security Rule Zero: A Warning about X-Forwarded-For +
  • + +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-348/ClientSuppliedIpUsedInSecurityCheck.ql b/java/ql/src/experimental/Security/CWE/CWE-348/ClientSuppliedIpUsedInSecurityCheck.ql new file mode 100644 index 00000000000..78d8bfee5f0 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-348/ClientSuppliedIpUsedInSecurityCheck.ql @@ -0,0 +1,53 @@ +/** + * @name IP address spoofing + * @description A remote endpoint identifier is read from an HTTP header. Attackers can modify the value + * of the identifier to forge the client ip. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/ip-address-spoofing + * @tags security + * external/cwe/cwe-348 + */ + +import java +import ClientSuppliedIpUsedInSecurityCheckLib +import semmle.code.java.dataflow.FlowSources +import DataFlow::PathGraph + +/** + * Taint-tracking configuration tracing flow from obtaining a client ip from an HTTP header to a sensitive use. + */ +class ClientSuppliedIpUsedInSecurityCheckConfig extends TaintTracking::Configuration { + ClientSuppliedIpUsedInSecurityCheckConfig() { this = "ClientSuppliedIpUsedInSecurityCheckConfig" } + + override predicate isSource(DataFlow::Node source) { + source instanceof ClientSuppliedIpUsedInSecurityCheck + } + + override predicate isSink(DataFlow::Node sink) { + sink instanceof ClientSuppliedIpUsedInSecurityCheckSink + } + + /** + * Splitting a header value by `,` and taking an entry other than the first is sanitizing, because + * later entries may originate from more-trustworthy intermediate proxies, not the original client. + */ + override predicate isSanitizer(DataFlow::Node node) { + exists(ArrayAccess aa, MethodAccess ma | aa.getArray() = ma | + ma.getQualifier() = node.asExpr() and + ma.getMethod() instanceof SplitMethod and + not aa.getIndexExpr().(CompileTimeConstantExpr).getIntValue() = 0 + ) + or + node.getType() instanceof PrimitiveType + or + node.getType() instanceof BoxedType + } +} + +from + DataFlow::PathNode source, DataFlow::PathNode sink, ClientSuppliedIpUsedInSecurityCheckConfig conf +where conf.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "IP address spoofing might include code from $@.", + source.getNode(), "this user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-348/ClientSuppliedIpUsedInSecurityCheckLib.qll b/java/ql/src/experimental/Security/CWE/CWE-348/ClientSuppliedIpUsedInSecurityCheckLib.qll new file mode 100644 index 00000000000..fd9353d3414 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-348/ClientSuppliedIpUsedInSecurityCheckLib.qll @@ -0,0 +1,100 @@ +import java +import DataFlow +import semmle.code.java.frameworks.Networking +import semmle.code.java.security.QueryInjection +import experimental.semmle.code.java.Logging + +/** + * A data flow source of the client ip obtained according to the remote endpoint identifier specified + * (`X-Forwarded-For`, `X-Real-IP`, `Proxy-Client-IP`, etc.) in the header. + * + * For example: `ServletRequest.getHeader("X-Forwarded-For")`. + */ +class ClientSuppliedIpUsedInSecurityCheck extends DataFlow::Node { + ClientSuppliedIpUsedInSecurityCheck() { + exists(MethodAccess ma | + ma.getMethod().hasName("getHeader") and + ma.getArgument(0).(CompileTimeConstantExpr).getStringValue().toLowerCase() in [ + "x-forwarded-for", "x-real-ip", "proxy-client-ip", "wl-proxy-client-ip", + "http_x_forwarded_for", "http_x_forwarded", "http_x_cluster_client_ip", "http_client_ip", + "http_forwarded_for", "http_forwarded", "http_via", "remote_addr" + ] and + ma = this.asExpr() + ) + } +} + +/** A data flow sink for ip address forgery vulnerabilities. */ +abstract class ClientSuppliedIpUsedInSecurityCheckSink extends DataFlow::Node { } + +/** + * A data flow sink for remote client ip comparison. + * + * For example: `if (!StringUtils.startsWith(ipAddr, "192.168.")){...` determine whether the client ip starts + * with `192.168.`, and the program can be deceived by forging the ip address. + */ +private class CompareSink extends ClientSuppliedIpUsedInSecurityCheckSink { + CompareSink() { + exists(MethodAccess ma | + ma.getMethod().getName() in ["equals", "equalsIgnoreCase"] and + ma.getMethod().getDeclaringType() instanceof TypeString and + ma.getMethod().getNumberOfParameters() = 1 and + ( + ma.getArgument(0) = this.asExpr() and + ma.getQualifier().(CompileTimeConstantExpr).getStringValue() instanceof PrivateHostName and + not ma.getQualifier().(CompileTimeConstantExpr).getStringValue() = "0:0:0:0:0:0:0:1" + or + ma.getQualifier() = this.asExpr() and + ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() instanceof PrivateHostName and + not ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "0:0:0:0:0:0:0:1" + ) + ) + or + exists(MethodAccess ma | + ma.getMethod().getName() in ["contains", "startsWith"] and + ma.getMethod().getDeclaringType() instanceof TypeString and + ma.getMethod().getNumberOfParameters() = 1 and + ma.getQualifier() = this.asExpr() and + ma.getAnArgument().(CompileTimeConstantExpr).getStringValue().regexpMatch(getIpAddressRegex()) // Matches IP-address-like strings + ) + or + exists(MethodAccess ma | + ma.getMethod().hasName("startsWith") and + ma.getMethod() + .getDeclaringType() + .hasQualifiedName(["org.apache.commons.lang3", "org.apache.commons.lang"], "StringUtils") and + ma.getMethod().getNumberOfParameters() = 2 and + ma.getAnArgument() = this.asExpr() and + ma.getAnArgument().(CompileTimeConstantExpr).getStringValue().regexpMatch(getIpAddressRegex()) + ) + or + exists(MethodAccess ma | + ma.getMethod().getName() in ["equals", "equalsIgnoreCase"] and + ma.getMethod() + .getDeclaringType() + .hasQualifiedName(["org.apache.commons.lang3", "org.apache.commons.lang"], "StringUtils") and + ma.getMethod().getNumberOfParameters() = 2 and + ma.getAnArgument() = this.asExpr() and + ma.getAnArgument().(CompileTimeConstantExpr).getStringValue() instanceof PrivateHostName and + not ma.getAnArgument().(CompileTimeConstantExpr).getStringValue() = "0:0:0:0:0:0:0:1" + ) + } +} + +/** A data flow sink for sql operation. */ +private class SqlOperationSink extends ClientSuppliedIpUsedInSecurityCheckSink { + SqlOperationSink() { this instanceof QueryInjectionSink } +} + +/** A method that split string. */ +class SplitMethod extends Method { + SplitMethod() { + this.getNumberOfParameters() = 1 and + this.hasQualifiedName("java.lang", "String", "split") + } +} + +string getIpAddressRegex() { + result = + "^((10\\.((1\\d{2})?|(2[0-4]\\d)?|(25[0-5])?|([1-9]\\d|[0-9])?)(\\.)?)|(192\\.168\\.)|172\\.(1[6789]|2[0-9]|3[01])\\.)((1\\d{2})?|(2[0-4]\\d)?|(25[0-5])?|([1-9]\\d|[0-9])?)(\\.)?((1\\d{2})?|(2[0-4]\\d)?|(25[0-5])?|([1-9]\\d|[0-9])?)$" +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonStringLib.qll b/java/ql/src/experimental/Security/CWE/CWE-352/JsonStringLib.qll new file mode 100644 index 00000000000..b8f1a13b119 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonStringLib.qll @@ -0,0 +1,57 @@ +import java +import semmle.code.java.dataflow.DataFlow +import semmle.code.java.dataflow.FlowSources +import DataFlow::PathGraph + +/** Json string type data. */ +abstract class JsonStringSource extends DataFlow::Node { } + +/** + * Convert to String using Gson library. * + * + * For example, in the method access `Gson.toJson(...)`, + * the `Object` type data is converted to the `String` type data. + */ +private class GsonString extends JsonStringSource { + GsonString() { + exists(MethodAccess ma, Method m | ma.getMethod() = m | + m.hasName("toJson") and + m.getDeclaringType().getASupertype*().hasQualifiedName("com.google.gson", "Gson") and + this.asExpr() = ma + ) + } +} + +/** + * Convert to String using Fastjson library. + * + * For example, in the method access `JSON.toJSONString(...)`, + * the `Object` type data is converted to the `String` type data. + */ +private class FastjsonString extends JsonStringSource { + FastjsonString() { + exists(MethodAccess ma, Method m | ma.getMethod() = m | + m.hasName("toJSONString") and + m.getDeclaringType().getASupertype*().hasQualifiedName("com.alibaba.fastjson", "JSON") and + this.asExpr() = ma + ) + } +} + +/** + * Convert to String using Jackson library. + * + * For example, in the method access `ObjectMapper.writeValueAsString(...)`, + * the `Object` type data is converted to the `String` type data. + */ +private class JacksonString extends JsonStringSource { + JacksonString() { + exists(MethodAccess ma, Method m | ma.getMethod() = m | + m.hasName("writeValueAsString") and + m.getDeclaringType() + .getASupertype*() + .hasQualifiedName("com.fasterxml.jackson.databind", "ObjectMapper") and + this.asExpr() = ma + ) + } +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.java b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.java new file mode 100644 index 00000000000..8f39efbc2b6 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.java @@ -0,0 +1,161 @@ +import com.alibaba.fastjson.JSONObject; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.util.HashMap; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.multipart.MultipartFile; + +@Controller +public class JsonpInjection { + + private static HashMap hashMap = new HashMap(); + + static { + hashMap.put("username","admin"); + hashMap.put("password","123456"); + } + + @GetMapping(value = "jsonp1") + @ResponseBody + public String bad1(HttpServletRequest request) { + String resultStr = null; + String jsonpCallback = request.getParameter("jsonpCallback"); + Gson gson = new Gson(); + String result = gson.toJson(hashMap); + resultStr = jsonpCallback + "(" + result + ")"; + return resultStr; + } + + @GetMapping(value = "jsonp2") + @ResponseBody + public String bad2(HttpServletRequest request) { + String resultStr = null; + String jsonpCallback = request.getParameter("jsonpCallback"); + resultStr = jsonpCallback + "(" + JSONObject.toJSONString(hashMap) + ")"; + return resultStr; + } + + @GetMapping(value = "jsonp3") + @ResponseBody + public String bad3(HttpServletRequest request) { + String resultStr = null; + String jsonpCallback = request.getParameter("jsonpCallback"); + String jsonStr = getJsonStr(hashMap); + resultStr = jsonpCallback + "(" + jsonStr + ")"; + return resultStr; + } + + @GetMapping(value = "jsonp4") + @ResponseBody + public String bad4(HttpServletRequest request) { + String resultStr = null; + String jsonpCallback = request.getParameter("jsonpCallback"); + String restr = JSONObject.toJSONString(hashMap); + resultStr = jsonpCallback + "(" + restr + ");"; + return resultStr; + } + + @GetMapping(value = "jsonp5") + @ResponseBody + public void bad5(HttpServletRequest request, + HttpServletResponse response) throws Exception { + String jsonpCallback = request.getParameter("jsonpCallback"); + PrintWriter pw = null; + Gson gson = new Gson(); + String result = gson.toJson(hashMap); + String resultStr = null; + pw = response.getWriter(); + resultStr = jsonpCallback + "(" + result + ")"; + pw.println(resultStr); + } + + @GetMapping(value = "jsonp6") + @ResponseBody + public void bad6(HttpServletRequest request, + HttpServletResponse response) throws Exception { + String jsonpCallback = request.getParameter("jsonpCallback"); + PrintWriter pw = null; + ObjectMapper mapper = new ObjectMapper(); + String result = mapper.writeValueAsString(hashMap); + String resultStr = null; + pw = response.getWriter(); + resultStr = jsonpCallback + "(" + result + ")"; + pw.println(resultStr); + } + + @RequestMapping(value = "jsonp7", method = RequestMethod.GET) + @ResponseBody + public String bad7(HttpServletRequest request) { + String resultStr = null; + String jsonpCallback = request.getParameter("jsonpCallback"); + Gson gson = new Gson(); + String result = gson.toJson(hashMap); + resultStr = jsonpCallback + "(" + result + ")"; + return resultStr; + } + + @RequestMapping(value = "jsonp11") + @ResponseBody + public String good1(HttpServletRequest request) { + JSONObject parameterObj = readToJSONObect(request); + String resultStr = null; + String jsonpCallback = request.getParameter("jsonpCallback"); + String restr = JSONObject.toJSONString(hashMap); + resultStr = jsonpCallback + "(" + restr + ");"; + return resultStr; + } + + @RequestMapping(value = "jsonp12") + @ResponseBody + public String good2(@RequestParam("file") MultipartFile file,HttpServletRequest request) { + if(null == file){ + return "upload file error"; + } + String fileName = file.getOriginalFilename(); + System.out.println("file operations"); + String resultStr = null; + String jsonpCallback = request.getParameter("jsonpCallback"); + String restr = JSONObject.toJSONString(hashMap); + resultStr = jsonpCallback + "(" + restr + ");"; + return resultStr; + } + + public static JSONObject readToJSONObect(HttpServletRequest request){ + String jsonText = readPostContent(request); + JSONObject jsonObj = JSONObject.parseObject(jsonText, JSONObject.class); + return jsonObj; + } + + public static String readPostContent(HttpServletRequest request){ + BufferedReader in= null; + String content = null; + String line = null; + try { + in = new BufferedReader(new InputStreamReader(request.getInputStream(),"UTF-8")); + StringBuilder buf = new StringBuilder(); + while ((line = in.readLine()) != null) { + buf.append(line); + } + content = buf.toString(); + } catch (IOException e) { + e.printStackTrace(); + } + String uri = request.getRequestURI(); + return content; + } + + public static String getJsonStr(Object result) { + return JSONObject.toJSONString(result); + } +} \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.qhelp new file mode 100644 index 00000000000..e8fb89d3989 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.qhelp @@ -0,0 +1,37 @@ + + + +

    The software uses external input as the function name to wrap JSON data and returns it to the client as a request response. +When there is a cross-domain problem, this could lead to information leakage.

    + +
    + + +

    Adding Referer/Origin or random token verification processing can effectively prevent the leakage of sensitive information.

    + +
    + + +

    The following examples show the bad case and the good case respectively. Bad cases, such as bad1 to bad7, +will cause information leakage when there are cross-domain problems. In a good case, for example, in the good1 +method and the good2 method, When these two methods process the request, there must be a request body in the request, which does not meet the conditions of Jsonp injection.

    + + + +
    + + +
  • +OWASPLondon20161124_JSON_Hijacking_Gareth_Heyes: +JSON hijacking. +
  • +
  • +Practical JSONP Injection: + + Completely controllable from the URL (GET variable) +. +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql new file mode 100644 index 00000000000..71ee842f162 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql @@ -0,0 +1,45 @@ +/** + * @name JSONP Injection + * @description User-controlled callback function names that are not verified are vulnerable + * to jsonp injection attacks. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/jsonp-injection + * @tags security + * external/cwe/cwe-352 + */ + +import java +import JsonpInjectionLib +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.deadcode.WebEntryPoints +import DataFlow::PathGraph + +/** Taint-tracking configuration tracing flow from get method request sources to output jsonp data. */ +class RequestResponseFlowConfig extends TaintTracking::Configuration { + RequestResponseFlowConfig() { this = "RequestResponseFlowConfig" } + + override predicate isSource(DataFlow::Node source) { + source instanceof RemoteFlowSource and + any(RequestGetMethod m).polyCalls*(source.getEnclosingCallable()) + } + + override predicate isSink(DataFlow::Node sink) { + sink instanceof XssSink and + any(RequestGetMethod m).polyCalls*(sink.getEnclosingCallable()) + } + + override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { + exists(MethodAccess ma | + isRequestGetParamMethod(ma) and pred.asExpr() = ma.getQualifier() and succ.asExpr() = ma + ) + } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, RequestResponseFlowConfig conf +where + conf.hasFlowPath(source, sink) and + exists(JsonpInjectionFlowConfig jhfc | jhfc.hasFlowTo(sink.getNode())) +select sink.getNode(), source, sink, "Jsonp response might include code from $@.", source.getNode(), + "this user input" diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionLib.qll new file mode 100644 index 00000000000..6da4f87294a --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionLib.qll @@ -0,0 +1,118 @@ +import java +import DataFlow +import JsonStringLib +import semmle.code.java.security.XSS +import semmle.code.java.dataflow.DataFlow +import semmle.code.java.dataflow.DataFlow3 +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.frameworks.spring.SpringController + +/** + * A method that is called to handle an HTTP GET request. + */ +abstract class RequestGetMethod extends Method { + RequestGetMethod() { + not exists(MethodAccess ma | + // Exclude apparent GET handlers that read a request entity, because this likely indicates this is not in fact a GET handler. + // This is particularly a problem with Spring handlers, which can sometimes neglect to specify a request method. + // Even if it is in fact a GET handler, such a request method will be unusable in the context ` diff --git a/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.expected b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.expected index 95c6d7e03d9..47e57e0a242 100644 --- a/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.expected +++ b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.expected @@ -1,2 +1,6 @@ +underlyingTypeNode +| foo | Bar | foo.ts:3:1:5:1 | use (instance (member Bar (member exports (module foo)))) | +| foo | Bar | foo.ts:3:12:3:12 | use (instance (member Bar (member exports (module foo)))) | +#select | tst.ts:8:14:8:16 | arg | Base in global scope | | tst.ts:8:14:8:16 | arg | Sub in global scope | diff --git a/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.ql b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.ql index 77e311b3b76..72d4e6d0f3d 100644 --- a/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.ql +++ b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.ql @@ -3,3 +3,7 @@ import javascript from Expr e, TypeName typeName where e.getType().hasUnderlyingTypeName(typeName) select e, typeName + +query API::Node underlyingTypeNode(string mod, string name) { + result = API::Node::ofType(mod, name) +} diff --git a/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/foo.ts b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/foo.ts new file mode 100644 index 00000000000..1b5be79068a --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/foo.ts @@ -0,0 +1,5 @@ +import foo from "foo"; + +function f(x: foo.Bar) { + return x; +} diff --git a/javascript/ql/test/library-tests/TypeScript/IndexTypes/test.expected b/javascript/ql/test/library-tests/TypeScript/IndexTypes/test.expected new file mode 100644 index 00000000000..4779b178e4f --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/IndexTypes/test.expected @@ -0,0 +1,2 @@ +| Foo | boolean | +| typeof Foo in global scope | string | diff --git a/javascript/ql/test/library-tests/TypeScript/IndexTypes/test.ql b/javascript/ql/test/library-tests/TypeScript/IndexTypes/test.ql new file mode 100644 index 00000000000..ff6a2a4836f --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/IndexTypes/test.ql @@ -0,0 +1,3 @@ +import javascript + +query Type stringIndexType(Type t) { result = t.getStringIndexType() } diff --git a/javascript/ql/test/library-tests/TypeScript/IndexTypes/tsconfig.json b/javascript/ql/test/library-tests/TypeScript/IndexTypes/tsconfig.json new file mode 100644 index 00000000000..9e26dfeeb6e --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/IndexTypes/tsconfig.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/javascript/ql/test/library-tests/TypeScript/IndexTypes/tst.ts b/javascript/ql/test/library-tests/TypeScript/IndexTypes/tst.ts new file mode 100644 index 00000000000..9035ff2b2ac --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/IndexTypes/tst.ts @@ -0,0 +1,8 @@ + // static index signature + class Foo { + static hello = "world"; + static [n: string]: string; + [n: string]: boolean; + } + Foo["whatever"] = "foo"; + new Foo()["something"] = true; \ No newline at end of file diff --git a/javascript/ql/test/library-tests/TypeScript/RegressionTests/ImportSelf/test.expected b/javascript/ql/test/library-tests/TypeScript/RegressionTests/ImportSelf/test.expected index 200517fd84d..6e38d3a0038 100644 --- a/javascript/ql/test/library-tests/TypeScript/RegressionTests/ImportSelf/test.expected +++ b/javascript/ql/test/library-tests/TypeScript/RegressionTests/ImportSelf/test.expected @@ -2,11 +2,3 @@ | bar.ts:1:10:1:10 | A | any | | bar.ts:1:19:1:29 | "@blah/foo" | any | | bar.ts:3:5:3:5 | x | any | -| node_modules/@blah/foo/index.d.ts:1:8:1:15 | FooArray | typeof FooArray in library-tests/TypeScript/RegressionTests/ImportSelf/node_modules/@blah/foo/index.d.ts | -| node_modules/@blah/foo/index.d.ts:1:20:1:20 | A | any | -| node_modules/@blah/foo/index.d.ts:1:20:1:20 | A | any | -| node_modules/@blah/foo/index.d.ts:1:29:1:39 | '@blah/foo' | any | -| node_modules/@blah/foo/index.d.ts:3:11:3:18 | FooArray | typeof FooArray in library-tests/TypeScript/RegressionTests/ImportSelf/node_modules/@blah/foo/index.d.ts | -| node_modules/@blah/foo/index.d.ts:4:12:4:15 | blah | () => void | -| node_modules/@blah/foo/index.d.ts:8:10:8:10 | A | any | -| node_modules/@blah/foo/index.d.ts:8:10:8:10 | A | any | diff --git a/javascript/ql/test/library-tests/TypeScript/RegressionTests/TemplateTypes/TemplateTypes.expected b/javascript/ql/test/library-tests/TypeScript/RegressionTests/TemplateTypes/TemplateTypes.expected new file mode 100644 index 00000000000..e713b3e4804 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/RegressionTests/TemplateTypes/TemplateTypes.expected @@ -0,0 +1,6 @@ +| tst.ts:2:11:2:21 | `foo ${T1}` | +| tst.ts:4:45:4:49 | `foo` | +| tst.ts:4:53:4:57 | `bar` | +| tst.ts:5:46:5:50 | `foo` | +| tst.ts:5:54:5:63 | `bar ${K}` | +| tst.ts:7:15:7:19 | `foo` | diff --git a/javascript/ql/test/library-tests/TypeScript/RegressionTests/TemplateTypes/TemplateTypes.ql b/javascript/ql/test/library-tests/TypeScript/RegressionTests/TemplateTypes/TemplateTypes.ql new file mode 100644 index 00000000000..f08f5a201dd --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/RegressionTests/TemplateTypes/TemplateTypes.ql @@ -0,0 +1,3 @@ +import javascript + +query TemplateLiteralTypeExpr literalType() { any() } diff --git a/javascript/ql/test/library-tests/TypeScript/RegressionTests/TemplateTypes/tsconfig.json b/javascript/ql/test/library-tests/TypeScript/RegressionTests/TemplateTypes/tsconfig.json new file mode 100644 index 00000000000..82194fc7ab0 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/RegressionTests/TemplateTypes/tsconfig.json @@ -0,0 +1,3 @@ +{ + "include": ["."] +} diff --git a/javascript/ql/test/library-tests/TypeScript/RegressionTests/TemplateTypes/tst.ts b/javascript/ql/test/library-tests/TypeScript/RegressionTests/TemplateTypes/tst.ts new file mode 100644 index 00000000000..21718ea3b34 --- /dev/null +++ b/javascript/ql/test/library-tests/TypeScript/RegressionTests/TemplateTypes/tst.ts @@ -0,0 +1,7 @@ +type T1 = 'foo' | 'bar'; +type T2 = `foo ${T1}`; + +type FooToBar = K extends `foo` ? `bar` : K; +type FooToBar2 = K extends `foo` ? `bar ${K}` : K; + +type Tuple = [`foo`?]; diff --git a/javascript/ql/test/library-tests/TypeScript/RegressionTests/TraceResolution/test.expected b/javascript/ql/test/library-tests/TypeScript/RegressionTests/TraceResolution/test.expected index ec7a2343e6b..dc21f343569 100644 --- a/javascript/ql/test/library-tests/TypeScript/RegressionTests/TraceResolution/test.expected +++ b/javascript/ql/test/library-tests/TypeScript/RegressionTests/TraceResolution/test.expected @@ -1,4 +1,3 @@ -| node_modules/@types/foo/index.d.ts:1:17:1:19 | foo | () => any | | test.ts:1:10:1:12 | foo | () => any | | test.ts:1:10:1:12 | foo | () => any | | test.ts:1:21:1:25 | "foo" | any | diff --git a/javascript/ql/test/library-tests/TypeScript/Types/dummy.ts b/javascript/ql/test/library-tests/TypeScript/Types/dummy.ts index 77f17538e18..d36be434986 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/dummy.ts +++ b/javascript/ql/test/library-tests/TypeScript/Types/dummy.ts @@ -1,2 +1,4 @@ // Dummy file to be imported so the other files are seen as modules. export let x = 5; + +export let reg = /ab+c/; \ No newline at end of file diff --git a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected index bd50eaa28ec..d05b3228025 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected +++ b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected @@ -74,6 +74,23 @@ nodes | dummy.ts:2:12:2:12 | [VarDecl] x | semmle.label | [VarDecl] x | | dummy.ts:2:12:2:16 | [VariableDeclarator] x = 5 | semmle.label | [VariableDeclarator] x = 5 | | dummy.ts:2:16:2:16 | [Literal] 5 | semmle.label | [Literal] 5 | +| dummy.ts:4:1:4:24 | [ExportDeclaration] export ... /ab+c/; | semmle.label | [ExportDeclaration] export ... /ab+c/; | +| dummy.ts:4:1:4:24 | [ExportDeclaration] export ... /ab+c/; | semmle.order | 13 | +| dummy.ts:4:8:4:24 | [DeclStmt] let reg = ... | semmle.label | [DeclStmt] let reg = ... | +| dummy.ts:4:12:4:14 | [VarDecl] reg | semmle.label | [VarDecl] reg | +| dummy.ts:4:12:4:23 | [VariableDeclarator] reg = /ab+c/ | semmle.label | [VariableDeclarator] reg = /ab+c/ | +| dummy.ts:4:18:4:23 | [RegExpLiteral] /ab+c/ | semmle.label | [RegExpLiteral] /ab+c/ | +| dummy.ts:4:19:4:19 | [RegExpNormalConstant] a | semmle.label | [RegExpNormalConstant] a | +| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | semmle.label | [RegExpSequence] ab+c | +| dummy.ts:4:20:4:20 | [RegExpNormalConstant] b | semmle.label | [RegExpNormalConstant] b | +| dummy.ts:4:20:4:21 | [RegExpPlus] b+ | semmle.label | [RegExpPlus] b+ | +| dummy.ts:4:22:4:22 | [RegExpNormalConstant] c | semmle.label | [RegExpNormalConstant] c | +| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | +| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | +| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | +| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | +| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | +| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | @@ -85,7 +102,7 @@ nodes | file://:0:0:0:0 | (TypeParameters) | semmle.label | (TypeParameters) | | file://:0:0:0:0 | (TypeParameters) | semmle.label | (TypeParameters) | | middle-rest.ts:1:1:1:40 | [DeclStmt] let foo = ... | semmle.label | [DeclStmt] let foo = ... | -| middle-rest.ts:1:1:1:40 | [DeclStmt] let foo = ... | semmle.order | 13 | +| middle-rest.ts:1:1:1:40 | [DeclStmt] let foo = ... | semmle.order | 14 | | middle-rest.ts:1:5:1:7 | [VarDecl] foo | semmle.label | [VarDecl] foo | | middle-rest.ts:1:5:1:39 | [VariableDeclarator] foo: [b ... number] | semmle.label | [VariableDeclarator] foo: [b ... number] | | middle-rest.ts:1:10:1:39 | [TupleTypeExpr] [boolea ... number] | semmle.label | [TupleTypeExpr] [boolea ... number] | @@ -97,55 +114,55 @@ nodes | middle-rest.ts:3:1:3:3 | [VarRef] foo | semmle.label | [VarRef] foo | | middle-rest.ts:3:1:3:26 | [AssignExpr] foo = [ ... ", 123] | semmle.label | [AssignExpr] foo = [ ... ", 123] | | middle-rest.ts:3:1:3:27 | [ExprStmt] foo = [ ... , 123]; | semmle.label | [ExprStmt] foo = [ ... , 123]; | -| middle-rest.ts:3:1:3:27 | [ExprStmt] foo = [ ... , 123]; | semmle.order | 14 | +| middle-rest.ts:3:1:3:27 | [ExprStmt] foo = [ ... , 123]; | semmle.order | 15 | | middle-rest.ts:3:7:3:26 | [ArrayExpr] [true, "hello", 123] | semmle.label | [ArrayExpr] [true, "hello", 123] | | middle-rest.ts:3:8:3:11 | [Literal] true | semmle.label | [Literal] true | | middle-rest.ts:3:14:3:20 | [Literal] "hello" | semmle.label | [Literal] "hello" | | middle-rest.ts:3:23:3:25 | [Literal] 123 | semmle.label | [Literal] 123 | | tst.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.label | [ImportDeclaration] import ... dummy"; | -| tst.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 15 | +| tst.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 16 | | tst.ts:1:8:1:17 | [ImportSpecifier] * as dummy | semmle.label | [ImportSpecifier] * as dummy | | tst.ts:1:13:1:17 | [VarDecl] dummy | semmle.label | [VarDecl] dummy | | tst.ts:1:24:1:32 | [Literal] "./dummy" | semmle.label | [Literal] "./dummy" | | tst.ts:3:1:3:19 | [DeclStmt] var numVar = ... | semmle.label | [DeclStmt] var numVar = ... | -| tst.ts:3:1:3:19 | [DeclStmt] var numVar = ... | semmle.order | 16 | +| tst.ts:3:1:3:19 | [DeclStmt] var numVar = ... | semmle.order | 17 | | tst.ts:3:5:3:10 | [VarDecl] numVar | semmle.label | [VarDecl] numVar | | tst.ts:3:5:3:18 | [VariableDeclarator] numVar: number | semmle.label | [VariableDeclarator] numVar: number | | tst.ts:3:13:3:18 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:5:1:5:18 | [DeclStmt] var num1 = ... | semmle.label | [DeclStmt] var num1 = ... | -| tst.ts:5:1:5:18 | [DeclStmt] var num1 = ... | semmle.order | 17 | +| tst.ts:5:1:5:18 | [DeclStmt] var num1 = ... | semmle.order | 18 | | tst.ts:5:5:5:8 | [VarDecl] num1 | semmle.label | [VarDecl] num1 | | tst.ts:5:5:5:17 | [VariableDeclarator] num1 = numVar | semmle.label | [VariableDeclarator] num1 = numVar | | tst.ts:5:12:5:17 | [VarRef] numVar | semmle.label | [VarRef] numVar | | tst.ts:6:1:6:13 | [DeclStmt] var num2 = ... | semmle.label | [DeclStmt] var num2 = ... | -| tst.ts:6:1:6:13 | [DeclStmt] var num2 = ... | semmle.order | 18 | +| tst.ts:6:1:6:13 | [DeclStmt] var num2 = ... | semmle.order | 19 | | tst.ts:6:5:6:8 | [VarDecl] num2 | semmle.label | [VarDecl] num2 | | tst.ts:6:5:6:12 | [VariableDeclarator] num2 = 5 | semmle.label | [VariableDeclarator] num2 = 5 | | tst.ts:6:12:6:12 | [Literal] 5 | semmle.label | [Literal] 5 | | tst.ts:7:1:7:23 | [DeclStmt] var num3 = ... | semmle.label | [DeclStmt] var num3 = ... | -| tst.ts:7:1:7:23 | [DeclStmt] var num3 = ... | semmle.order | 19 | +| tst.ts:7:1:7:23 | [DeclStmt] var num3 = ... | semmle.order | 20 | | tst.ts:7:5:7:8 | [VarDecl] num3 | semmle.label | [VarDecl] num3 | | tst.ts:7:5:7:22 | [VariableDeclarator] num3 = num1 + num2 | semmle.label | [VariableDeclarator] num3 = num1 + num2 | | tst.ts:7:12:7:15 | [VarRef] num1 | semmle.label | [VarRef] num1 | | tst.ts:7:12:7:22 | [BinaryExpr] num1 + num2 | semmle.label | [BinaryExpr] num1 + num2 | | tst.ts:7:19:7:22 | [VarRef] num2 | semmle.label | [VarRef] num2 | | tst.ts:9:1:9:19 | [DeclStmt] var strVar = ... | semmle.label | [DeclStmt] var strVar = ... | -| tst.ts:9:1:9:19 | [DeclStmt] var strVar = ... | semmle.order | 20 | +| tst.ts:9:1:9:19 | [DeclStmt] var strVar = ... | semmle.order | 21 | | tst.ts:9:5:9:10 | [VarDecl] strVar | semmle.label | [VarDecl] strVar | | tst.ts:9:5:9:18 | [VariableDeclarator] strVar: string | semmle.label | [VariableDeclarator] strVar: string | | tst.ts:9:13:9:18 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:10:1:10:20 | [DeclStmt] var hello = ... | semmle.label | [DeclStmt] var hello = ... | -| tst.ts:10:1:10:20 | [DeclStmt] var hello = ... | semmle.order | 21 | +| tst.ts:10:1:10:20 | [DeclStmt] var hello = ... | semmle.order | 22 | | tst.ts:10:5:10:9 | [VarDecl] hello | semmle.label | [VarDecl] hello | | tst.ts:10:5:10:19 | [VariableDeclarator] hello = "hello" | semmle.label | [VariableDeclarator] hello = "hello" | | tst.ts:10:13:10:19 | [Literal] "hello" | semmle.label | [Literal] "hello" | | tst.ts:11:1:11:20 | [DeclStmt] var world = ... | semmle.label | [DeclStmt] var world = ... | -| tst.ts:11:1:11:20 | [DeclStmt] var world = ... | semmle.order | 22 | +| tst.ts:11:1:11:20 | [DeclStmt] var world = ... | semmle.order | 23 | | tst.ts:11:5:11:9 | [VarDecl] world | semmle.label | [VarDecl] world | | tst.ts:11:5:11:19 | [VariableDeclarator] world = "world" | semmle.label | [VariableDeclarator] world = "world" | | tst.ts:11:13:11:19 | [Literal] "world" | semmle.label | [Literal] "world" | | tst.ts:12:1:12:30 | [DeclStmt] var msg = ... | semmle.label | [DeclStmt] var msg = ... | -| tst.ts:12:1:12:30 | [DeclStmt] var msg = ... | semmle.order | 23 | +| tst.ts:12:1:12:30 | [DeclStmt] var msg = ... | semmle.order | 24 | | tst.ts:12:5:12:7 | [VarDecl] msg | semmle.label | [VarDecl] msg | | tst.ts:12:5:12:29 | [VariableDeclarator] msg = h ... + world | semmle.label | [VariableDeclarator] msg = h ... + world | | tst.ts:12:11:12:15 | [VarRef] hello | semmle.label | [VarRef] hello | @@ -154,7 +171,7 @@ nodes | tst.ts:12:19:12:21 | [Literal] " " | semmle.label | [Literal] " " | | tst.ts:12:25:12:29 | [VarRef] world | semmle.label | [VarRef] world | | tst.ts:14:1:14:63 | [FunctionDeclStmt] functio ... + y; } | semmle.label | [FunctionDeclStmt] functio ... + y; } | -| tst.ts:14:1:14:63 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 24 | +| tst.ts:14:1:14:63 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 25 | | tst.ts:14:10:14:15 | [VarDecl] concat | semmle.label | [VarDecl] concat | | tst.ts:14:17:14:17 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | | tst.ts:14:20:14:25 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | @@ -167,7 +184,7 @@ nodes | tst.ts:14:56:14:60 | [BinaryExpr] x + y | semmle.label | [BinaryExpr] x + y | | tst.ts:14:60:14:60 | [VarRef] y | semmle.label | [VarRef] y | | tst.ts:16:1:16:60 | [FunctionDeclStmt] functio ... + y; } | semmle.label | [FunctionDeclStmt] functio ... + y; } | -| tst.ts:16:1:16:60 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 25 | +| tst.ts:16:1:16:60 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 26 | | tst.ts:16:10:16:12 | [VarDecl] add | semmle.label | [VarDecl] add | | tst.ts:16:14:16:14 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | | tst.ts:16:17:16:22 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | @@ -180,7 +197,7 @@ nodes | tst.ts:16:53:16:57 | [BinaryExpr] x + y | semmle.label | [BinaryExpr] x + y | | tst.ts:16:57:16:57 | [VarRef] y | semmle.label | [VarRef] y | | tst.ts:18:1:18:40 | [FunctionDeclStmt] functio ... + y; } | semmle.label | [FunctionDeclStmt] functio ... + y; } | -| tst.ts:18:1:18:40 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 26 | +| tst.ts:18:1:18:40 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 27 | | tst.ts:18:10:18:16 | [VarDecl] untyped | semmle.label | [VarDecl] untyped | | tst.ts:18:18:18:18 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | | tst.ts:18:21:18:21 | [SimpleParameter] y | semmle.label | [SimpleParameter] y | @@ -190,7 +207,7 @@ nodes | tst.ts:18:33:18:37 | [BinaryExpr] x + y | semmle.label | [BinaryExpr] x + y | | tst.ts:18:37:18:37 | [VarRef] y | semmle.label | [VarRef] y | | tst.ts:20:1:20:53 | [FunctionDeclStmt] functio ... + y; } | semmle.label | [FunctionDeclStmt] functio ... + y; } | -| tst.ts:20:1:20:53 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 27 | +| tst.ts:20:1:20:53 | [FunctionDeclStmt] functio ... + y; } | semmle.order | 28 | | tst.ts:20:10:20:21 | [VarDecl] partialTyped | semmle.label | [VarDecl] partialTyped | | tst.ts:20:23:20:23 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | | tst.ts:20:26:20:26 | [SimpleParameter] y | semmle.label | [SimpleParameter] y | @@ -201,7 +218,7 @@ nodes | tst.ts:20:46:20:50 | [BinaryExpr] x + y | semmle.label | [BinaryExpr] x + y | | tst.ts:20:50:20:50 | [VarRef] y | semmle.label | [VarRef] y | | tst.ts:22:1:22:34 | [ForOfStmt] for (le ... 2]) {} | semmle.label | [ForOfStmt] for (le ... 2]) {} | -| tst.ts:22:1:22:34 | [ForOfStmt] for (le ... 2]) {} | semmle.order | 28 | +| tst.ts:22:1:22:34 | [ForOfStmt] for (le ... 2]) {} | semmle.order | 29 | | tst.ts:22:6:22:20 | [DeclStmt] let numFromLoop = ... | semmle.label | [DeclStmt] let numFromLoop = ... | | tst.ts:22:10:22:20 | [VarDecl] numFromLoop | semmle.label | [VarDecl] numFromLoop | | tst.ts:22:10:22:20 | [VariableDeclarator] numFromLoop | semmle.label | [VariableDeclarator] numFromLoop | @@ -210,54 +227,54 @@ nodes | tst.ts:22:29:22:29 | [Literal] 2 | semmle.label | [Literal] 2 | | tst.ts:22:33:22:34 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | | tst.ts:24:1:24:20 | [DeclStmt] let array = ... | semmle.label | [DeclStmt] let array = ... | -| tst.ts:24:1:24:20 | [DeclStmt] let array = ... | semmle.order | 29 | +| tst.ts:24:1:24:20 | [DeclStmt] let array = ... | semmle.order | 30 | | tst.ts:24:5:24:9 | [VarDecl] array | semmle.label | [VarDecl] array | | tst.ts:24:5:24:19 | [VariableDeclarator] array: number[] | semmle.label | [VariableDeclarator] array: number[] | | tst.ts:24:12:24:17 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:24:12:24:19 | [ArrayTypeExpr] number[] | semmle.label | [ArrayTypeExpr] number[] | | tst.ts:26:1:26:25 | [DeclStmt] let voidType = ... | semmle.label | [DeclStmt] let voidType = ... | -| tst.ts:26:1:26:25 | [DeclStmt] let voidType = ... | semmle.order | 30 | +| tst.ts:26:1:26:25 | [DeclStmt] let voidType = ... | semmle.order | 31 | | tst.ts:26:5:26:12 | [VarDecl] voidType | semmle.label | [VarDecl] voidType | | tst.ts:26:5:26:24 | [VariableDeclarator] voidType: () => void | semmle.label | [VariableDeclarator] voidType: () => void | | tst.ts:26:15:26:24 | [FunctionExpr] () => void | semmle.label | [FunctionExpr] () => void | | tst.ts:26:15:26:24 | [FunctionTypeExpr] () => void | semmle.label | [FunctionTypeExpr] () => void | | tst.ts:26:21:26:24 | [KeywordTypeExpr] void | semmle.label | [KeywordTypeExpr] void | | tst.ts:27:1:27:29 | [DeclStmt] let undefinedType = ... | semmle.label | [DeclStmt] let undefinedType = ... | -| tst.ts:27:1:27:29 | [DeclStmt] let undefinedType = ... | semmle.order | 31 | +| tst.ts:27:1:27:29 | [DeclStmt] let undefinedType = ... | semmle.order | 32 | | tst.ts:27:5:27:17 | [VarDecl] undefinedType | semmle.label | [VarDecl] undefinedType | | tst.ts:27:5:27:28 | [VariableDeclarator] undefin ... defined | semmle.label | [VariableDeclarator] undefin ... defined | | tst.ts:27:20:27:28 | [KeywordTypeExpr] undefined | semmle.label | [KeywordTypeExpr] undefined | | tst.ts:28:1:28:26 | [DeclStmt] let nullType = ... | semmle.label | [DeclStmt] let nullType = ... | -| tst.ts:28:1:28:26 | [DeclStmt] let nullType = ... | semmle.order | 32 | +| tst.ts:28:1:28:26 | [DeclStmt] let nullType = ... | semmle.order | 33 | | tst.ts:28:5:28:12 | [VarDecl] nullType | semmle.label | [VarDecl] nullType | | tst.ts:28:5:28:25 | [VariableDeclarator] nullTyp ... = null | semmle.label | [VariableDeclarator] nullTyp ... = null | | tst.ts:28:15:28:18 | [KeywordTypeExpr] null | semmle.label | [KeywordTypeExpr] null | | tst.ts:28:22:28:25 | [Literal] null | semmle.label | [Literal] null | | tst.ts:29:1:29:27 | [DeclStmt] let neverType = ... | semmle.label | [DeclStmt] let neverType = ... | -| tst.ts:29:1:29:27 | [DeclStmt] let neverType = ... | semmle.order | 33 | +| tst.ts:29:1:29:27 | [DeclStmt] let neverType = ... | semmle.order | 34 | | tst.ts:29:5:29:13 | [VarDecl] neverType | semmle.label | [VarDecl] neverType | | tst.ts:29:5:29:26 | [VariableDeclarator] neverTy ... > never | semmle.label | [VariableDeclarator] neverTy ... > never | | tst.ts:29:16:29:26 | [FunctionExpr] () => never | semmle.label | [FunctionExpr] () => never | | tst.ts:29:16:29:26 | [FunctionTypeExpr] () => never | semmle.label | [FunctionTypeExpr] () => never | | tst.ts:29:22:29:26 | [KeywordTypeExpr] never | semmle.label | [KeywordTypeExpr] never | | tst.ts:30:1:30:23 | [DeclStmt] let symbolType = ... | semmle.label | [DeclStmt] let symbolType = ... | -| tst.ts:30:1:30:23 | [DeclStmt] let symbolType = ... | semmle.order | 34 | +| tst.ts:30:1:30:23 | [DeclStmt] let symbolType = ... | semmle.order | 35 | | tst.ts:30:5:30:14 | [VarDecl] symbolType | semmle.label | [VarDecl] symbolType | | tst.ts:30:5:30:22 | [VariableDeclarator] symbolType: symbol | semmle.label | [VariableDeclarator] symbolType: symbol | | tst.ts:30:17:30:22 | [KeywordTypeExpr] symbol | semmle.label | [KeywordTypeExpr] symbol | | tst.ts:31:1:31:45 | [DeclStmt] const uniqueSymbolType = ... | semmle.label | [DeclStmt] const uniqueSymbolType = ... | -| tst.ts:31:1:31:45 | [DeclStmt] const uniqueSymbolType = ... | semmle.order | 35 | +| tst.ts:31:1:31:45 | [DeclStmt] const uniqueSymbolType = ... | semmle.order | 36 | | tst.ts:31:7:31:22 | [VarDecl] uniqueSymbolType | semmle.label | [VarDecl] uniqueSymbolType | | tst.ts:31:7:31:44 | [VariableDeclarator] uniqueS ... = null | semmle.label | [VariableDeclarator] uniqueS ... = null | | tst.ts:31:25:31:37 | [KeywordTypeExpr] unique symbol | semmle.label | [KeywordTypeExpr] unique symbol | | tst.ts:31:41:31:44 | [Literal] null | semmle.label | [Literal] null | | tst.ts:32:1:32:23 | [DeclStmt] let objectType = ... | semmle.label | [DeclStmt] let objectType = ... | -| tst.ts:32:1:32:23 | [DeclStmt] let objectType = ... | semmle.order | 36 | +| tst.ts:32:1:32:23 | [DeclStmt] let objectType = ... | semmle.order | 37 | | tst.ts:32:5:32:14 | [VarDecl] objectType | semmle.label | [VarDecl] objectType | | tst.ts:32:5:32:22 | [VariableDeclarator] objectType: object | semmle.label | [VariableDeclarator] objectType: object | | tst.ts:32:17:32:22 | [KeywordTypeExpr] object | semmle.label | [KeywordTypeExpr] object | | tst.ts:33:1:33:39 | [DeclStmt] let intersection = ... | semmle.label | [DeclStmt] let intersection = ... | -| tst.ts:33:1:33:39 | [DeclStmt] let intersection = ... | semmle.order | 37 | +| tst.ts:33:1:33:39 | [DeclStmt] let intersection = ... | semmle.order | 38 | | tst.ts:33:5:33:16 | [VarDecl] intersection | semmle.label | [VarDecl] intersection | | tst.ts:33:5:33:38 | [VariableDeclarator] interse ... string} | semmle.label | [VariableDeclarator] interse ... string} | | tst.ts:33:19:33:24 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | @@ -267,14 +284,14 @@ nodes | tst.ts:33:29:33:37 | [FieldDeclaration] x: string | semmle.label | [FieldDeclaration] x: string | | tst.ts:33:32:33:37 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:34:1:34:28 | [DeclStmt] let tuple = ... | semmle.label | [DeclStmt] let tuple = ... | -| tst.ts:34:1:34:28 | [DeclStmt] let tuple = ... | semmle.order | 38 | +| tst.ts:34:1:34:28 | [DeclStmt] let tuple = ... | semmle.order | 39 | | tst.ts:34:5:34:9 | [VarDecl] tuple | semmle.label | [VarDecl] tuple | | tst.ts:34:5:34:27 | [VariableDeclarator] tuple: ... string] | semmle.label | [VariableDeclarator] tuple: ... string] | | tst.ts:34:12:34:27 | [TupleTypeExpr] [number, string] | semmle.label | [TupleTypeExpr] [number, string] | | tst.ts:34:13:34:18 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:34:21:34:26 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:36:1:36:56 | [DeclStmt] let tupleWithOptionalElement = ... | semmle.label | [DeclStmt] let tupleWithOptionalElement = ... | -| tst.ts:36:1:36:56 | [DeclStmt] let tupleWithOptionalElement = ... | semmle.order | 39 | +| tst.ts:36:1:36:56 | [DeclStmt] let tupleWithOptionalElement = ... | semmle.order | 40 | | tst.ts:36:5:36:28 | [VarDecl] tupleWithOptionalElement | semmle.label | [VarDecl] tupleWithOptionalElement | | tst.ts:36:5:36:55 | [VariableDeclarator] tupleWi ... umber?] | semmle.label | [VariableDeclarator] tupleWi ... umber?] | | tst.ts:36:31:36:55 | [TupleTypeExpr] [number ... umber?] | semmle.label | [TupleTypeExpr] [number ... umber?] | @@ -283,12 +300,12 @@ nodes | tst.ts:36:48:36:53 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:36:48:36:54 | [OptionalTypeExpr] number? | semmle.label | [OptionalTypeExpr] number? | | tst.ts:37:1:37:19 | [DeclStmt] let emptyTuple = ... | semmle.label | [DeclStmt] let emptyTuple = ... | -| tst.ts:37:1:37:19 | [DeclStmt] let emptyTuple = ... | semmle.order | 40 | +| tst.ts:37:1:37:19 | [DeclStmt] let emptyTuple = ... | semmle.order | 41 | | tst.ts:37:5:37:14 | [VarDecl] emptyTuple | semmle.label | [VarDecl] emptyTuple | | tst.ts:37:5:37:18 | [VariableDeclarator] emptyTuple: [] | semmle.label | [VariableDeclarator] emptyTuple: [] | | tst.ts:37:17:37:18 | [TupleTypeExpr] [] | semmle.label | [TupleTypeExpr] [] | | tst.ts:38:1:38:48 | [DeclStmt] let tupleWithRestElement = ... | semmle.label | [DeclStmt] let tupleWithRestElement = ... | -| tst.ts:38:1:38:48 | [DeclStmt] let tupleWithRestElement = ... | semmle.order | 41 | +| tst.ts:38:1:38:48 | [DeclStmt] let tupleWithRestElement = ... | semmle.order | 42 | | tst.ts:38:5:38:24 | [VarDecl] tupleWithRestElement | semmle.label | [VarDecl] tupleWithRestElement | | tst.ts:38:5:38:47 | [VariableDeclarator] tupleWi ... ring[]] | semmle.label | [VariableDeclarator] tupleWi ... ring[]] | | tst.ts:38:27:38:47 | [TupleTypeExpr] [number ... ring[]] | semmle.label | [TupleTypeExpr] [number ... ring[]] | @@ -297,7 +314,7 @@ nodes | tst.ts:38:39:38:44 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:38:39:38:46 | [ArrayTypeExpr] string[] | semmle.label | [ArrayTypeExpr] string[] | | tst.ts:39:1:39:69 | [DeclStmt] let tupleWithOptionalAndRestElements = ... | semmle.label | [DeclStmt] let tupleWithOptionalAndRestElements = ... | -| tst.ts:39:1:39:69 | [DeclStmt] let tupleWithOptionalAndRestElements = ... | semmle.order | 42 | +| tst.ts:39:1:39:69 | [DeclStmt] let tupleWithOptionalAndRestElements = ... | semmle.order | 43 | | tst.ts:39:5:39:36 | [VarDecl] tupleWithOptionalAndRestElements | semmle.label | [VarDecl] tupleWithOptionalAndRestElements | | tst.ts:39:5:39:68 | [VariableDeclarator] tupleWi ... mber[]] | semmle.label | [VariableDeclarator] tupleWi ... mber[]] | | tst.ts:39:39:39:68 | [TupleTypeExpr] [number ... mber[]] | semmle.label | [TupleTypeExpr] [number ... mber[]] | @@ -308,12 +325,12 @@ nodes | tst.ts:39:60:39:65 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:39:60:39:67 | [ArrayTypeExpr] number[] | semmle.label | [ArrayTypeExpr] number[] | | tst.ts:40:1:40:25 | [DeclStmt] let unknownType = ... | semmle.label | [DeclStmt] let unknownType = ... | -| tst.ts:40:1:40:25 | [DeclStmt] let unknownType = ... | semmle.order | 43 | +| tst.ts:40:1:40:25 | [DeclStmt] let unknownType = ... | semmle.order | 44 | | tst.ts:40:5:40:15 | [VarDecl] unknownType | semmle.label | [VarDecl] unknownType | | tst.ts:40:5:40:24 | [VariableDeclarator] unknownType: unknown | semmle.label | [VariableDeclarator] unknownType: unknown | | tst.ts:40:18:40:24 | [KeywordTypeExpr] unknown | semmle.label | [KeywordTypeExpr] unknown | | tst.ts:42:1:42:40 | [DeclStmt] let constArrayLiteral = ... | semmle.label | [DeclStmt] let constArrayLiteral = ... | -| tst.ts:42:1:42:40 | [DeclStmt] let constArrayLiteral = ... | semmle.order | 44 | +| tst.ts:42:1:42:40 | [DeclStmt] let constArrayLiteral = ... | semmle.order | 45 | | tst.ts:42:5:42:21 | [VarDecl] constArrayLiteral | semmle.label | [VarDecl] constArrayLiteral | | tst.ts:42:5:42:39 | [VariableDeclarator] constAr ... s const | semmle.label | [VariableDeclarator] constAr ... s const | | tst.ts:42:25:42:30 | [ArrayExpr] [1, 2] | semmle.label | [ArrayExpr] [1, 2] | @@ -322,7 +339,7 @@ nodes | tst.ts:42:29:42:29 | [Literal] 2 | semmle.label | [Literal] 2 | | tst.ts:42:35:42:39 | [KeywordTypeExpr] const | semmle.label | [KeywordTypeExpr] const | | tst.ts:43:1:43:49 | [DeclStmt] let constObjectLiteral = ... | semmle.label | [DeclStmt] let constObjectLiteral = ... | -| tst.ts:43:1:43:49 | [DeclStmt] let constObjectLiteral = ... | semmle.order | 45 | +| tst.ts:43:1:43:49 | [DeclStmt] let constObjectLiteral = ... | semmle.order | 46 | | tst.ts:43:5:43:22 | [VarDecl] constObjectLiteral | semmle.label | [VarDecl] constObjectLiteral | | tst.ts:43:5:43:48 | [VariableDeclarator] constOb ... s const | semmle.label | [VariableDeclarator] constOb ... s const | | tst.ts:43:26:43:39 | [ObjectExpr] {foo: ...} | semmle.label | [ObjectExpr] {foo: ...} | @@ -332,7 +349,7 @@ nodes | tst.ts:43:33:43:37 | [Literal] "foo" | semmle.label | [Literal] "foo" | | tst.ts:43:44:43:48 | [KeywordTypeExpr] const | semmle.label | [KeywordTypeExpr] const | | tst.ts:46:1:51:1 | [TryStmt] try { } ... ; } } | semmle.label | [TryStmt] try { } ... ; } } | -| tst.ts:46:1:51:1 | [TryStmt] try { } ... ; } } | semmle.order | 46 | +| tst.ts:46:1:51:1 | [TryStmt] try { } ... ; } } | semmle.order | 47 | | tst.ts:46:5:46:7 | [BlockStmt] { } | semmle.label | [BlockStmt] { } | | tst.ts:47:1:51:1 | [CatchClause] catch ( ... ; } } | semmle.label | [CatchClause] catch ( ... ; } } | | tst.ts:47:8:47:8 | [SimpleParameter] e | semmle.label | [SimpleParameter] e | @@ -349,21 +366,21 @@ nodes | tst.ts:49:15:49:20 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:49:24:49:24 | [VarRef] e | semmle.label | [VarRef] e | | tst.ts:54:1:56:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | -| tst.ts:54:1:56:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.order | 47 | +| tst.ts:54:1:56:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.order | 48 | | tst.ts:54:11:54:26 | [Identifier] NonAbstractDummy | semmle.label | [Identifier] NonAbstractDummy | | tst.ts:55:3:55:9 | [Label] getArea | semmle.label | [Label] getArea | | tst.ts:55:3:55:20 | [FunctionExpr] getArea(): number; | semmle.label | [FunctionExpr] getArea(): number; | | tst.ts:55:3:55:20 | [MethodSignature] getArea(): number; | semmle.label | [MethodSignature] getArea(): number; | | tst.ts:55:14:55:19 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:58:1:60:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | -| tst.ts:58:1:60:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.order | 48 | +| tst.ts:58:1:60:1 | [InterfaceDeclaration,TypeDefinition] interfa ... mber; } | semmle.order | 49 | | tst.ts:58:11:58:17 | [Identifier] HasArea | semmle.label | [Identifier] HasArea | | tst.ts:59:3:59:9 | [Label] getArea | semmle.label | [Label] getArea | | tst.ts:59:3:59:20 | [FunctionExpr] getArea(): number; | semmle.label | [FunctionExpr] getArea(): number; | | tst.ts:59:3:59:20 | [MethodSignature] getArea(): number; | semmle.label | [MethodSignature] getArea(): number; | | tst.ts:59:14:59:19 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | tst.ts:63:1:63:45 | [DeclStmt] let Ctor = ... | semmle.label | [DeclStmt] let Ctor = ... | -| tst.ts:63:1:63:45 | [DeclStmt] let Ctor = ... | semmle.order | 49 | +| tst.ts:63:1:63:45 | [DeclStmt] let Ctor = ... | semmle.order | 50 | | tst.ts:63:5:63:8 | [VarDecl] Ctor | semmle.label | [VarDecl] Ctor | | tst.ts:63:5:63:44 | [VariableDeclarator] Ctor: a ... = Shape | semmle.label | [VariableDeclarator] Ctor: a ... = Shape | | tst.ts:63:11:63:36 | [FunctionExpr] abstrac ... HasArea | semmle.label | [FunctionExpr] abstrac ... HasArea | @@ -371,7 +388,7 @@ nodes | tst.ts:63:30:63:36 | [LocalTypeAccess] HasArea | semmle.label | [LocalTypeAccess] HasArea | | tst.ts:63:40:63:44 | [VarRef] Shape | semmle.label | [VarRef] Shape | | tst.ts:65:1:65:54 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | -| tst.ts:65:1:65:54 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.order | 50 | +| tst.ts:65:1:65:54 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.order | 51 | | tst.ts:65:6:65:12 | [Identifier] MyUnion | semmle.label | [Identifier] MyUnion | | tst.ts:65:16:65:30 | [InterfaceTypeExpr] {myUnion: true} | semmle.label | [InterfaceTypeExpr] {myUnion: true} | | tst.ts:65:16:65:53 | [UnionTypeExpr] {myUnio ... : true} | semmle.label | [UnionTypeExpr] {myUnio ... : true} | @@ -383,7 +400,7 @@ nodes | tst.ts:65:35:65:52 | [FieldDeclaration] stillMyUnion: true | semmle.label | [FieldDeclaration] stillMyUnion: true | | tst.ts:65:49:65:52 | [LiteralTypeExpr] true | semmle.label | [LiteralTypeExpr] true | | tst.ts:66:1:66:38 | [DeclStmt] let union1 = ... | semmle.label | [DeclStmt] let union1 = ... | -| tst.ts:66:1:66:38 | [DeclStmt] let union1 = ... | semmle.order | 51 | +| tst.ts:66:1:66:38 | [DeclStmt] let union1 = ... | semmle.order | 52 | | tst.ts:66:5:66:10 | [VarDecl] union1 | semmle.label | [VarDecl] union1 | | tst.ts:66:5:66:37 | [VariableDeclarator] union1: ... : true} | semmle.label | [VariableDeclarator] union1: ... : true} | | tst.ts:66:13:66:19 | [LocalTypeAccess] MyUnion | semmle.label | [LocalTypeAccess] MyUnion | @@ -392,7 +409,7 @@ nodes | tst.ts:66:24:66:36 | [Property] myUnion: true | semmle.label | [Property] myUnion: true | | tst.ts:66:33:66:36 | [Literal] true | semmle.label | [Literal] true | | tst.ts:68:1:68:49 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | -| tst.ts:68:1:68:49 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.order | 52 | +| tst.ts:68:1:68:49 | [TypeAliasDeclaration,TypeDefinition] type My ... true}; | semmle.order | 53 | | tst.ts:68:6:68:13 | [Identifier] MyUnion2 | semmle.label | [Identifier] MyUnion2 | | tst.ts:68:17:68:23 | [LocalTypeAccess] MyUnion | semmle.label | [LocalTypeAccess] MyUnion | | tst.ts:68:17:68:48 | [UnionTypeExpr] MyUnion ... : true} | semmle.label | [UnionTypeExpr] MyUnion ... : true} | @@ -401,7 +418,7 @@ nodes | tst.ts:68:28:68:47 | [FieldDeclaration] yetAnotherType: true | semmle.label | [FieldDeclaration] yetAnotherType: true | | tst.ts:68:44:68:47 | [LiteralTypeExpr] true | semmle.label | [LiteralTypeExpr] true | | tst.ts:69:1:69:46 | [DeclStmt] let union2 = ... | semmle.label | [DeclStmt] let union2 = ... | -| tst.ts:69:1:69:46 | [DeclStmt] let union2 = ... | semmle.order | 53 | +| tst.ts:69:1:69:46 | [DeclStmt] let union2 = ... | semmle.order | 54 | | tst.ts:69:5:69:10 | [VarDecl] union2 | semmle.label | [VarDecl] union2 | | tst.ts:69:5:69:45 | [VariableDeclarator] union2: ... : true} | semmle.label | [VariableDeclarator] union2: ... : true} | | tst.ts:69:13:69:20 | [LocalTypeAccess] MyUnion2 | semmle.label | [LocalTypeAccess] MyUnion2 | @@ -409,17 +426,182 @@ nodes | tst.ts:69:25:69:38 | [Label] yetAnotherType | semmle.label | [Label] yetAnotherType | | tst.ts:69:25:69:44 | [Property] yetAnotherType: true | semmle.label | [Property] yetAnotherType: true | | tst.ts:69:41:69:44 | [Literal] true | semmle.label | [Literal] true | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | semmle.label | [NamespaceDeclaration] module ... } } | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | semmle.order | 55 | +| tst.ts:71:8:71:11 | [VarDecl] TS43 | semmle.label | [VarDecl] TS43 | +| tst.ts:73:3:76:3 | [InterfaceDeclaration,TypeDefinition] interfa ... n); } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... n); } | +| tst.ts:73:13:73:18 | [Identifier] ThingI | semmle.label | [Identifier] ThingI | +| tst.ts:74:5:74:22 | [FunctionExpr] get size(): number | semmle.label | [FunctionExpr] get size(): number | +| tst.ts:74:5:74:22 | [GetterMethodSignature] get size(): number | semmle.label | [GetterMethodSignature] get size(): number | +| tst.ts:74:9:74:12 | [Label] size | semmle.label | [Label] size | +| tst.ts:74:17:74:22 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | +| tst.ts:75:5:75:47 | [FunctionExpr] set siz ... olean); | semmle.label | [FunctionExpr] set siz ... olean); | +| tst.ts:75:5:75:47 | [SetterMethodSignature] set siz ... olean); | semmle.label | [SetterMethodSignature] set siz ... olean); | +| tst.ts:75:9:75:12 | [Label] size | semmle.label | [Label] size | +| tst.ts:75:14:75:18 | [SimpleParameter] value | semmle.label | [SimpleParameter] value | +| tst.ts:75:21:75:26 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | +| tst.ts:75:21:75:45 | [UnionTypeExpr] number ... boolean | semmle.label | [UnionTypeExpr] number ... boolean | +| tst.ts:75:30:75:35 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | +| tst.ts:75:39:75:45 | [KeywordTypeExpr] boolean | semmle.label | [KeywordTypeExpr] boolean | +| tst.ts:78:3:88:3 | [ExportDeclaration] export ... } } | semmle.label | [ExportDeclaration] export ... } } | +| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | semmle.label | [ClassDefinition,TypeDefinition] class T ... } } | +| tst.ts:78:16:78:20 | [VarDecl] Thing | semmle.label | [VarDecl] Thing | +| tst.ts:78:33:78:38 | [LocalTypeAccess] ThingI | semmle.label | [LocalTypeAccess] ThingI | +| tst.ts:78:40:78:39 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | +| tst.ts:78:40:78:39 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | [ClassInitializedMember,ConstructorDefinition] constructor() {} | +| tst.ts:78:40:78:39 | [FunctionExpr] () {} | semmle.label | [FunctionExpr] () {} | +| tst.ts:78:40:78:39 | [Label] constructor | semmle.label | [Label] constructor | +| tst.ts:79:5:79:9 | [Label] #size | semmle.label | [Label] #size | +| tst.ts:79:5:79:14 | [FieldDeclaration] #size = 0; | semmle.label | [FieldDeclaration] #size = 0; | +| tst.ts:79:13:79:13 | [Literal] 0 | semmle.label | [Literal] 0 | +| tst.ts:81:5:83:5 | [ClassInitializedMember,GetterMethodDefinition] get siz ... ; } | semmle.label | [ClassInitializedMember,GetterMethodDefinition] get siz ... ; } | +| tst.ts:81:5:83:5 | [FunctionExpr] get siz ... ; } | semmle.label | [FunctionExpr] get siz ... ; } | +| tst.ts:81:9:81:12 | [Label] size | semmle.label | [Label] size | +| tst.ts:81:17:81:22 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | +| tst.ts:81:24:83:5 | [BlockStmt] { ... ; } | semmle.label | [BlockStmt] { ... ; } | +| tst.ts:82:7:82:24 | [ReturnStmt] return this.#size; | semmle.label | [ReturnStmt] return this.#size; | +| tst.ts:82:14:82:17 | [ThisExpr] this | semmle.label | [ThisExpr] this | +| tst.ts:82:14:82:23 | [DotExpr] this.#size | semmle.label | [DotExpr] this.#size | +| tst.ts:82:19:82:23 | [Label] #size | semmle.label | [Label] #size | +| tst.ts:85:5:87:5 | [ClassInitializedMember,SetterMethodDefinition] set siz ... ; } | semmle.label | [ClassInitializedMember,SetterMethodDefinition] set siz ... ; } | +| tst.ts:85:5:87:5 | [FunctionExpr] set siz ... ; } | semmle.label | [FunctionExpr] set siz ... ; } | +| tst.ts:85:9:85:12 | [Label] size | semmle.label | [Label] size | +| tst.ts:85:14:85:18 | [SimpleParameter] value | semmle.label | [SimpleParameter] value | +| tst.ts:85:21:85:26 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | +| tst.ts:85:21:85:45 | [UnionTypeExpr] string ... boolean | semmle.label | [UnionTypeExpr] string ... boolean | +| tst.ts:85:30:85:35 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | +| tst.ts:85:39:85:45 | [KeywordTypeExpr] boolean | semmle.label | [KeywordTypeExpr] boolean | +| tst.ts:85:48:87:5 | [BlockStmt] { ... ; } | semmle.label | [BlockStmt] { ... ; } | +| tst.ts:86:7:86:10 | [ThisExpr] this | semmle.label | [ThisExpr] this | +| tst.ts:86:7:86:16 | [DotExpr] this.#size | semmle.label | [DotExpr] this.#size | +| tst.ts:86:7:86:32 | [AssignExpr] this.#s ... (value) | semmle.label | [AssignExpr] this.#s ... (value) | +| tst.ts:86:7:86:33 | [ExprStmt] this.#s ... value); | semmle.label | [ExprStmt] this.#s ... value); | +| tst.ts:86:12:86:16 | [Label] #size | semmle.label | [Label] #size | +| tst.ts:86:20:86:25 | [VarRef] Number | semmle.label | [VarRef] Number | +| tst.ts:86:20:86:32 | [CallExpr] Number(value) | semmle.label | [CallExpr] Number(value) | +| tst.ts:86:27:86:31 | [VarRef] value | semmle.label | [VarRef] value | +| tst.ts:91:3:95:3 | [ClassDefinition,TypeDefinition] class S ... } } | semmle.label | [ClassDefinition,TypeDefinition] class S ... } } | +| tst.ts:91:9:91:13 | [VarDecl] Super | semmle.label | [VarDecl] Super | +| tst.ts:91:15:91:14 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | +| tst.ts:91:15:91:14 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | [ClassInitializedMember,ConstructorDefinition] constructor() {} | +| tst.ts:91:15:91:14 | [FunctionExpr] () {} | semmle.label | [FunctionExpr] () {} | +| tst.ts:91:15:91:14 | [Label] constructor | semmle.label | [Label] constructor | +| tst.ts:92:5:92:10 | [Label] random | semmle.label | [Label] random | +| tst.ts:92:5:94:5 | [ClassInitializedMember,MethodDefinition] random( ... ; } | semmle.label | [ClassInitializedMember,MethodDefinition] random( ... ; } | +| tst.ts:92:5:94:5 | [FunctionExpr] random( ... ; } | semmle.label | [FunctionExpr] random( ... ; } | +| tst.ts:92:15:92:20 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | +| tst.ts:92:22:94:5 | [BlockStmt] { ... ; } | semmle.label | [BlockStmt] { ... ; } | +| tst.ts:93:7:93:15 | [ReturnStmt] return 4; | semmle.label | [ReturnStmt] return 4; | +| tst.ts:93:14:93:14 | [Literal] 4 | semmle.label | [Literal] 4 | +| tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | semmle.label | [ClassDefinition,TypeDefinition] class S ... } } | +| tst.ts:97:9:97:11 | [VarDecl] Sub | semmle.label | [VarDecl] Sub | +| tst.ts:97:21:97:25 | [VarRef] Super | semmle.label | [VarRef] Super | +| tst.ts:97:27:97:26 | [BlockStmt] { super(...args); } | semmle.label | [BlockStmt] { super(...args); } | +| tst.ts:97:27:97:26 | [CallExpr] super(...args) | semmle.label | [CallExpr] super(...args) | +| tst.ts:97:27:97:26 | [ClassInitializedMember,ConstructorDefinition] constru ... rgs); } | semmle.label | [ClassInitializedMember,ConstructorDefinition] constru ... rgs); } | +| tst.ts:97:27:97:26 | [ExprStmt] super(...args); | semmle.label | [ExprStmt] super(...args); | +| tst.ts:97:27:97:26 | [FunctionExpr] (...arg ... rgs); } | semmle.label | [FunctionExpr] (...arg ... rgs); } | +| tst.ts:97:27:97:26 | [Label] constructor | semmle.label | [Label] constructor | +| tst.ts:97:27:97:26 | [SimpleParameter] args | semmle.label | [SimpleParameter] args | +| tst.ts:97:27:97:26 | [SpreadElement] ...args | semmle.label | [SpreadElement] ...args | +| tst.ts:97:27:97:26 | [SuperExpr] super | semmle.label | [SuperExpr] super | +| tst.ts:97:27:97:26 | [VarRef] args | semmle.label | [VarRef] args | +| tst.ts:98:5:100:5 | [ClassInitializedMember,MethodDefinition] overrid ... ; } | semmle.label | [ClassInitializedMember,MethodDefinition] overrid ... ; } | +| tst.ts:98:5:100:5 | [FunctionExpr] overrid ... ; } | semmle.label | [FunctionExpr] overrid ... ; } | +| tst.ts:98:14:98:19 | [Label] random | semmle.label | [Label] random | +| tst.ts:98:24:98:29 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | +| tst.ts:98:31:100:5 | [BlockStmt] { ... ; } | semmle.label | [BlockStmt] { ... ; } | +| tst.ts:99:7:99:33 | [ReturnStmt] return ... ) * 10; | semmle.label | [ReturnStmt] return ... ) * 10; | +| tst.ts:99:14:99:18 | [SuperExpr] super | semmle.label | [SuperExpr] super | +| tst.ts:99:14:99:25 | [DotExpr] super.random | semmle.label | [DotExpr] super.random | +| tst.ts:99:14:99:27 | [MethodCallExpr] super.random() | semmle.label | [MethodCallExpr] super.random() | +| tst.ts:99:14:99:32 | [BinaryExpr] super.random() * 10 | semmle.label | [BinaryExpr] super.random() * 10 | +| tst.ts:99:20:99:25 | [Label] random | semmle.label | [Label] random | +| tst.ts:99:31:99:32 | [Literal] 10 | semmle.label | [Literal] 10 | +| tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | semmle.label | [FunctionDeclStmt] functio ... }`; } | +| tst.ts:104:12:104:14 | [VarDecl] bar | semmle.label | [VarDecl] bar | +| tst.ts:104:16:104:16 | [SimpleParameter] s | semmle.label | [SimpleParameter] s | +| tst.ts:104:19:104:24 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | +| tst.ts:104:28:104:44 | [TemplateLiteralTypeExpr] `hello ${string}` | semmle.label | [TemplateLiteralTypeExpr] `hello ${string}` | +| tst.ts:104:29:104:34 | [LiteralTypeExpr] hello | semmle.label | [LiteralTypeExpr] hello | +| tst.ts:104:37:104:42 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | +| tst.ts:104:46:107:3 | [BlockStmt] { / ... }`; } | semmle.label | [BlockStmt] { / ... }`; } | +| tst.ts:106:5:106:24 | [ReturnStmt] return `hello ${s}`; | semmle.label | [ReturnStmt] return `hello ${s}`; | +| tst.ts:106:12:106:23 | [TemplateLiteral] `hello ${s}` | semmle.label | [TemplateLiteral] `hello ${s}` | +| tst.ts:106:13:106:18 | [TemplateElement] hello | semmle.label | [TemplateElement] hello | +| tst.ts:106:21:106:21 | [VarRef] s | semmle.label | [VarRef] s | +| tst.ts:109:3:109:50 | [DeclStmt] let s1 = ... | semmle.label | [DeclStmt] let s1 = ... | +| tst.ts:109:15:109:16 | [VarDecl] s1 | semmle.label | [VarDecl] s1 | +| tst.ts:109:15:109:49 | [VariableDeclarator] s1: `${ ... umber}` | semmle.label | [VariableDeclarator] s1: `${ ... umber}` | +| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | semmle.label | [TemplateLiteralTypeExpr] `${numb ... umber}` | +| tst.ts:109:22:109:27 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | +| tst.ts:109:29:109:29 | [LiteralTypeExpr] - | semmle.label | [LiteralTypeExpr] - | +| tst.ts:109:32:109:37 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | +| tst.ts:109:39:109:39 | [LiteralTypeExpr] - | semmle.label | [LiteralTypeExpr] - | +| tst.ts:109:42:109:47 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | +| tst.ts:110:3:110:26 | [DeclStmt] let s2 = ... | semmle.label | [DeclStmt] let s2 = ... | +| tst.ts:110:15:110:16 | [VarDecl] s2 | semmle.label | [VarDecl] s2 | +| tst.ts:110:15:110:25 | [VariableDeclarator] s2: `1-2-3` | semmle.label | [VariableDeclarator] s2: `1-2-3` | +| tst.ts:110:19:110:25 | [LiteralTypeExpr] `1-2-3` | semmle.label | [LiteralTypeExpr] `1-2-3` | +| tst.ts:110:19:110:25 | [TemplateLiteralTypeExpr] `1-2-3` | semmle.label | [TemplateLiteralTypeExpr] `1-2-3` | +| tst.ts:111:3:111:34 | [DeclStmt] let s3 = ... | semmle.label | [DeclStmt] let s3 = ... | +| tst.ts:111:15:111:16 | [VarDecl] s3 | semmle.label | [VarDecl] s3 | +| tst.ts:111:15:111:33 | [VariableDeclarator] s3: `${number}-2-3` | semmle.label | [VariableDeclarator] s3: `${number}-2-3` | +| tst.ts:111:19:111:33 | [TemplateLiteralTypeExpr] `${number}-2-3` | semmle.label | [TemplateLiteralTypeExpr] `${number}-2-3` | +| tst.ts:111:22:111:27 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | +| tst.ts:111:29:111:32 | [LiteralTypeExpr] -2-3 | semmle.label | [LiteralTypeExpr] -2-3 | +| tst.ts:112:3:112:4 | [VarRef] s1 | semmle.label | [VarRef] s1 | +| tst.ts:112:3:112:9 | [AssignExpr] s1 = s2 | semmle.label | [AssignExpr] s1 = s2 | +| tst.ts:112:3:112:10 | [ExprStmt] s1 = s2; | semmle.label | [ExprStmt] s1 = s2; | +| tst.ts:112:8:112:9 | [VarRef] s2 | semmle.label | [VarRef] s2 | +| tst.ts:113:3:113:4 | [VarRef] s1 | semmle.label | [VarRef] s1 | +| tst.ts:113:3:113:9 | [AssignExpr] s1 = s3 | semmle.label | [AssignExpr] s1 = s3 | +| tst.ts:113:3:113:10 | [ExprStmt] s1 = s3; | semmle.label | [ExprStmt] s1 = s3; | +| tst.ts:113:8:113:9 | [VarRef] s3 | semmle.label | [VarRef] s3 | +| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | semmle.label | [ClassDefinition,TypeDefinition] class F ... } } | +| tst.ts:116:9:116:11 | [VarDecl] Foo | semmle.label | [VarDecl] Foo | +| tst.ts:116:13:116:12 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | +| tst.ts:116:13:116:12 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | [ClassInitializedMember,ConstructorDefinition] constructor() {} | +| tst.ts:116:13:116:12 | [FunctionExpr] () {} | semmle.label | [FunctionExpr] () {} | +| tst.ts:116:13:116:12 | [Label] constructor | semmle.label | [Label] constructor | +| tst.ts:117:5:117:15 | [Label] #someMethod | semmle.label | [Label] #someMethod | +| tst.ts:117:5:119:5 | [ClassInitializedMember,MethodDefinition] #someMe ... ; } | semmle.label | [ClassInitializedMember,MethodDefinition] #someMe ... ; } | +| tst.ts:117:5:119:5 | [FunctionExpr] #someMe ... ; } | semmle.label | [FunctionExpr] #someMe ... ; } | +| tst.ts:117:20:117:25 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | +| tst.ts:117:27:119:5 | [BlockStmt] { ... ; } | semmle.label | [BlockStmt] { ... ; } | +| tst.ts:118:7:118:16 | [ReturnStmt] return 42; | semmle.label | [ReturnStmt] return 42; | +| tst.ts:118:14:118:15 | [Literal] 42 | semmle.label | [Literal] 42 | +| tst.ts:121:5:123:5 | [ClassInitializedMember,GetterMethodDefinition] get #so ... ; } | semmle.label | [ClassInitializedMember,GetterMethodDefinition] get #so ... ; } | +| tst.ts:121:5:123:5 | [FunctionExpr] get #so ... ; } | semmle.label | [FunctionExpr] get #so ... ; } | +| tst.ts:121:9:121:18 | [Label] #someValue | semmle.label | [Label] #someValue | +| tst.ts:121:23:121:28 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | +| tst.ts:121:30:123:5 | [BlockStmt] { ... ; } | semmle.label | [BlockStmt] { ... ; } | +| tst.ts:122:7:122:17 | [ReturnStmt] return 100; | semmle.label | [ReturnStmt] return 100; | +| tst.ts:122:14:122:16 | [Literal] 100 | semmle.label | [Literal] 100 | +| tst.ts:125:5:125:16 | [Label] publicMethod | semmle.label | [Label] publicMethod | +| tst.ts:125:5:128:5 | [ClassInitializedMember,MethodDefinition] publicM ... ; } | semmle.label | [ClassInitializedMember,MethodDefinition] publicM ... ; } | +| tst.ts:125:5:128:5 | [FunctionExpr] publicM ... ; } | semmle.label | [FunctionExpr] publicM ... ; } | +| tst.ts:125:20:128:5 | [BlockStmt] { ... ; } | semmle.label | [BlockStmt] { ... ; } | +| tst.ts:126:7:126:10 | [ThisExpr] this | semmle.label | [ThisExpr] this | +| tst.ts:126:7:126:22 | [DotExpr] this.#someMethod | semmle.label | [DotExpr] this.#someMethod | +| tst.ts:126:7:126:24 | [MethodCallExpr] this.#someMethod() | semmle.label | [MethodCallExpr] this.#someMethod() | +| tst.ts:126:7:126:25 | [ExprStmt] this.#someMethod(); | semmle.label | [ExprStmt] this.#someMethod(); | +| tst.ts:126:12:126:22 | [Label] #someMethod | semmle.label | [Label] #someMethod | +| tst.ts:127:7:127:29 | [ReturnStmt] return ... eValue; | semmle.label | [ReturnStmt] return ... eValue; | +| tst.ts:127:14:127:17 | [ThisExpr] this | semmle.label | [ThisExpr] this | +| tst.ts:127:14:127:28 | [DotExpr] this.#someValue | semmle.label | [DotExpr] this.#someValue | +| tst.ts:127:19:127:28 | [Label] #someValue | semmle.label | [Label] #someValue | | type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | -| type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | semmle.order | 54 | +| type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | semmle.order | 56 | | type_alias.ts:1:6:1:6 | [Identifier] B | semmle.label | [Identifier] B | | type_alias.ts:1:10:1:16 | [KeywordTypeExpr] boolean | semmle.label | [KeywordTypeExpr] boolean | | type_alias.ts:3:1:3:9 | [DeclStmt] var b = ... | semmle.label | [DeclStmt] var b = ... | -| type_alias.ts:3:1:3:9 | [DeclStmt] var b = ... | semmle.order | 55 | +| type_alias.ts:3:1:3:9 | [DeclStmt] var b = ... | semmle.order | 57 | | type_alias.ts:3:5:3:5 | [VarDecl] b | semmle.label | [VarDecl] b | | type_alias.ts:3:5:3:8 | [VariableDeclarator] b: B | semmle.label | [VariableDeclarator] b: B | | type_alias.ts:3:8:3:8 | [LocalTypeAccess] B | semmle.label | [LocalTypeAccess] B | | type_alias.ts:5:1:5:50 | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | -| type_alias.ts:5:1:5:50 | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | semmle.order | 56 | +| type_alias.ts:5:1:5:50 | [TypeAliasDeclaration,TypeDefinition] type Va ... ay>; | semmle.order | 58 | | type_alias.ts:5:6:5:17 | [Identifier] ValueOrArray | semmle.label | [Identifier] ValueOrArray | | type_alias.ts:5:19:5:19 | [Identifier] T | semmle.label | [Identifier] T | | type_alias.ts:5:19:5:19 | [TypeParameter] T | semmle.label | [TypeParameter] T | @@ -431,14 +613,14 @@ nodes | type_alias.ts:5:34:5:48 | [GenericTypeExpr] ValueOrArray | semmle.label | [GenericTypeExpr] ValueOrArray | | type_alias.ts:5:47:5:47 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | | type_alias.ts:7:1:7:28 | [DeclStmt] var c = ... | semmle.label | [DeclStmt] var c = ... | -| type_alias.ts:7:1:7:28 | [DeclStmt] var c = ... | semmle.order | 57 | +| type_alias.ts:7:1:7:28 | [DeclStmt] var c = ... | semmle.order | 59 | | type_alias.ts:7:5:7:5 | [VarDecl] c | semmle.label | [VarDecl] c | | type_alias.ts:7:5:7:27 | [VariableDeclarator] c: Valu ... number> | semmle.label | [VariableDeclarator] c: Valu ... number> | | type_alias.ts:7:8:7:19 | [LocalTypeAccess] ValueOrArray | semmle.label | [LocalTypeAccess] ValueOrArray | | type_alias.ts:7:8:7:27 | [GenericTypeExpr] ValueOrArray | semmle.label | [GenericTypeExpr] ValueOrArray | | type_alias.ts:7:21:7:26 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | type_alias.ts:9:1:15:13 | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | -| type_alias.ts:9:1:15:13 | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | semmle.order | 58 | +| type_alias.ts:9:1:15:13 | [TypeAliasDeclaration,TypeDefinition] type Js ... Json[]; | semmle.order | 60 | | type_alias.ts:9:6:9:9 | [Identifier] Json | semmle.label | [Identifier] Json | | type_alias.ts:10:5:15:12 | [UnionTypeExpr] \| strin ... Json[] | semmle.label | [UnionTypeExpr] \| strin ... Json[] | | type_alias.ts:10:7:10:12 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | @@ -454,12 +636,12 @@ nodes | type_alias.ts:15:7:15:10 | [LocalTypeAccess] Json | semmle.label | [LocalTypeAccess] Json | | type_alias.ts:15:7:15:12 | [ArrayTypeExpr] Json[] | semmle.label | [ArrayTypeExpr] Json[] | | type_alias.ts:17:1:17:15 | [DeclStmt] var json = ... | semmle.label | [DeclStmt] var json = ... | -| type_alias.ts:17:1:17:15 | [DeclStmt] var json = ... | semmle.order | 59 | +| type_alias.ts:17:1:17:15 | [DeclStmt] var json = ... | semmle.order | 61 | | type_alias.ts:17:5:17:8 | [VarDecl] json | semmle.label | [VarDecl] json | | type_alias.ts:17:5:17:14 | [VariableDeclarator] json: Json | semmle.label | [VariableDeclarator] json: Json | | type_alias.ts:17:11:17:14 | [LocalTypeAccess] Json | semmle.label | [LocalTypeAccess] Json | | type_alias.ts:19:1:21:57 | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | -| type_alias.ts:19:1:21:57 | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | semmle.order | 60 | +| type_alias.ts:19:1:21:57 | [TypeAliasDeclaration,TypeDefinition] type Vi ... ode[]]; | semmle.order | 62 | | type_alias.ts:19:6:19:16 | [Identifier] VirtualNode | semmle.label | [Identifier] VirtualNode | | type_alias.ts:20:5:21:56 | [UnionTypeExpr] \| strin ... Node[]] | semmle.label | [UnionTypeExpr] \| strin ... Node[]] | | type_alias.ts:20:7:20:12 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | @@ -475,7 +657,7 @@ nodes | type_alias.ts:21:43:21:53 | [LocalTypeAccess] VirtualNode | semmle.label | [LocalTypeAccess] VirtualNode | | type_alias.ts:21:43:21:55 | [ArrayTypeExpr] VirtualNode[] | semmle.label | [ArrayTypeExpr] VirtualNode[] | | type_alias.ts:23:1:27:6 | [DeclStmt] const myNode = ... | semmle.label | [DeclStmt] const myNode = ... | -| type_alias.ts:23:1:27:6 | [DeclStmt] const myNode = ... | semmle.order | 61 | +| type_alias.ts:23:1:27:6 | [DeclStmt] const myNode = ... | semmle.order | 63 | | type_alias.ts:23:7:23:12 | [VarDecl] myNode | semmle.label | [VarDecl] myNode | | type_alias.ts:23:7:27:5 | [VariableDeclarator] myNode: ... ] ] | semmle.label | [VariableDeclarator] myNode: ... ] ] | | type_alias.ts:23:15:23:25 | [LocalTypeAccess] VirtualNode | semmle.label | [LocalTypeAccess] VirtualNode | @@ -500,12 +682,12 @@ nodes | type_alias.ts:26:23:26:36 | [Literal] "second-child" | semmle.label | [Literal] "second-child" | | type_alias.ts:26:41:26:62 | [Literal] "I'm the second child" | semmle.label | [Literal] "I'm the second child" | | type_definition_objects.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.label | [ImportDeclaration] import ... dummy"; | -| type_definition_objects.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 62 | +| type_definition_objects.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 64 | | type_definition_objects.ts:1:8:1:17 | [ImportSpecifier] * as dummy | semmle.label | [ImportSpecifier] * as dummy | | type_definition_objects.ts:1:13:1:17 | [VarDecl] dummy | semmle.label | [VarDecl] dummy | | type_definition_objects.ts:1:24:1:32 | [Literal] "./dummy" | semmle.label | [Literal] "./dummy" | | type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | semmle.label | [ExportDeclaration] export class C {} | -| type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | semmle.order | 63 | +| type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | semmle.order | 65 | | type_definition_objects.ts:3:8:3:17 | [ClassDefinition,TypeDefinition] class C {} | semmle.label | [ClassDefinition,TypeDefinition] class C {} | | type_definition_objects.ts:3:14:3:14 | [VarDecl] C | semmle.label | [VarDecl] C | | type_definition_objects.ts:3:16:3:15 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | @@ -513,36 +695,36 @@ nodes | type_definition_objects.ts:3:16:3:15 | [FunctionExpr] () {} | semmle.label | [FunctionExpr] () {} | | type_definition_objects.ts:3:16:3:15 | [Label] constructor | semmle.label | [Label] constructor | | type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.label | [DeclStmt] let classObj = ... | -| type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.order | 64 | +| type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.order | 66 | | type_definition_objects.ts:4:5:4:12 | [VarDecl] classObj | semmle.label | [VarDecl] classObj | | type_definition_objects.ts:4:5:4:16 | [VariableDeclarator] classObj = C | semmle.label | [VariableDeclarator] classObj = C | | type_definition_objects.ts:4:16:4:16 | [VarRef] C | semmle.label | [VarRef] C | | type_definition_objects.ts:6:1:6:16 | [ExportDeclaration] export enum E {} | semmle.label | [ExportDeclaration] export enum E {} | -| type_definition_objects.ts:6:1:6:16 | [ExportDeclaration] export enum E {} | semmle.order | 65 | +| type_definition_objects.ts:6:1:6:16 | [ExportDeclaration] export enum E {} | semmle.order | 67 | | type_definition_objects.ts:6:8:6:16 | [EnumDeclaration,TypeDefinition] enum E {} | semmle.label | [EnumDeclaration,TypeDefinition] enum E {} | | type_definition_objects.ts:6:13:6:13 | [VarDecl] E | semmle.label | [VarDecl] E | | type_definition_objects.ts:7:1:7:16 | [DeclStmt] let enumObj = ... | semmle.label | [DeclStmt] let enumObj = ... | -| type_definition_objects.ts:7:1:7:16 | [DeclStmt] let enumObj = ... | semmle.order | 66 | +| type_definition_objects.ts:7:1:7:16 | [DeclStmt] let enumObj = ... | semmle.order | 68 | | type_definition_objects.ts:7:5:7:11 | [VarDecl] enumObj | semmle.label | [VarDecl] enumObj | | type_definition_objects.ts:7:5:7:15 | [VariableDeclarator] enumObj = E | semmle.label | [VariableDeclarator] enumObj = E | | type_definition_objects.ts:7:15:7:15 | [VarRef] E | semmle.label | [VarRef] E | | type_definition_objects.ts:9:1:9:22 | [ExportDeclaration] export ... e N {;} | semmle.label | [ExportDeclaration] export ... e N {;} | -| type_definition_objects.ts:9:1:9:22 | [ExportDeclaration] export ... e N {;} | semmle.order | 67 | +| type_definition_objects.ts:9:1:9:22 | [ExportDeclaration] export ... e N {;} | semmle.order | 69 | | type_definition_objects.ts:9:8:9:22 | [NamespaceDeclaration] namespace N {;} | semmle.label | [NamespaceDeclaration] namespace N {;} | | type_definition_objects.ts:9:18:9:18 | [VarDecl] N | semmle.label | [VarDecl] N | | type_definition_objects.ts:9:21:9:21 | [EmptyStmt] ; | semmle.label | [EmptyStmt] ; | | type_definition_objects.ts:10:1:10:21 | [DeclStmt] let namespaceObj = ... | semmle.label | [DeclStmt] let namespaceObj = ... | -| type_definition_objects.ts:10:1:10:21 | [DeclStmt] let namespaceObj = ... | semmle.order | 68 | +| type_definition_objects.ts:10:1:10:21 | [DeclStmt] let namespaceObj = ... | semmle.order | 70 | | type_definition_objects.ts:10:5:10:16 | [VarDecl] namespaceObj | semmle.label | [VarDecl] namespaceObj | | type_definition_objects.ts:10:5:10:20 | [VariableDeclarator] namespaceObj = N | semmle.label | [VariableDeclarator] namespaceObj = N | | type_definition_objects.ts:10:20:10:20 | [VarRef] N | semmle.label | [VarRef] N | | type_definitions.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.label | [ImportDeclaration] import ... dummy"; | -| type_definitions.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 69 | +| type_definitions.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | semmle.order | 71 | | type_definitions.ts:1:8:1:17 | [ImportSpecifier] * as dummy | semmle.label | [ImportSpecifier] * as dummy | | type_definitions.ts:1:13:1:17 | [VarDecl] dummy | semmle.label | [VarDecl] dummy | | type_definitions.ts:1:24:1:32 | [Literal] "./dummy" | semmle.label | [Literal] "./dummy" | | type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | -| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.order | 70 | +| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.order | 72 | | type_definitions.ts:3:11:3:11 | [Identifier] I | semmle.label | [Identifier] I | | type_definitions.ts:3:13:3:13 | [Identifier] S | semmle.label | [Identifier] S | | type_definitions.ts:3:13:3:13 | [TypeParameter] S | semmle.label | [TypeParameter] S | @@ -550,14 +732,14 @@ nodes | type_definitions.ts:4:3:4:7 | [FieldDeclaration] x: S; | semmle.label | [FieldDeclaration] x: S; | | type_definitions.ts:4:6:4:6 | [LocalTypeAccess] S | semmle.label | [LocalTypeAccess] S | | type_definitions.ts:6:1:6:16 | [DeclStmt] let i = ... | semmle.label | [DeclStmt] let i = ... | -| type_definitions.ts:6:1:6:16 | [DeclStmt] let i = ... | semmle.order | 71 | +| type_definitions.ts:6:1:6:16 | [DeclStmt] let i = ... | semmle.order | 73 | | type_definitions.ts:6:5:6:5 | [VarDecl] i | semmle.label | [VarDecl] i | | type_definitions.ts:6:5:6:16 | [VariableDeclarator] i: I | semmle.label | [VariableDeclarator] i: I | | type_definitions.ts:6:8:6:8 | [LocalTypeAccess] I | semmle.label | [LocalTypeAccess] I | | type_definitions.ts:6:8:6:16 | [GenericTypeExpr] I | semmle.label | [GenericTypeExpr] I | | type_definitions.ts:6:10:6:15 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.label | [ClassDefinition,TypeDefinition] class C ... x: T } | -| type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.order | 72 | +| type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.order | 74 | | type_definitions.ts:8:7:8:7 | [VarDecl] C | semmle.label | [VarDecl] C | | type_definitions.ts:8:8:8:7 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | | type_definitions.ts:8:8:8:7 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | [ClassInitializedMember,ConstructorDefinition] constructor() {} | @@ -569,14 +751,14 @@ nodes | type_definitions.ts:9:3:9:6 | [FieldDeclaration] x: T | semmle.label | [FieldDeclaration] x: T | | type_definitions.ts:9:6:9:6 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | | type_definitions.ts:11:1:11:17 | [DeclStmt] let c = ... | semmle.label | [DeclStmt] let c = ... | -| type_definitions.ts:11:1:11:17 | [DeclStmt] let c = ... | semmle.order | 73 | +| type_definitions.ts:11:1:11:17 | [DeclStmt] let c = ... | semmle.order | 75 | | type_definitions.ts:11:5:11:5 | [VarDecl] c | semmle.label | [VarDecl] c | | type_definitions.ts:11:5:11:16 | [VariableDeclarator] c: C | semmle.label | [VariableDeclarator] c: C | | type_definitions.ts:11:8:11:8 | [LocalTypeAccess] C | semmle.label | [LocalTypeAccess] C | | type_definitions.ts:11:8:11:16 | [GenericTypeExpr] C | semmle.label | [GenericTypeExpr] C | | type_definitions.ts:11:10:11:15 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | | type_definitions.ts:13:1:15:1 | [EnumDeclaration,TypeDefinition] enum Co ... blue } | semmle.label | [EnumDeclaration,TypeDefinition] enum Co ... blue } | -| type_definitions.ts:13:1:15:1 | [EnumDeclaration,TypeDefinition] enum Co ... blue } | semmle.order | 74 | +| type_definitions.ts:13:1:15:1 | [EnumDeclaration,TypeDefinition] enum Co ... blue } | semmle.order | 76 | | type_definitions.ts:13:6:13:10 | [VarDecl] Color | semmle.label | [VarDecl] Color | | type_definitions.ts:14:3:14:5 | [EnumMember,TypeDefinition] red | semmle.label | [EnumMember,TypeDefinition] red | | type_definitions.ts:14:3:14:5 | [VarDecl] red | semmle.label | [VarDecl] red | @@ -585,29 +767,29 @@ nodes | type_definitions.ts:14:15:14:18 | [EnumMember,TypeDefinition] blue | semmle.label | [EnumMember,TypeDefinition] blue | | type_definitions.ts:14:15:14:18 | [VarDecl] blue | semmle.label | [VarDecl] blue | | type_definitions.ts:16:1:16:17 | [DeclStmt] let color = ... | semmle.label | [DeclStmt] let color = ... | -| type_definitions.ts:16:1:16:17 | [DeclStmt] let color = ... | semmle.order | 75 | +| type_definitions.ts:16:1:16:17 | [DeclStmt] let color = ... | semmle.order | 77 | | type_definitions.ts:16:5:16:9 | [VarDecl] color | semmle.label | [VarDecl] color | | type_definitions.ts:16:5:16:16 | [VariableDeclarator] color: Color | semmle.label | [VariableDeclarator] color: Color | | type_definitions.ts:16:12:16:16 | [LocalTypeAccess] Color | semmle.label | [LocalTypeAccess] Color | | type_definitions.ts:18:1:18:33 | [EnumDeclaration,TypeDefinition] enum En ... ember } | semmle.label | [EnumDeclaration,TypeDefinition] enum En ... ember } | -| type_definitions.ts:18:1:18:33 | [EnumDeclaration,TypeDefinition] enum En ... ember } | semmle.order | 76 | +| type_definitions.ts:18:1:18:33 | [EnumDeclaration,TypeDefinition] enum En ... ember } | semmle.order | 78 | | type_definitions.ts:18:6:18:22 | [VarDecl] EnumWithOneMember | semmle.label | [VarDecl] EnumWithOneMember | | type_definitions.ts:18:26:18:31 | [EnumMember,TypeDefinition] member | semmle.label | [EnumMember,TypeDefinition] member | | type_definitions.ts:18:26:18:31 | [VarDecl] member | semmle.label | [VarDecl] member | | type_definitions.ts:19:1:19:25 | [DeclStmt] let e = ... | semmle.label | [DeclStmt] let e = ... | -| type_definitions.ts:19:1:19:25 | [DeclStmt] let e = ... | semmle.order | 77 | +| type_definitions.ts:19:1:19:25 | [DeclStmt] let e = ... | semmle.order | 79 | | type_definitions.ts:19:5:19:5 | [VarDecl] e | semmle.label | [VarDecl] e | | type_definitions.ts:19:5:19:24 | [VariableDeclarator] e: EnumWithOneMember | semmle.label | [VariableDeclarator] e: EnumWithOneMember | | type_definitions.ts:19:8:19:24 | [LocalTypeAccess] EnumWithOneMember | semmle.label | [LocalTypeAccess] EnumWithOneMember | | type_definitions.ts:21:1:21:20 | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | semmle.label | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | -| type_definitions.ts:21:1:21:20 | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | semmle.order | 78 | +| type_definitions.ts:21:1:21:20 | [TypeAliasDeclaration,TypeDefinition] type Alias = T[]; | semmle.order | 80 | | type_definitions.ts:21:6:21:10 | [Identifier] Alias | semmle.label | [Identifier] Alias | | type_definitions.ts:21:12:21:12 | [Identifier] T | semmle.label | [Identifier] T | | type_definitions.ts:21:12:21:12 | [TypeParameter] T | semmle.label | [TypeParameter] T | | type_definitions.ts:21:17:21:17 | [LocalTypeAccess] T | semmle.label | [LocalTypeAccess] T | | type_definitions.ts:21:17:21:19 | [ArrayTypeExpr] T[] | semmle.label | [ArrayTypeExpr] T[] | | type_definitions.ts:22:1:22:39 | [DeclStmt] let aliasForNumberArray = ... | semmle.label | [DeclStmt] let aliasForNumberArray = ... | -| type_definitions.ts:22:1:22:39 | [DeclStmt] let aliasForNumberArray = ... | semmle.order | 79 | +| type_definitions.ts:22:1:22:39 | [DeclStmt] let aliasForNumberArray = ... | semmle.order | 81 | | type_definitions.ts:22:5:22:23 | [VarDecl] aliasForNumberArray | semmle.label | [VarDecl] aliasForNumberArray | | type_definitions.ts:22:5:22:38 | [VariableDeclarator] aliasFo ... number> | semmle.label | [VariableDeclarator] aliasFo ... number> | | type_definitions.ts:22:26:22:30 | [LocalTypeAccess] Alias | semmle.label | [LocalTypeAccess] Alias | @@ -716,6 +898,28 @@ edges | dummy.ts:2:12:2:16 | [VariableDeclarator] x = 5 | dummy.ts:2:12:2:12 | [VarDecl] x | semmle.order | 1 | | dummy.ts:2:12:2:16 | [VariableDeclarator] x = 5 | dummy.ts:2:16:2:16 | [Literal] 5 | semmle.label | 2 | | dummy.ts:2:12:2:16 | [VariableDeclarator] x = 5 | dummy.ts:2:16:2:16 | [Literal] 5 | semmle.order | 2 | +| dummy.ts:4:1:4:24 | [ExportDeclaration] export ... /ab+c/; | dummy.ts:4:8:4:24 | [DeclStmt] let reg = ... | semmle.label | 1 | +| dummy.ts:4:1:4:24 | [ExportDeclaration] export ... /ab+c/; | dummy.ts:4:8:4:24 | [DeclStmt] let reg = ... | semmle.order | 1 | +| dummy.ts:4:8:4:24 | [DeclStmt] let reg = ... | dummy.ts:4:12:4:23 | [VariableDeclarator] reg = /ab+c/ | semmle.label | 1 | +| dummy.ts:4:8:4:24 | [DeclStmt] let reg = ... | dummy.ts:4:12:4:23 | [VariableDeclarator] reg = /ab+c/ | semmle.order | 1 | +| dummy.ts:4:12:4:23 | [VariableDeclarator] reg = /ab+c/ | dummy.ts:4:12:4:14 | [VarDecl] reg | semmle.label | 1 | +| dummy.ts:4:12:4:23 | [VariableDeclarator] reg = /ab+c/ | dummy.ts:4:12:4:14 | [VarDecl] reg | semmle.order | 1 | +| dummy.ts:4:12:4:23 | [VariableDeclarator] reg = /ab+c/ | dummy.ts:4:18:4:23 | [RegExpLiteral] /ab+c/ | semmle.label | 2 | +| dummy.ts:4:12:4:23 | [VariableDeclarator] reg = /ab+c/ | dummy.ts:4:18:4:23 | [RegExpLiteral] /ab+c/ | semmle.order | 2 | +| dummy.ts:4:18:4:23 | [RegExpLiteral] /ab+c/ | dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | semmle.label | 0 | +| dummy.ts:4:18:4:23 | [RegExpLiteral] /ab+c/ | dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | semmle.order | 0 | +| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | dummy.ts:4:19:4:19 | [RegExpNormalConstant] a | semmle.label | 0 | +| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | dummy.ts:4:19:4:19 | [RegExpNormalConstant] a | semmle.order | 0 | +| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | dummy.ts:4:20:4:21 | [RegExpPlus] b+ | semmle.label | 1 | +| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | dummy.ts:4:20:4:21 | [RegExpPlus] b+ | semmle.order | 1 | +| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | dummy.ts:4:22:4:22 | [RegExpNormalConstant] c | semmle.label | 2 | +| dummy.ts:4:19:4:22 | [RegExpSequence] ab+c | dummy.ts:4:22:4:22 | [RegExpNormalConstant] c | semmle.order | 2 | +| dummy.ts:4:20:4:21 | [RegExpPlus] b+ | dummy.ts:4:20:4:20 | [RegExpNormalConstant] b | semmle.label | 0 | +| dummy.ts:4:20:4:21 | [RegExpPlus] b+ | dummy.ts:4:20:4:20 | [RegExpNormalConstant] b | semmle.order | 0 | +| file://:0:0:0:0 | (Arguments) | tst.ts:86:27:86:31 | [VarRef] value | semmle.label | 0 | +| file://:0:0:0:0 | (Arguments) | tst.ts:86:27:86:31 | [VarRef] value | semmle.order | 0 | +| file://:0:0:0:0 | (Arguments) | tst.ts:97:27:97:26 | [SpreadElement] ...args | semmle.label | 0 | +| file://:0:0:0:0 | (Arguments) | tst.ts:97:27:97:26 | [SpreadElement] ...args | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | tst.ts:14:17:14:17 | [SimpleParameter] x | semmle.label | 0 | | file://:0:0:0:0 | (Parameters) | tst.ts:14:17:14:17 | [SimpleParameter] x | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | tst.ts:14:28:14:28 | [SimpleParameter] y | semmle.label | 1 | @@ -732,6 +936,14 @@ edges | file://:0:0:0:0 | (Parameters) | tst.ts:20:23:20:23 | [SimpleParameter] x | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | tst.ts:20:26:20:26 | [SimpleParameter] y | semmle.label | 1 | | file://:0:0:0:0 | (Parameters) | tst.ts:20:26:20:26 | [SimpleParameter] y | semmle.order | 1 | +| file://:0:0:0:0 | (Parameters) | tst.ts:75:14:75:18 | [SimpleParameter] value | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:75:14:75:18 | [SimpleParameter] value | semmle.order | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:85:14:85:18 | [SimpleParameter] value | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:85:14:85:18 | [SimpleParameter] value | semmle.order | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:97:27:97:26 | [SimpleParameter] args | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:97:27:97:26 | [SimpleParameter] args | semmle.order | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:104:16:104:16 | [SimpleParameter] s | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | tst.ts:104:16:104:16 | [SimpleParameter] s | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | type_alias.ts:14:10:14:17 | [SimpleParameter] property | semmle.label | 0 | | file://:0:0:0:0 | (Parameters) | type_alias.ts:14:10:14:17 | [SimpleParameter] property | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | type_alias.ts:21:19:21:21 | [SimpleParameter] key | semmle.label | 0 | @@ -1222,6 +1434,332 @@ edges | tst.ts:69:25:69:44 | [Property] yetAnotherType: true | tst.ts:69:25:69:38 | [Label] yetAnotherType | semmle.order | 1 | | tst.ts:69:25:69:44 | [Property] yetAnotherType: true | tst.ts:69:41:69:44 | [Literal] true | semmle.label | 2 | | tst.ts:69:25:69:44 | [Property] yetAnotherType: true | tst.ts:69:41:69:44 | [Literal] true | semmle.order | 2 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:71:8:71:11 | [VarDecl] TS43 | semmle.label | 1 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:71:8:71:11 | [VarDecl] TS43 | semmle.order | 1 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:73:3:76:3 | [InterfaceDeclaration,TypeDefinition] interfa ... n); } | semmle.label | 2 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:73:3:76:3 | [InterfaceDeclaration,TypeDefinition] interfa ... n); } | semmle.order | 2 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:78:3:88:3 | [ExportDeclaration] export ... } } | semmle.label | 3 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:78:3:88:3 | [ExportDeclaration] export ... } } | semmle.order | 3 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:91:3:95:3 | [ClassDefinition,TypeDefinition] class S ... } } | semmle.label | 4 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:91:3:95:3 | [ClassDefinition,TypeDefinition] class S ... } } | semmle.order | 4 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | semmle.label | 5 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | semmle.order | 5 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | semmle.label | 6 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | semmle.order | 6 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:109:3:109:50 | [DeclStmt] let s1 = ... | semmle.label | 7 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:109:3:109:50 | [DeclStmt] let s1 = ... | semmle.order | 7 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:110:3:110:26 | [DeclStmt] let s2 = ... | semmle.label | 8 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:110:3:110:26 | [DeclStmt] let s2 = ... | semmle.order | 8 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:111:3:111:34 | [DeclStmt] let s3 = ... | semmle.label | 9 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:111:3:111:34 | [DeclStmt] let s3 = ... | semmle.order | 9 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:112:3:112:10 | [ExprStmt] s1 = s2; | semmle.label | 10 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:112:3:112:10 | [ExprStmt] s1 = s2; | semmle.order | 10 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:113:3:113:10 | [ExprStmt] s1 = s3; | semmle.label | 11 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:113:3:113:10 | [ExprStmt] s1 = s3; | semmle.order | 11 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | semmle.label | 12 | +| tst.ts:71:1:130:1 | [NamespaceDeclaration] module ... } } | tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | semmle.order | 12 | +| tst.ts:73:3:76:3 | [InterfaceDeclaration,TypeDefinition] interfa ... n); } | tst.ts:73:13:73:18 | [Identifier] ThingI | semmle.label | 1 | +| tst.ts:73:3:76:3 | [InterfaceDeclaration,TypeDefinition] interfa ... n); } | tst.ts:73:13:73:18 | [Identifier] ThingI | semmle.order | 1 | +| tst.ts:73:3:76:3 | [InterfaceDeclaration,TypeDefinition] interfa ... n); } | tst.ts:74:5:74:22 | [GetterMethodSignature] get size(): number | semmle.label | 2 | +| tst.ts:73:3:76:3 | [InterfaceDeclaration,TypeDefinition] interfa ... n); } | tst.ts:74:5:74:22 | [GetterMethodSignature] get size(): number | semmle.order | 2 | +| tst.ts:73:3:76:3 | [InterfaceDeclaration,TypeDefinition] interfa ... n); } | tst.ts:75:5:75:47 | [SetterMethodSignature] set siz ... olean); | semmle.label | 3 | +| tst.ts:73:3:76:3 | [InterfaceDeclaration,TypeDefinition] interfa ... n); } | tst.ts:75:5:75:47 | [SetterMethodSignature] set siz ... olean); | semmle.order | 3 | +| tst.ts:74:5:74:22 | [FunctionExpr] get size(): number | tst.ts:74:17:74:22 | [KeywordTypeExpr] number | semmle.label | 4 | +| tst.ts:74:5:74:22 | [FunctionExpr] get size(): number | tst.ts:74:17:74:22 | [KeywordTypeExpr] number | semmle.order | 4 | +| tst.ts:74:5:74:22 | [GetterMethodSignature] get size(): number | tst.ts:74:5:74:22 | [FunctionExpr] get size(): number | semmle.label | 1 | +| tst.ts:74:5:74:22 | [GetterMethodSignature] get size(): number | tst.ts:74:5:74:22 | [FunctionExpr] get size(): number | semmle.order | 1 | +| tst.ts:74:5:74:22 | [GetterMethodSignature] get size(): number | tst.ts:74:9:74:12 | [Label] size | semmle.label | 2 | +| tst.ts:74:5:74:22 | [GetterMethodSignature] get size(): number | tst.ts:74:9:74:12 | [Label] size | semmle.order | 2 | +| tst.ts:75:5:75:47 | [FunctionExpr] set siz ... olean); | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| tst.ts:75:5:75:47 | [FunctionExpr] set siz ... olean); | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| tst.ts:75:5:75:47 | [SetterMethodSignature] set siz ... olean); | tst.ts:75:5:75:47 | [FunctionExpr] set siz ... olean); | semmle.label | 1 | +| tst.ts:75:5:75:47 | [SetterMethodSignature] set siz ... olean); | tst.ts:75:5:75:47 | [FunctionExpr] set siz ... olean); | semmle.order | 1 | +| tst.ts:75:5:75:47 | [SetterMethodSignature] set siz ... olean); | tst.ts:75:9:75:12 | [Label] size | semmle.label | 2 | +| tst.ts:75:5:75:47 | [SetterMethodSignature] set siz ... olean); | tst.ts:75:9:75:12 | [Label] size | semmle.order | 2 | +| tst.ts:75:14:75:18 | [SimpleParameter] value | tst.ts:75:21:75:45 | [UnionTypeExpr] number ... boolean | semmle.label | 0 | +| tst.ts:75:14:75:18 | [SimpleParameter] value | tst.ts:75:21:75:45 | [UnionTypeExpr] number ... boolean | semmle.order | 0 | +| tst.ts:75:21:75:45 | [UnionTypeExpr] number ... boolean | tst.ts:75:21:75:26 | [KeywordTypeExpr] number | semmle.label | 1 | +| tst.ts:75:21:75:45 | [UnionTypeExpr] number ... boolean | tst.ts:75:21:75:26 | [KeywordTypeExpr] number | semmle.order | 1 | +| tst.ts:75:21:75:45 | [UnionTypeExpr] number ... boolean | tst.ts:75:30:75:35 | [KeywordTypeExpr] string | semmle.label | 2 | +| tst.ts:75:21:75:45 | [UnionTypeExpr] number ... boolean | tst.ts:75:30:75:35 | [KeywordTypeExpr] string | semmle.order | 2 | +| tst.ts:75:21:75:45 | [UnionTypeExpr] number ... boolean | tst.ts:75:39:75:45 | [KeywordTypeExpr] boolean | semmle.label | 3 | +| tst.ts:75:21:75:45 | [UnionTypeExpr] number ... boolean | tst.ts:75:39:75:45 | [KeywordTypeExpr] boolean | semmle.order | 3 | +| tst.ts:78:3:88:3 | [ExportDeclaration] export ... } } | tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | semmle.label | 1 | +| tst.ts:78:3:88:3 | [ExportDeclaration] export ... } } | tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | semmle.order | 1 | +| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:78:16:78:20 | [VarDecl] Thing | semmle.label | 1 | +| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:78:16:78:20 | [VarDecl] Thing | semmle.order | 1 | +| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:78:33:78:38 | [LocalTypeAccess] ThingI | semmle.label | 2 | +| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:78:33:78:38 | [LocalTypeAccess] ThingI | semmle.order | 2 | +| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:78:40:78:39 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | 3 | +| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:78:40:78:39 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.order | 3 | +| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:79:5:79:14 | [FieldDeclaration] #size = 0; | semmle.label | 4 | +| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:79:5:79:14 | [FieldDeclaration] #size = 0; | semmle.order | 4 | +| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:81:5:83:5 | [ClassInitializedMember,GetterMethodDefinition] get siz ... ; } | semmle.label | 5 | +| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:81:5:83:5 | [ClassInitializedMember,GetterMethodDefinition] get siz ... ; } | semmle.order | 5 | +| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:85:5:87:5 | [ClassInitializedMember,SetterMethodDefinition] set siz ... ; } | semmle.label | 6 | +| tst.ts:78:10:88:3 | [ClassDefinition,TypeDefinition] class T ... } } | tst.ts:85:5:87:5 | [ClassInitializedMember,SetterMethodDefinition] set siz ... ; } | semmle.order | 6 | +| tst.ts:78:40:78:39 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:78:40:78:39 | [FunctionExpr] () {} | semmle.label | 2 | +| tst.ts:78:40:78:39 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:78:40:78:39 | [FunctionExpr] () {} | semmle.order | 2 | +| tst.ts:78:40:78:39 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:78:40:78:39 | [Label] constructor | semmle.label | 1 | +| tst.ts:78:40:78:39 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:78:40:78:39 | [Label] constructor | semmle.order | 1 | +| tst.ts:78:40:78:39 | [FunctionExpr] () {} | tst.ts:78:40:78:39 | [BlockStmt] {} | semmle.label | 5 | +| tst.ts:78:40:78:39 | [FunctionExpr] () {} | tst.ts:78:40:78:39 | [BlockStmt] {} | semmle.order | 5 | +| tst.ts:79:5:79:14 | [FieldDeclaration] #size = 0; | tst.ts:79:5:79:9 | [Label] #size | semmle.label | 1 | +| tst.ts:79:5:79:14 | [FieldDeclaration] #size = 0; | tst.ts:79:5:79:9 | [Label] #size | semmle.order | 1 | +| tst.ts:79:5:79:14 | [FieldDeclaration] #size = 0; | tst.ts:79:13:79:13 | [Literal] 0 | semmle.label | 2 | +| tst.ts:79:5:79:14 | [FieldDeclaration] #size = 0; | tst.ts:79:13:79:13 | [Literal] 0 | semmle.order | 2 | +| tst.ts:81:5:83:5 | [ClassInitializedMember,GetterMethodDefinition] get siz ... ; } | tst.ts:81:5:83:5 | [FunctionExpr] get siz ... ; } | semmle.label | 1 | +| tst.ts:81:5:83:5 | [ClassInitializedMember,GetterMethodDefinition] get siz ... ; } | tst.ts:81:5:83:5 | [FunctionExpr] get siz ... ; } | semmle.order | 1 | +| tst.ts:81:5:83:5 | [ClassInitializedMember,GetterMethodDefinition] get siz ... ; } | tst.ts:81:9:81:12 | [Label] size | semmle.label | 2 | +| tst.ts:81:5:83:5 | [ClassInitializedMember,GetterMethodDefinition] get siz ... ; } | tst.ts:81:9:81:12 | [Label] size | semmle.order | 2 | +| tst.ts:81:5:83:5 | [FunctionExpr] get siz ... ; } | tst.ts:81:17:81:22 | [KeywordTypeExpr] number | semmle.label | 4 | +| tst.ts:81:5:83:5 | [FunctionExpr] get siz ... ; } | tst.ts:81:17:81:22 | [KeywordTypeExpr] number | semmle.order | 4 | +| tst.ts:81:5:83:5 | [FunctionExpr] get siz ... ; } | tst.ts:81:24:83:5 | [BlockStmt] { ... ; } | semmle.label | 5 | +| tst.ts:81:5:83:5 | [FunctionExpr] get siz ... ; } | tst.ts:81:24:83:5 | [BlockStmt] { ... ; } | semmle.order | 5 | +| tst.ts:81:24:83:5 | [BlockStmt] { ... ; } | tst.ts:82:7:82:24 | [ReturnStmt] return this.#size; | semmle.label | 1 | +| tst.ts:81:24:83:5 | [BlockStmt] { ... ; } | tst.ts:82:7:82:24 | [ReturnStmt] return this.#size; | semmle.order | 1 | +| tst.ts:82:7:82:24 | [ReturnStmt] return this.#size; | tst.ts:82:14:82:23 | [DotExpr] this.#size | semmle.label | 1 | +| tst.ts:82:7:82:24 | [ReturnStmt] return this.#size; | tst.ts:82:14:82:23 | [DotExpr] this.#size | semmle.order | 1 | +| tst.ts:82:14:82:23 | [DotExpr] this.#size | tst.ts:82:14:82:17 | [ThisExpr] this | semmle.label | 1 | +| tst.ts:82:14:82:23 | [DotExpr] this.#size | tst.ts:82:14:82:17 | [ThisExpr] this | semmle.order | 1 | +| tst.ts:82:14:82:23 | [DotExpr] this.#size | tst.ts:82:19:82:23 | [Label] #size | semmle.label | 2 | +| tst.ts:82:14:82:23 | [DotExpr] this.#size | tst.ts:82:19:82:23 | [Label] #size | semmle.order | 2 | +| tst.ts:85:5:87:5 | [ClassInitializedMember,SetterMethodDefinition] set siz ... ; } | tst.ts:85:5:87:5 | [FunctionExpr] set siz ... ; } | semmle.label | 1 | +| tst.ts:85:5:87:5 | [ClassInitializedMember,SetterMethodDefinition] set siz ... ; } | tst.ts:85:5:87:5 | [FunctionExpr] set siz ... ; } | semmle.order | 1 | +| tst.ts:85:5:87:5 | [ClassInitializedMember,SetterMethodDefinition] set siz ... ; } | tst.ts:85:9:85:12 | [Label] size | semmle.label | 2 | +| tst.ts:85:5:87:5 | [ClassInitializedMember,SetterMethodDefinition] set siz ... ; } | tst.ts:85:9:85:12 | [Label] size | semmle.order | 2 | +| tst.ts:85:5:87:5 | [FunctionExpr] set siz ... ; } | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| tst.ts:85:5:87:5 | [FunctionExpr] set siz ... ; } | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| tst.ts:85:5:87:5 | [FunctionExpr] set siz ... ; } | tst.ts:85:48:87:5 | [BlockStmt] { ... ; } | semmle.label | 5 | +| tst.ts:85:5:87:5 | [FunctionExpr] set siz ... ; } | tst.ts:85:48:87:5 | [BlockStmt] { ... ; } | semmle.order | 5 | +| tst.ts:85:14:85:18 | [SimpleParameter] value | tst.ts:85:21:85:45 | [UnionTypeExpr] string ... boolean | semmle.label | 0 | +| tst.ts:85:14:85:18 | [SimpleParameter] value | tst.ts:85:21:85:45 | [UnionTypeExpr] string ... boolean | semmle.order | 0 | +| tst.ts:85:21:85:45 | [UnionTypeExpr] string ... boolean | tst.ts:85:21:85:26 | [KeywordTypeExpr] string | semmle.label | 1 | +| tst.ts:85:21:85:45 | [UnionTypeExpr] string ... boolean | tst.ts:85:21:85:26 | [KeywordTypeExpr] string | semmle.order | 1 | +| tst.ts:85:21:85:45 | [UnionTypeExpr] string ... boolean | tst.ts:85:30:85:35 | [KeywordTypeExpr] number | semmle.label | 2 | +| tst.ts:85:21:85:45 | [UnionTypeExpr] string ... boolean | tst.ts:85:30:85:35 | [KeywordTypeExpr] number | semmle.order | 2 | +| tst.ts:85:21:85:45 | [UnionTypeExpr] string ... boolean | tst.ts:85:39:85:45 | [KeywordTypeExpr] boolean | semmle.label | 3 | +| tst.ts:85:21:85:45 | [UnionTypeExpr] string ... boolean | tst.ts:85:39:85:45 | [KeywordTypeExpr] boolean | semmle.order | 3 | +| tst.ts:85:48:87:5 | [BlockStmt] { ... ; } | tst.ts:86:7:86:33 | [ExprStmt] this.#s ... value); | semmle.label | 1 | +| tst.ts:85:48:87:5 | [BlockStmt] { ... ; } | tst.ts:86:7:86:33 | [ExprStmt] this.#s ... value); | semmle.order | 1 | +| tst.ts:86:7:86:16 | [DotExpr] this.#size | tst.ts:86:7:86:10 | [ThisExpr] this | semmle.label | 1 | +| tst.ts:86:7:86:16 | [DotExpr] this.#size | tst.ts:86:7:86:10 | [ThisExpr] this | semmle.order | 1 | +| tst.ts:86:7:86:16 | [DotExpr] this.#size | tst.ts:86:12:86:16 | [Label] #size | semmle.label | 2 | +| tst.ts:86:7:86:16 | [DotExpr] this.#size | tst.ts:86:12:86:16 | [Label] #size | semmle.order | 2 | +| tst.ts:86:7:86:32 | [AssignExpr] this.#s ... (value) | tst.ts:86:7:86:16 | [DotExpr] this.#size | semmle.label | 1 | +| tst.ts:86:7:86:32 | [AssignExpr] this.#s ... (value) | tst.ts:86:7:86:16 | [DotExpr] this.#size | semmle.order | 1 | +| tst.ts:86:7:86:32 | [AssignExpr] this.#s ... (value) | tst.ts:86:20:86:32 | [CallExpr] Number(value) | semmle.label | 2 | +| tst.ts:86:7:86:32 | [AssignExpr] this.#s ... (value) | tst.ts:86:20:86:32 | [CallExpr] Number(value) | semmle.order | 2 | +| tst.ts:86:7:86:33 | [ExprStmt] this.#s ... value); | tst.ts:86:7:86:32 | [AssignExpr] this.#s ... (value) | semmle.label | 1 | +| tst.ts:86:7:86:33 | [ExprStmt] this.#s ... value); | tst.ts:86:7:86:32 | [AssignExpr] this.#s ... (value) | semmle.order | 1 | +| tst.ts:86:20:86:32 | [CallExpr] Number(value) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 | +| tst.ts:86:20:86:32 | [CallExpr] Number(value) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | +| tst.ts:86:20:86:32 | [CallExpr] Number(value) | tst.ts:86:20:86:25 | [VarRef] Number | semmle.label | 0 | +| tst.ts:86:20:86:32 | [CallExpr] Number(value) | tst.ts:86:20:86:25 | [VarRef] Number | semmle.order | 0 | +| tst.ts:91:3:95:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:91:9:91:13 | [VarDecl] Super | semmle.label | 1 | +| tst.ts:91:3:95:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:91:9:91:13 | [VarDecl] Super | semmle.order | 1 | +| tst.ts:91:3:95:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:91:15:91:14 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | 2 | +| tst.ts:91:3:95:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:91:15:91:14 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.order | 2 | +| tst.ts:91:3:95:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:92:5:94:5 | [ClassInitializedMember,MethodDefinition] random( ... ; } | semmle.label | 3 | +| tst.ts:91:3:95:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:92:5:94:5 | [ClassInitializedMember,MethodDefinition] random( ... ; } | semmle.order | 3 | +| tst.ts:91:15:91:14 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:91:15:91:14 | [FunctionExpr] () {} | semmle.label | 2 | +| tst.ts:91:15:91:14 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:91:15:91:14 | [FunctionExpr] () {} | semmle.order | 2 | +| tst.ts:91:15:91:14 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:91:15:91:14 | [Label] constructor | semmle.label | 1 | +| tst.ts:91:15:91:14 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:91:15:91:14 | [Label] constructor | semmle.order | 1 | +| tst.ts:91:15:91:14 | [FunctionExpr] () {} | tst.ts:91:15:91:14 | [BlockStmt] {} | semmle.label | 5 | +| tst.ts:91:15:91:14 | [FunctionExpr] () {} | tst.ts:91:15:91:14 | [BlockStmt] {} | semmle.order | 5 | +| tst.ts:92:5:94:5 | [ClassInitializedMember,MethodDefinition] random( ... ; } | tst.ts:92:5:92:10 | [Label] random | semmle.label | 1 | +| tst.ts:92:5:94:5 | [ClassInitializedMember,MethodDefinition] random( ... ; } | tst.ts:92:5:92:10 | [Label] random | semmle.order | 1 | +| tst.ts:92:5:94:5 | [ClassInitializedMember,MethodDefinition] random( ... ; } | tst.ts:92:5:94:5 | [FunctionExpr] random( ... ; } | semmle.label | 2 | +| tst.ts:92:5:94:5 | [ClassInitializedMember,MethodDefinition] random( ... ; } | tst.ts:92:5:94:5 | [FunctionExpr] random( ... ; } | semmle.order | 2 | +| tst.ts:92:5:94:5 | [FunctionExpr] random( ... ; } | tst.ts:92:15:92:20 | [KeywordTypeExpr] number | semmle.label | 4 | +| tst.ts:92:5:94:5 | [FunctionExpr] random( ... ; } | tst.ts:92:15:92:20 | [KeywordTypeExpr] number | semmle.order | 4 | +| tst.ts:92:5:94:5 | [FunctionExpr] random( ... ; } | tst.ts:92:22:94:5 | [BlockStmt] { ... ; } | semmle.label | 5 | +| tst.ts:92:5:94:5 | [FunctionExpr] random( ... ; } | tst.ts:92:22:94:5 | [BlockStmt] { ... ; } | semmle.order | 5 | +| tst.ts:92:22:94:5 | [BlockStmt] { ... ; } | tst.ts:93:7:93:15 | [ReturnStmt] return 4; | semmle.label | 1 | +| tst.ts:92:22:94:5 | [BlockStmt] { ... ; } | tst.ts:93:7:93:15 | [ReturnStmt] return 4; | semmle.order | 1 | +| tst.ts:93:7:93:15 | [ReturnStmt] return 4; | tst.ts:93:14:93:14 | [Literal] 4 | semmle.label | 1 | +| tst.ts:93:7:93:15 | [ReturnStmt] return 4; | tst.ts:93:14:93:14 | [Literal] 4 | semmle.order | 1 | +| tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:97:9:97:11 | [VarDecl] Sub | semmle.label | 1 | +| tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:97:9:97:11 | [VarDecl] Sub | semmle.order | 1 | +| tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:97:21:97:25 | [VarRef] Super | semmle.label | 2 | +| tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:97:21:97:25 | [VarRef] Super | semmle.order | 2 | +| tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:97:27:97:26 | [ClassInitializedMember,ConstructorDefinition] constru ... rgs); } | semmle.label | 3 | +| tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:97:27:97:26 | [ClassInitializedMember,ConstructorDefinition] constru ... rgs); } | semmle.order | 3 | +| tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:98:5:100:5 | [ClassInitializedMember,MethodDefinition] overrid ... ; } | semmle.label | 4 | +| tst.ts:97:3:101:3 | [ClassDefinition,TypeDefinition] class S ... } } | tst.ts:98:5:100:5 | [ClassInitializedMember,MethodDefinition] overrid ... ; } | semmle.order | 4 | +| tst.ts:97:27:97:26 | [BlockStmt] { super(...args); } | tst.ts:97:27:97:26 | [ExprStmt] super(...args); | semmle.label | 1 | +| tst.ts:97:27:97:26 | [BlockStmt] { super(...args); } | tst.ts:97:27:97:26 | [ExprStmt] super(...args); | semmle.order | 1 | +| tst.ts:97:27:97:26 | [CallExpr] super(...args) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 | +| tst.ts:97:27:97:26 | [CallExpr] super(...args) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | +| tst.ts:97:27:97:26 | [CallExpr] super(...args) | tst.ts:97:27:97:26 | [SuperExpr] super | semmle.label | 0 | +| tst.ts:97:27:97:26 | [CallExpr] super(...args) | tst.ts:97:27:97:26 | [SuperExpr] super | semmle.order | 0 | +| tst.ts:97:27:97:26 | [ClassInitializedMember,ConstructorDefinition] constru ... rgs); } | tst.ts:97:27:97:26 | [FunctionExpr] (...arg ... rgs); } | semmle.label | 2 | +| tst.ts:97:27:97:26 | [ClassInitializedMember,ConstructorDefinition] constru ... rgs); } | tst.ts:97:27:97:26 | [FunctionExpr] (...arg ... rgs); } | semmle.order | 2 | +| tst.ts:97:27:97:26 | [ClassInitializedMember,ConstructorDefinition] constru ... rgs); } | tst.ts:97:27:97:26 | [Label] constructor | semmle.label | 1 | +| tst.ts:97:27:97:26 | [ClassInitializedMember,ConstructorDefinition] constru ... rgs); } | tst.ts:97:27:97:26 | [Label] constructor | semmle.order | 1 | +| tst.ts:97:27:97:26 | [ExprStmt] super(...args); | tst.ts:97:27:97:26 | [CallExpr] super(...args) | semmle.label | 1 | +| tst.ts:97:27:97:26 | [ExprStmt] super(...args); | tst.ts:97:27:97:26 | [CallExpr] super(...args) | semmle.order | 1 | +| tst.ts:97:27:97:26 | [FunctionExpr] (...arg ... rgs); } | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| tst.ts:97:27:97:26 | [FunctionExpr] (...arg ... rgs); } | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| tst.ts:97:27:97:26 | [FunctionExpr] (...arg ... rgs); } | tst.ts:97:27:97:26 | [BlockStmt] { super(...args); } | semmle.label | 5 | +| tst.ts:97:27:97:26 | [FunctionExpr] (...arg ... rgs); } | tst.ts:97:27:97:26 | [BlockStmt] { super(...args); } | semmle.order | 5 | +| tst.ts:97:27:97:26 | [SpreadElement] ...args | tst.ts:97:27:97:26 | [VarRef] args | semmle.label | 1 | +| tst.ts:97:27:97:26 | [SpreadElement] ...args | tst.ts:97:27:97:26 | [VarRef] args | semmle.order | 1 | +| tst.ts:98:5:100:5 | [ClassInitializedMember,MethodDefinition] overrid ... ; } | tst.ts:98:5:100:5 | [FunctionExpr] overrid ... ; } | semmle.label | 1 | +| tst.ts:98:5:100:5 | [ClassInitializedMember,MethodDefinition] overrid ... ; } | tst.ts:98:5:100:5 | [FunctionExpr] overrid ... ; } | semmle.order | 1 | +| tst.ts:98:5:100:5 | [ClassInitializedMember,MethodDefinition] overrid ... ; } | tst.ts:98:14:98:19 | [Label] random | semmle.label | 2 | +| tst.ts:98:5:100:5 | [ClassInitializedMember,MethodDefinition] overrid ... ; } | tst.ts:98:14:98:19 | [Label] random | semmle.order | 2 | +| tst.ts:98:5:100:5 | [FunctionExpr] overrid ... ; } | tst.ts:98:24:98:29 | [KeywordTypeExpr] number | semmle.label | 4 | +| tst.ts:98:5:100:5 | [FunctionExpr] overrid ... ; } | tst.ts:98:24:98:29 | [KeywordTypeExpr] number | semmle.order | 4 | +| tst.ts:98:5:100:5 | [FunctionExpr] overrid ... ; } | tst.ts:98:31:100:5 | [BlockStmt] { ... ; } | semmle.label | 5 | +| tst.ts:98:5:100:5 | [FunctionExpr] overrid ... ; } | tst.ts:98:31:100:5 | [BlockStmt] { ... ; } | semmle.order | 5 | +| tst.ts:98:31:100:5 | [BlockStmt] { ... ; } | tst.ts:99:7:99:33 | [ReturnStmt] return ... ) * 10; | semmle.label | 1 | +| tst.ts:98:31:100:5 | [BlockStmt] { ... ; } | tst.ts:99:7:99:33 | [ReturnStmt] return ... ) * 10; | semmle.order | 1 | +| tst.ts:99:7:99:33 | [ReturnStmt] return ... ) * 10; | tst.ts:99:14:99:32 | [BinaryExpr] super.random() * 10 | semmle.label | 1 | +| tst.ts:99:7:99:33 | [ReturnStmt] return ... ) * 10; | tst.ts:99:14:99:32 | [BinaryExpr] super.random() * 10 | semmle.order | 1 | +| tst.ts:99:14:99:25 | [DotExpr] super.random | tst.ts:99:14:99:18 | [SuperExpr] super | semmle.label | 1 | +| tst.ts:99:14:99:25 | [DotExpr] super.random | tst.ts:99:14:99:18 | [SuperExpr] super | semmle.order | 1 | +| tst.ts:99:14:99:25 | [DotExpr] super.random | tst.ts:99:20:99:25 | [Label] random | semmle.label | 2 | +| tst.ts:99:14:99:25 | [DotExpr] super.random | tst.ts:99:20:99:25 | [Label] random | semmle.order | 2 | +| tst.ts:99:14:99:27 | [MethodCallExpr] super.random() | tst.ts:99:14:99:25 | [DotExpr] super.random | semmle.label | 0 | +| tst.ts:99:14:99:27 | [MethodCallExpr] super.random() | tst.ts:99:14:99:25 | [DotExpr] super.random | semmle.order | 0 | +| tst.ts:99:14:99:32 | [BinaryExpr] super.random() * 10 | tst.ts:99:14:99:27 | [MethodCallExpr] super.random() | semmle.label | 1 | +| tst.ts:99:14:99:32 | [BinaryExpr] super.random() * 10 | tst.ts:99:14:99:27 | [MethodCallExpr] super.random() | semmle.order | 1 | +| tst.ts:99:14:99:32 | [BinaryExpr] super.random() * 10 | tst.ts:99:31:99:32 | [Literal] 10 | semmle.label | 2 | +| tst.ts:99:14:99:32 | [BinaryExpr] super.random() * 10 | tst.ts:99:31:99:32 | [Literal] 10 | semmle.order | 2 | +| tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | tst.ts:104:12:104:14 | [VarDecl] bar | semmle.label | 0 | +| tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | tst.ts:104:12:104:14 | [VarDecl] bar | semmle.order | 0 | +| tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | tst.ts:104:28:104:44 | [TemplateLiteralTypeExpr] `hello ${string}` | semmle.label | 4 | +| tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | tst.ts:104:28:104:44 | [TemplateLiteralTypeExpr] `hello ${string}` | semmle.order | 4 | +| tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | tst.ts:104:46:107:3 | [BlockStmt] { / ... }`; } | semmle.label | 5 | +| tst.ts:104:3:107:3 | [FunctionDeclStmt] functio ... }`; } | tst.ts:104:46:107:3 | [BlockStmt] { / ... }`; } | semmle.order | 5 | +| tst.ts:104:16:104:16 | [SimpleParameter] s | tst.ts:104:19:104:24 | [KeywordTypeExpr] string | semmle.label | 0 | +| tst.ts:104:16:104:16 | [SimpleParameter] s | tst.ts:104:19:104:24 | [KeywordTypeExpr] string | semmle.order | 0 | +| tst.ts:104:28:104:44 | [TemplateLiteralTypeExpr] `hello ${string}` | tst.ts:104:29:104:34 | [LiteralTypeExpr] hello | semmle.label | 1 | +| tst.ts:104:28:104:44 | [TemplateLiteralTypeExpr] `hello ${string}` | tst.ts:104:29:104:34 | [LiteralTypeExpr] hello | semmle.order | 1 | +| tst.ts:104:28:104:44 | [TemplateLiteralTypeExpr] `hello ${string}` | tst.ts:104:37:104:42 | [KeywordTypeExpr] string | semmle.label | 2 | +| tst.ts:104:28:104:44 | [TemplateLiteralTypeExpr] `hello ${string}` | tst.ts:104:37:104:42 | [KeywordTypeExpr] string | semmle.order | 2 | +| tst.ts:104:46:107:3 | [BlockStmt] { / ... }`; } | tst.ts:106:5:106:24 | [ReturnStmt] return `hello ${s}`; | semmle.label | 1 | +| tst.ts:104:46:107:3 | [BlockStmt] { / ... }`; } | tst.ts:106:5:106:24 | [ReturnStmt] return `hello ${s}`; | semmle.order | 1 | +| tst.ts:106:5:106:24 | [ReturnStmt] return `hello ${s}`; | tst.ts:106:12:106:23 | [TemplateLiteral] `hello ${s}` | semmle.label | 1 | +| tst.ts:106:5:106:24 | [ReturnStmt] return `hello ${s}`; | tst.ts:106:12:106:23 | [TemplateLiteral] `hello ${s}` | semmle.order | 1 | +| tst.ts:106:12:106:23 | [TemplateLiteral] `hello ${s}` | tst.ts:106:13:106:18 | [TemplateElement] hello | semmle.label | 1 | +| tst.ts:106:12:106:23 | [TemplateLiteral] `hello ${s}` | tst.ts:106:13:106:18 | [TemplateElement] hello | semmle.order | 1 | +| tst.ts:106:12:106:23 | [TemplateLiteral] `hello ${s}` | tst.ts:106:21:106:21 | [VarRef] s | semmle.label | 2 | +| tst.ts:106:12:106:23 | [TemplateLiteral] `hello ${s}` | tst.ts:106:21:106:21 | [VarRef] s | semmle.order | 2 | +| tst.ts:109:3:109:50 | [DeclStmt] let s1 = ... | tst.ts:109:15:109:49 | [VariableDeclarator] s1: `${ ... umber}` | semmle.label | 1 | +| tst.ts:109:3:109:50 | [DeclStmt] let s1 = ... | tst.ts:109:15:109:49 | [VariableDeclarator] s1: `${ ... umber}` | semmle.order | 1 | +| tst.ts:109:15:109:49 | [VariableDeclarator] s1: `${ ... umber}` | tst.ts:109:15:109:16 | [VarDecl] s1 | semmle.label | 1 | +| tst.ts:109:15:109:49 | [VariableDeclarator] s1: `${ ... umber}` | tst.ts:109:15:109:16 | [VarDecl] s1 | semmle.order | 1 | +| tst.ts:109:15:109:49 | [VariableDeclarator] s1: `${ ... umber}` | tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | semmle.label | 2 | +| tst.ts:109:15:109:49 | [VariableDeclarator] s1: `${ ... umber}` | tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | semmle.order | 2 | +| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | tst.ts:109:22:109:27 | [KeywordTypeExpr] number | semmle.label | 1 | +| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | tst.ts:109:22:109:27 | [KeywordTypeExpr] number | semmle.order | 1 | +| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | tst.ts:109:29:109:29 | [LiteralTypeExpr] - | semmle.label | 2 | +| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | tst.ts:109:29:109:29 | [LiteralTypeExpr] - | semmle.order | 2 | +| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | tst.ts:109:32:109:37 | [KeywordTypeExpr] number | semmle.label | 3 | +| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | tst.ts:109:32:109:37 | [KeywordTypeExpr] number | semmle.order | 3 | +| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | tst.ts:109:39:109:39 | [LiteralTypeExpr] - | semmle.label | 4 | +| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | tst.ts:109:39:109:39 | [LiteralTypeExpr] - | semmle.order | 4 | +| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | tst.ts:109:42:109:47 | [KeywordTypeExpr] number | semmle.label | 5 | +| tst.ts:109:19:109:49 | [TemplateLiteralTypeExpr] `${numb ... umber}` | tst.ts:109:42:109:47 | [KeywordTypeExpr] number | semmle.order | 5 | +| tst.ts:110:3:110:26 | [DeclStmt] let s2 = ... | tst.ts:110:15:110:25 | [VariableDeclarator] s2: `1-2-3` | semmle.label | 1 | +| tst.ts:110:3:110:26 | [DeclStmt] let s2 = ... | tst.ts:110:15:110:25 | [VariableDeclarator] s2: `1-2-3` | semmle.order | 1 | +| tst.ts:110:15:110:25 | [VariableDeclarator] s2: `1-2-3` | tst.ts:110:15:110:16 | [VarDecl] s2 | semmle.label | 1 | +| tst.ts:110:15:110:25 | [VariableDeclarator] s2: `1-2-3` | tst.ts:110:15:110:16 | [VarDecl] s2 | semmle.order | 1 | +| tst.ts:110:15:110:25 | [VariableDeclarator] s2: `1-2-3` | tst.ts:110:19:110:25 | [TemplateLiteralTypeExpr] `1-2-3` | semmle.label | 2 | +| tst.ts:110:15:110:25 | [VariableDeclarator] s2: `1-2-3` | tst.ts:110:19:110:25 | [TemplateLiteralTypeExpr] `1-2-3` | semmle.order | 2 | +| tst.ts:110:19:110:25 | [TemplateLiteralTypeExpr] `1-2-3` | tst.ts:110:19:110:25 | [LiteralTypeExpr] `1-2-3` | semmle.label | 1 | +| tst.ts:110:19:110:25 | [TemplateLiteralTypeExpr] `1-2-3` | tst.ts:110:19:110:25 | [LiteralTypeExpr] `1-2-3` | semmle.order | 1 | +| tst.ts:111:3:111:34 | [DeclStmt] let s3 = ... | tst.ts:111:15:111:33 | [VariableDeclarator] s3: `${number}-2-3` | semmle.label | 1 | +| tst.ts:111:3:111:34 | [DeclStmt] let s3 = ... | tst.ts:111:15:111:33 | [VariableDeclarator] s3: `${number}-2-3` | semmle.order | 1 | +| tst.ts:111:15:111:33 | [VariableDeclarator] s3: `${number}-2-3` | tst.ts:111:15:111:16 | [VarDecl] s3 | semmle.label | 1 | +| tst.ts:111:15:111:33 | [VariableDeclarator] s3: `${number}-2-3` | tst.ts:111:15:111:16 | [VarDecl] s3 | semmle.order | 1 | +| tst.ts:111:15:111:33 | [VariableDeclarator] s3: `${number}-2-3` | tst.ts:111:19:111:33 | [TemplateLiteralTypeExpr] `${number}-2-3` | semmle.label | 2 | +| tst.ts:111:15:111:33 | [VariableDeclarator] s3: `${number}-2-3` | tst.ts:111:19:111:33 | [TemplateLiteralTypeExpr] `${number}-2-3` | semmle.order | 2 | +| tst.ts:111:19:111:33 | [TemplateLiteralTypeExpr] `${number}-2-3` | tst.ts:111:22:111:27 | [KeywordTypeExpr] number | semmle.label | 1 | +| tst.ts:111:19:111:33 | [TemplateLiteralTypeExpr] `${number}-2-3` | tst.ts:111:22:111:27 | [KeywordTypeExpr] number | semmle.order | 1 | +| tst.ts:111:19:111:33 | [TemplateLiteralTypeExpr] `${number}-2-3` | tst.ts:111:29:111:32 | [LiteralTypeExpr] -2-3 | semmle.label | 2 | +| tst.ts:111:19:111:33 | [TemplateLiteralTypeExpr] `${number}-2-3` | tst.ts:111:29:111:32 | [LiteralTypeExpr] -2-3 | semmle.order | 2 | +| tst.ts:112:3:112:9 | [AssignExpr] s1 = s2 | tst.ts:112:3:112:4 | [VarRef] s1 | semmle.label | 1 | +| tst.ts:112:3:112:9 | [AssignExpr] s1 = s2 | tst.ts:112:3:112:4 | [VarRef] s1 | semmle.order | 1 | +| tst.ts:112:3:112:9 | [AssignExpr] s1 = s2 | tst.ts:112:8:112:9 | [VarRef] s2 | semmle.label | 2 | +| tst.ts:112:3:112:9 | [AssignExpr] s1 = s2 | tst.ts:112:8:112:9 | [VarRef] s2 | semmle.order | 2 | +| tst.ts:112:3:112:10 | [ExprStmt] s1 = s2; | tst.ts:112:3:112:9 | [AssignExpr] s1 = s2 | semmle.label | 1 | +| tst.ts:112:3:112:10 | [ExprStmt] s1 = s2; | tst.ts:112:3:112:9 | [AssignExpr] s1 = s2 | semmle.order | 1 | +| tst.ts:113:3:113:9 | [AssignExpr] s1 = s3 | tst.ts:113:3:113:4 | [VarRef] s1 | semmle.label | 1 | +| tst.ts:113:3:113:9 | [AssignExpr] s1 = s3 | tst.ts:113:3:113:4 | [VarRef] s1 | semmle.order | 1 | +| tst.ts:113:3:113:9 | [AssignExpr] s1 = s3 | tst.ts:113:8:113:9 | [VarRef] s3 | semmle.label | 2 | +| tst.ts:113:3:113:9 | [AssignExpr] s1 = s3 | tst.ts:113:8:113:9 | [VarRef] s3 | semmle.order | 2 | +| tst.ts:113:3:113:10 | [ExprStmt] s1 = s3; | tst.ts:113:3:113:9 | [AssignExpr] s1 = s3 | semmle.label | 1 | +| tst.ts:113:3:113:10 | [ExprStmt] s1 = s3; | tst.ts:113:3:113:9 | [AssignExpr] s1 = s3 | semmle.order | 1 | +| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | tst.ts:116:9:116:11 | [VarDecl] Foo | semmle.label | 1 | +| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | tst.ts:116:9:116:11 | [VarDecl] Foo | semmle.order | 1 | +| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | tst.ts:116:13:116:12 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | 2 | +| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | tst.ts:116:13:116:12 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.order | 2 | +| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | tst.ts:117:5:119:5 | [ClassInitializedMember,MethodDefinition] #someMe ... ; } | semmle.label | 3 | +| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | tst.ts:117:5:119:5 | [ClassInitializedMember,MethodDefinition] #someMe ... ; } | semmle.order | 3 | +| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | tst.ts:121:5:123:5 | [ClassInitializedMember,GetterMethodDefinition] get #so ... ; } | semmle.label | 4 | +| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | tst.ts:121:5:123:5 | [ClassInitializedMember,GetterMethodDefinition] get #so ... ; } | semmle.order | 4 | +| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | tst.ts:125:5:128:5 | [ClassInitializedMember,MethodDefinition] publicM ... ; } | semmle.label | 5 | +| tst.ts:116:3:129:3 | [ClassDefinition,TypeDefinition] class F ... } } | tst.ts:125:5:128:5 | [ClassInitializedMember,MethodDefinition] publicM ... ; } | semmle.order | 5 | +| tst.ts:116:13:116:12 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:116:13:116:12 | [FunctionExpr] () {} | semmle.label | 2 | +| tst.ts:116:13:116:12 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:116:13:116:12 | [FunctionExpr] () {} | semmle.order | 2 | +| tst.ts:116:13:116:12 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:116:13:116:12 | [Label] constructor | semmle.label | 1 | +| tst.ts:116:13:116:12 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:116:13:116:12 | [Label] constructor | semmle.order | 1 | +| tst.ts:116:13:116:12 | [FunctionExpr] () {} | tst.ts:116:13:116:12 | [BlockStmt] {} | semmle.label | 5 | +| tst.ts:116:13:116:12 | [FunctionExpr] () {} | tst.ts:116:13:116:12 | [BlockStmt] {} | semmle.order | 5 | +| tst.ts:117:5:119:5 | [ClassInitializedMember,MethodDefinition] #someMe ... ; } | tst.ts:117:5:117:15 | [Label] #someMethod | semmle.label | 1 | +| tst.ts:117:5:119:5 | [ClassInitializedMember,MethodDefinition] #someMe ... ; } | tst.ts:117:5:117:15 | [Label] #someMethod | semmle.order | 1 | +| tst.ts:117:5:119:5 | [ClassInitializedMember,MethodDefinition] #someMe ... ; } | tst.ts:117:5:119:5 | [FunctionExpr] #someMe ... ; } | semmle.label | 2 | +| tst.ts:117:5:119:5 | [ClassInitializedMember,MethodDefinition] #someMe ... ; } | tst.ts:117:5:119:5 | [FunctionExpr] #someMe ... ; } | semmle.order | 2 | +| tst.ts:117:5:119:5 | [FunctionExpr] #someMe ... ; } | tst.ts:117:20:117:25 | [KeywordTypeExpr] number | semmle.label | 4 | +| tst.ts:117:5:119:5 | [FunctionExpr] #someMe ... ; } | tst.ts:117:20:117:25 | [KeywordTypeExpr] number | semmle.order | 4 | +| tst.ts:117:5:119:5 | [FunctionExpr] #someMe ... ; } | tst.ts:117:27:119:5 | [BlockStmt] { ... ; } | semmle.label | 5 | +| tst.ts:117:5:119:5 | [FunctionExpr] #someMe ... ; } | tst.ts:117:27:119:5 | [BlockStmt] { ... ; } | semmle.order | 5 | +| tst.ts:117:27:119:5 | [BlockStmt] { ... ; } | tst.ts:118:7:118:16 | [ReturnStmt] return 42; | semmle.label | 1 | +| tst.ts:117:27:119:5 | [BlockStmt] { ... ; } | tst.ts:118:7:118:16 | [ReturnStmt] return 42; | semmle.order | 1 | +| tst.ts:118:7:118:16 | [ReturnStmt] return 42; | tst.ts:118:14:118:15 | [Literal] 42 | semmle.label | 1 | +| tst.ts:118:7:118:16 | [ReturnStmt] return 42; | tst.ts:118:14:118:15 | [Literal] 42 | semmle.order | 1 | +| tst.ts:121:5:123:5 | [ClassInitializedMember,GetterMethodDefinition] get #so ... ; } | tst.ts:121:5:123:5 | [FunctionExpr] get #so ... ; } | semmle.label | 1 | +| tst.ts:121:5:123:5 | [ClassInitializedMember,GetterMethodDefinition] get #so ... ; } | tst.ts:121:5:123:5 | [FunctionExpr] get #so ... ; } | semmle.order | 1 | +| tst.ts:121:5:123:5 | [ClassInitializedMember,GetterMethodDefinition] get #so ... ; } | tst.ts:121:9:121:18 | [Label] #someValue | semmle.label | 2 | +| tst.ts:121:5:123:5 | [ClassInitializedMember,GetterMethodDefinition] get #so ... ; } | tst.ts:121:9:121:18 | [Label] #someValue | semmle.order | 2 | +| tst.ts:121:5:123:5 | [FunctionExpr] get #so ... ; } | tst.ts:121:23:121:28 | [KeywordTypeExpr] number | semmle.label | 4 | +| tst.ts:121:5:123:5 | [FunctionExpr] get #so ... ; } | tst.ts:121:23:121:28 | [KeywordTypeExpr] number | semmle.order | 4 | +| tst.ts:121:5:123:5 | [FunctionExpr] get #so ... ; } | tst.ts:121:30:123:5 | [BlockStmt] { ... ; } | semmle.label | 5 | +| tst.ts:121:5:123:5 | [FunctionExpr] get #so ... ; } | tst.ts:121:30:123:5 | [BlockStmt] { ... ; } | semmle.order | 5 | +| tst.ts:121:30:123:5 | [BlockStmt] { ... ; } | tst.ts:122:7:122:17 | [ReturnStmt] return 100; | semmle.label | 1 | +| tst.ts:121:30:123:5 | [BlockStmt] { ... ; } | tst.ts:122:7:122:17 | [ReturnStmt] return 100; | semmle.order | 1 | +| tst.ts:122:7:122:17 | [ReturnStmt] return 100; | tst.ts:122:14:122:16 | [Literal] 100 | semmle.label | 1 | +| tst.ts:122:7:122:17 | [ReturnStmt] return 100; | tst.ts:122:14:122:16 | [Literal] 100 | semmle.order | 1 | +| tst.ts:125:5:128:5 | [ClassInitializedMember,MethodDefinition] publicM ... ; } | tst.ts:125:5:125:16 | [Label] publicMethod | semmle.label | 1 | +| tst.ts:125:5:128:5 | [ClassInitializedMember,MethodDefinition] publicM ... ; } | tst.ts:125:5:125:16 | [Label] publicMethod | semmle.order | 1 | +| tst.ts:125:5:128:5 | [ClassInitializedMember,MethodDefinition] publicM ... ; } | tst.ts:125:5:128:5 | [FunctionExpr] publicM ... ; } | semmle.label | 2 | +| tst.ts:125:5:128:5 | [ClassInitializedMember,MethodDefinition] publicM ... ; } | tst.ts:125:5:128:5 | [FunctionExpr] publicM ... ; } | semmle.order | 2 | +| tst.ts:125:5:128:5 | [FunctionExpr] publicM ... ; } | tst.ts:125:20:128:5 | [BlockStmt] { ... ; } | semmle.label | 5 | +| tst.ts:125:5:128:5 | [FunctionExpr] publicM ... ; } | tst.ts:125:20:128:5 | [BlockStmt] { ... ; } | semmle.order | 5 | +| tst.ts:125:20:128:5 | [BlockStmt] { ... ; } | tst.ts:126:7:126:25 | [ExprStmt] this.#someMethod(); | semmle.label | 1 | +| tst.ts:125:20:128:5 | [BlockStmt] { ... ; } | tst.ts:126:7:126:25 | [ExprStmt] this.#someMethod(); | semmle.order | 1 | +| tst.ts:125:20:128:5 | [BlockStmt] { ... ; } | tst.ts:127:7:127:29 | [ReturnStmt] return ... eValue; | semmle.label | 2 | +| tst.ts:125:20:128:5 | [BlockStmt] { ... ; } | tst.ts:127:7:127:29 | [ReturnStmt] return ... eValue; | semmle.order | 2 | +| tst.ts:126:7:126:22 | [DotExpr] this.#someMethod | tst.ts:126:7:126:10 | [ThisExpr] this | semmle.label | 1 | +| tst.ts:126:7:126:22 | [DotExpr] this.#someMethod | tst.ts:126:7:126:10 | [ThisExpr] this | semmle.order | 1 | +| tst.ts:126:7:126:22 | [DotExpr] this.#someMethod | tst.ts:126:12:126:22 | [Label] #someMethod | semmle.label | 2 | +| tst.ts:126:7:126:22 | [DotExpr] this.#someMethod | tst.ts:126:12:126:22 | [Label] #someMethod | semmle.order | 2 | +| tst.ts:126:7:126:24 | [MethodCallExpr] this.#someMethod() | tst.ts:126:7:126:22 | [DotExpr] this.#someMethod | semmle.label | 0 | +| tst.ts:126:7:126:24 | [MethodCallExpr] this.#someMethod() | tst.ts:126:7:126:22 | [DotExpr] this.#someMethod | semmle.order | 0 | +| tst.ts:126:7:126:25 | [ExprStmt] this.#someMethod(); | tst.ts:126:7:126:24 | [MethodCallExpr] this.#someMethod() | semmle.label | 1 | +| tst.ts:126:7:126:25 | [ExprStmt] this.#someMethod(); | tst.ts:126:7:126:24 | [MethodCallExpr] this.#someMethod() | semmle.order | 1 | +| tst.ts:127:7:127:29 | [ReturnStmt] return ... eValue; | tst.ts:127:14:127:28 | [DotExpr] this.#someValue | semmle.label | 1 | +| tst.ts:127:7:127:29 | [ReturnStmt] return ... eValue; | tst.ts:127:14:127:28 | [DotExpr] this.#someValue | semmle.order | 1 | +| tst.ts:127:14:127:28 | [DotExpr] this.#someValue | tst.ts:127:14:127:17 | [ThisExpr] this | semmle.label | 1 | +| tst.ts:127:14:127:28 | [DotExpr] this.#someValue | tst.ts:127:14:127:17 | [ThisExpr] this | semmle.order | 1 | +| tst.ts:127:14:127:28 | [DotExpr] this.#someValue | tst.ts:127:19:127:28 | [Label] #someValue | semmle.label | 2 | +| tst.ts:127:14:127:28 | [DotExpr] this.#someValue | tst.ts:127:19:127:28 | [Label] #someValue | semmle.order | 2 | | type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | type_alias.ts:1:6:1:6 | [Identifier] B | semmle.label | 1 | | type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | type_alias.ts:1:6:1:6 | [Identifier] B | semmle.order | 1 | | type_alias.ts:1:1:1:17 | [TypeAliasDeclaration,TypeDefinition] type B = boolean; | type_alias.ts:1:10:1:16 | [KeywordTypeExpr] boolean | semmle.label | 2 | diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tests.expected b/javascript/ql/test/library-tests/TypeScript/Types/tests.expected index c9cad8a1443..187c0c7e6ce 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/tests.expected +++ b/javascript/ql/test/library-tests/TypeScript/Types/tests.expected @@ -15,6 +15,8 @@ getExprType | boolean-type.ts:15:5:15:12 | boolean6 | boolean | | dummy.ts:2:12:2:12 | x | number | | dummy.ts:2:16:2:16 | 5 | 5 | +| dummy.ts:4:12:4:14 | reg | RegExp | +| dummy.ts:4:18:4:23 | /ab+c/ | RegExp | | middle-rest.ts:1:5:1:7 | foo | [boolean, ...string[], number] | | middle-rest.ts:3:1:3:3 | foo | [boolean, ...string[], number] | | middle-rest.ts:3:1:3:26 | foo = [ ... ", 123] | [true, string, number] | @@ -125,6 +127,54 @@ getExprType | tst.ts:69:24:69:45 | {yetAno ... : true} | MyUnion2 | | tst.ts:69:25:69:38 | yetAnotherType | true | | tst.ts:69:41:69:44 | true | true | +| tst.ts:71:8:71:11 | TS43 | typeof TS43 in library-tests/TypeScript/Types/tst.ts | +| tst.ts:74:5:74:22 | get size(): number | number | +| tst.ts:74:9:74:12 | size | number | +| tst.ts:75:5:75:47 | set siz ... olean); | number | +| tst.ts:75:9:75:12 | size | number | +| tst.ts:75:14:75:18 | value | string \| number \| boolean | +| tst.ts:78:16:78:20 | Thing | Thing | +| tst.ts:79:13:79:13 | 0 | 0 | +| tst.ts:81:5:83:5 | get siz ... ;\\n } | number | +| tst.ts:81:9:81:12 | size | number | +| tst.ts:82:14:82:23 | this.#size | number | +| tst.ts:85:5:87:5 | set siz ... ;\\n } | number | +| tst.ts:85:9:85:12 | size | number | +| tst.ts:85:14:85:18 | value | string \| number \| boolean | +| tst.ts:86:7:86:16 | this.#size | number | +| tst.ts:86:7:86:32 | this.#s ... (value) | number | +| tst.ts:86:20:86:25 | Number | NumberConstructor | +| tst.ts:86:20:86:32 | Number(value) | number | +| tst.ts:86:27:86:31 | value | string \| number \| boolean | +| tst.ts:91:9:91:13 | Super | Super | +| tst.ts:92:5:92:10 | random | () => number | +| tst.ts:92:5:94:5 | random( ... ;\\n } | () => number | +| tst.ts:93:14:93:14 | 4 | 4 | +| tst.ts:97:9:97:11 | Sub | Sub | +| tst.ts:97:21:97:25 | Super | Super | +| tst.ts:98:5:100:5 | overrid ... ;\\n } | () => number | +| tst.ts:98:14:98:19 | random | () => number | +| tst.ts:99:14:99:25 | super.random | () => number | +| tst.ts:99:14:99:27 | super.random() | number | +| tst.ts:99:14:99:32 | super.random() * 10 | number | +| tst.ts:99:20:99:25 | random | () => number | +| tst.ts:99:31:99:32 | 10 | 10 | +| tst.ts:104:16:104:16 | s | string | +| tst.ts:106:13:106:18 | hello | any | +| tst.ts:106:21:106:21 | s | string | +| tst.ts:110:15:110:16 | s2 | "1-2-3" | +| tst.ts:112:3:112:9 | s1 = s2 | "1-2-3" | +| tst.ts:112:8:112:9 | s2 | "1-2-3" | +| tst.ts:116:9:116:11 | Foo | Foo | +| tst.ts:117:5:119:5 | #someMe ... ;\\n } | () => number | +| tst.ts:118:14:118:15 | 42 | 42 | +| tst.ts:121:5:123:5 | get #so ... ;\\n } | number | +| tst.ts:122:14:122:16 | 100 | 100 | +| tst.ts:125:5:125:16 | publicMethod | () => number | +| tst.ts:125:5:128:5 | publicM ... ;\\n } | () => number | +| tst.ts:126:7:126:22 | this.#someMethod | () => number | +| tst.ts:126:7:126:24 | this.#someMethod() | number | +| tst.ts:127:14:127:28 | this.#someValue | number | | type_alias.ts:3:5:3:5 | b | boolean | | type_alias.ts:7:5:7:5 | c | ValueOrArray | | type_alias.ts:14:9:14:32 | [proper ... ]: Json | any | @@ -178,6 +228,11 @@ getTypeDefinitionType | tst.ts:58:1:60:1 | interfa ... mber;\\n} | HasArea | | tst.ts:65:1:65:54 | type My ... true}; | MyUnion | | tst.ts:68:1:68:49 | type My ... true}; | MyUnion2 | +| tst.ts:73:3:76:3 | interfa ... n);\\n } | ThingI | +| tst.ts:78:10:88:3 | class T ... }\\n } | Thing | +| tst.ts:91:3:95:3 | class S ... }\\n } | Super | +| tst.ts:97:3:101:3 | class S ... }\\n } | Sub | +| tst.ts:116:3:129:3 | class F ... }\\n } | Foo | | type_alias.ts:1:1:1:17 | type B = boolean; | boolean | | type_alias.ts:5:1:5:50 | type Va ... ay>; | ValueOrArray | | type_alias.ts:9:1:15:13 | type Js ... Json[]; | Json | @@ -286,6 +341,34 @@ getTypeExprType | tst.ts:68:27:68:48 | {yetAno ... : true} | { yetAnotherType: true; } | | tst.ts:68:44:68:47 | true | true | | tst.ts:69:13:69:20 | MyUnion2 | MyUnion2 | +| tst.ts:73:13:73:18 | ThingI | ThingI | +| tst.ts:74:17:74:22 | number | number | +| tst.ts:75:21:75:26 | number | number | +| tst.ts:75:21:75:45 | number ... boolean | string \| number \| boolean | +| tst.ts:75:30:75:35 | string | string | +| tst.ts:75:39:75:45 | boolean | boolean | +| tst.ts:78:33:78:38 | ThingI | ThingI | +| tst.ts:81:17:81:22 | number | number | +| tst.ts:85:21:85:26 | string | string | +| tst.ts:85:21:85:45 | string ... boolean | string \| number \| boolean | +| tst.ts:85:30:85:35 | number | number | +| tst.ts:85:39:85:45 | boolean | boolean | +| tst.ts:92:15:92:20 | number | number | +| tst.ts:98:24:98:29 | number | number | +| tst.ts:104:19:104:24 | string | string | +| tst.ts:104:29:104:34 | hello | any | +| tst.ts:104:37:104:42 | string | string | +| tst.ts:109:22:109:27 | number | number | +| tst.ts:109:29:109:29 | - | any | +| tst.ts:109:32:109:37 | number | number | +| tst.ts:109:39:109:39 | - | any | +| tst.ts:109:42:109:47 | number | number | +| tst.ts:110:19:110:25 | `1-2-3` | "1-2-3" | +| tst.ts:110:19:110:25 | `1-2-3` | any | +| tst.ts:111:22:111:27 | number | number | +| tst.ts:111:29:111:32 | -2-3 | any | +| tst.ts:117:20:117:25 | number | number | +| tst.ts:121:23:121:28 | number | number | | type_alias.ts:1:6:1:6 | B | boolean | | type_alias.ts:1:10:1:16 | boolean | boolean | | type_alias.ts:3:8:3:8 | B | boolean | @@ -358,6 +441,7 @@ referenceDefinition | Color.red | type_definitions.ts:14:3:14:5 | red | | E | type_definition_objects.ts:6:8:6:16 | enum E {} | | EnumWithOneMember | type_definitions.ts:18:26:18:31 | member | +| Foo | tst.ts:116:3:129:3 | class F ... }\\n } | | HasArea | tst.ts:58:1:60:1 | interfa ... mber;\\n} | | I | type_definitions.ts:3:1:5:1 | interfa ... x: S;\\n} | | I | type_definitions.ts:3:1:5:1 | interfa ... x: S;\\n} | @@ -365,6 +449,11 @@ referenceDefinition | MyUnion | tst.ts:65:1:65:54 | type My ... true}; | | MyUnion2 | tst.ts:68:1:68:49 | type My ... true}; | | NonAbstractDummy | tst.ts:54:1:56:1 | interfa ... mber;\\n} | +| Sub | tst.ts:97:3:101:3 | class S ... }\\n } | +| Super | tst.ts:91:3:95:3 | class S ... }\\n } | +| Super | tst.ts:91:3:95:3 | class S ... }\\n } | +| Thing | tst.ts:78:10:88:3 | class T ... }\\n } | +| ThingI | tst.ts:73:3:76:3 | interfa ... n);\\n } | | ValueOrArray | type_alias.ts:5:1:5:50 | type Va ... ay>; | | ValueOrArray | type_alias.ts:5:1:5:50 | type Va ... ay>; | | VirtualNode | type_alias.ts:19:1:21:57 | type Vi ... ode[]]; | diff --git a/javascript/ql/test/library-tests/TypeScript/Types/tst.ts b/javascript/ql/test/library-tests/TypeScript/Types/tst.ts index 68c4ca6ebdd..389770984fd 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/tst.ts +++ b/javascript/ql/test/library-tests/TypeScript/Types/tst.ts @@ -66,4 +66,65 @@ type MyUnion = {myUnion: true} | {stillMyUnion: true}; let union1: MyUnion = {myUnion: true}; type MyUnion2 = MyUnion | {yetAnotherType: true}; -let union2: MyUnion2 = {yetAnotherType: true}; \ No newline at end of file +let union2: MyUnion2 = {yetAnotherType: true}; + +module TS43 { + // TypeScript 4.3 setter/getter types + interface ThingI { + get size(): number + set size(value: number | string | boolean); + } + + export class Thing implements ThingI { + #size = 0; + + get size(): number { + return this.#size; + } + + set size(value: string | number | boolean) { + this.#size = Number(value); + } + } + + // overrides + class Super { + random(): number { + return 4; + } + } + + class Sub extends Super { + override random(): number { + return super.random() * 10; + } + } + + // inference of template strings. + function bar(s: string): `hello ${string}` { + // Previously an error, now works! + return `hello ${s}`; + } + + declare let s1: `${number}-${number}-${number}`; + declare let s2: `1-2-3`; + declare let s3: `${number}-2-3`; + s1 = s2; + s1 = s3; + + // private methods + class Foo { + #someMethod(): number { + return 42; + } + + get #someValue(): number { + return 100; + } + + publicMethod() { + this.#someMethod(); + return this.#someValue; + } + } +} \ No newline at end of file diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected b/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected index e448b888e12..827be151d5b 100644 --- a/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected +++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected @@ -87,6 +87,8 @@ test_ClientRequest | tst.js:271:3:271:61 | proxy.w ... 080' }) | | tst.js:274:1:283:2 | httpPro ... true\\n}) | | tst.js:286:20:286:55 | new Web ... :8080') | +| tst.js:296:5:299:6 | axios({ ... \\n }) | +| tst.js:312:12:312:36 | fetchPo ... o/bar') | test_getADataNode | tst.js:53:5:53:23 | axios({data: data}) | tst.js:53:18:53:21 | data | | tst.js:57:5:57:39 | axios.p ... data2}) | tst.js:57:19:57:23 | data1 | @@ -227,6 +229,9 @@ test_getUrl | tst.js:271:3:271:61 | proxy.w ... 080' }) | tst.js:271:33:271:58 | 'http:/ ... m:8080' | | tst.js:274:1:283:2 | httpPro ... true\\n}) | tst.js:275:13:281:5 | {\\n ... ,\\n } | | tst.js:286:20:286:55 | new Web ... :8080') | tst.js:286:34:286:54 | 'ws://l ... t:8080' | +| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:296:11:299:5 | {\\n ... ,\\n } | +| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:298:14:298:44 | "http:/ ... -axios" | +| tst.js:312:12:312:36 | fetchPo ... o/bar') | tst.js:312:26:312:35 | '/foo/bar' | test_getAResponseDataNode | tst.js:19:5:19:23 | requestPromise(url) | tst.js:19:5:19:23 | requestPromise(url) | text | true | | tst.js:21:5:21:23 | superagent.get(url) | tst.js:21:5:21:23 | superagent.get(url) | stream | true | @@ -294,3 +299,8 @@ test_getAResponseDataNode | tst.js:235:5:237:6 | needle. ... \\n }) | tst.js:235:67:235:70 | resp | fetch.response | false | | tst.js:235:5:237:6 | needle. ... \\n }) | tst.js:235:73:235:76 | body | json | false | | tst.js:286:20:286:55 | new Web ... :8080') | tst.js:291:44:291:53 | event.data | json | false | +| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:296:5:299:6 | axios({ ... \\n }) | json | true | +| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:302:28:302:39 | err.response | json | false | +| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:303:26:303:37 | err.response | json | false | +| tst.js:296:5:299:6 | axios({ ... \\n }) | tst.js:304:27:304:38 | err.response | json | false | +| tst.js:312:12:312:36 | fetchPo ... o/bar') | tst.js:312:12:312:36 | fetchPo ... o/bar') | fetch.response | true | diff --git a/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js b/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js index bc15565c072..b2b9d8256ca 100644 --- a/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js +++ b/javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js @@ -290,4 +290,27 @@ function webSocket() { socket.addEventListener('message', function (event) { console.log("Data from server: " + event.data); }); -} \ No newline at end of file +} + +function moreAxios() { + axios({ + method: 'GET', + url: "http://example.org/more-axios", + }).then( + x => res.send(x.data), + (err) => { + const status = err.response.status; + const data = err.response.data; + const agent = err.response.headers.useragent; + } + ); +} + +import { fetch as fetchPolyfill } from 'whatwg-fetch' + +function usePolyfill() { + return fetchPolyfill('/foo/bar') + .then(function (response) { + return response.text() + }) +} diff --git a/javascript/ql/test/library-tests/frameworks/Electron/tests.expected b/javascript/ql/test/library-tests/frameworks/Electron/tests.expected index 4741bea1469..3050fb8fa53 100644 --- a/javascript/ql/test/library-tests/frameworks/Electron/tests.expected +++ b/javascript/ql/test/library-tests/frameworks/Electron/tests.expected @@ -15,8 +15,6 @@ browserObject | electron.js:62:13:62:59 | new Bro ... 1500 }) | | electron.js:63:3:63:5 | win | | electron.js:65:18:65:20 | win | -| electronTs.d.ts:2:16:2:28 | BrowserWindow | -| electronTs.d.ts:3:16:3:26 | BrowserView | | electronTs.ts:3:12:3:13 | bw | | electronTs.ts:3:40:3:41 | bv | | electronTs.ts:4:3:4:4 | bw | diff --git a/javascript/ql/test/library-tests/frameworks/Express/tests.expected b/javascript/ql/test/library-tests/frameworks/Express/tests.expected index c6b9ec97fc0..586a404b48a 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/tests.expected +++ b/javascript/ql/test/library-tests/frameworks/Express/tests.expected @@ -1422,6 +1422,7 @@ test_appCreation | src/express.js:2:11:2:19 | express() | | src/inheritedFromNode.js:2:11:2:19 | express() | | src/params.js:2:11:2:19 | express() | +| src/params.js:4:1:12:2 | app.par ... }\\n}) | | src/responseExprs.js:2:11:2:19 | express() | | src/routesetups.js:7:11:7:32 | express ... erver() | | src/subrouter.js:2:11:2:19 | express() | @@ -1519,6 +1520,7 @@ test_RouteExpr | src/express.js:46:1:51:2 | app.pos ... me];\\n}) | src/express.js:2:11:2:19 | express() | | src/inheritedFromNode.js:4:1:8:2 | app.pos ... url;\\n}) | src/inheritedFromNode.js:2:11:2:19 | express() | | src/params.js:4:1:12:2 | app.par ... }\\n}) | src/params.js:2:11:2:19 | express() | +| src/params.js:4:1:12:2 | app.par ... }\\n}) | src/params.js:4:1:12:2 | app.par ... }\\n}) | | src/params.js:14:1:16:2 | app.get ... o");\\n}) | src/params.js:2:11:2:19 | express() | | src/responseExprs.js:4:1:6:2 | app.get ... res1\\n}) | src/responseExprs.js:2:11:2:19 | express() | | src/responseExprs.js:7:1:9:2 | app.get ... es2;\\n}) | src/responseExprs.js:2:11:2:19 | express() | @@ -2174,6 +2176,7 @@ test_isRouterCreation | src/express.js:2:11:2:19 | express() | | src/inheritedFromNode.js:2:11:2:19 | express() | | src/params.js:2:11:2:19 | express() | +| src/params.js:4:1:12:2 | app.par ... }\\n}) | | src/responseExprs.js:2:11:2:19 | express() | | src/route.js:2:14:2:29 | express.Router() | | src/routesetups.js:3:1:3:16 | express.Router() | @@ -2264,6 +2267,7 @@ test_RouterDefinition_RouterDefinition | src/express.js:2:11:2:19 | express() | | src/inheritedFromNode.js:2:11:2:19 | express() | | src/params.js:2:11:2:19 | express() | +| src/params.js:4:1:12:2 | app.par ... }\\n}) | | src/responseExprs.js:2:11:2:19 | express() | | src/route.js:2:14:2:29 | express.Router() | | src/routesetups.js:3:1:3:16 | express.Router() | diff --git a/javascript/ql/test/library-tests/frameworks/Nest/Consistency.expected b/javascript/ql/test/library-tests/frameworks/Nest/Consistency.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/javascript/ql/test/library-tests/frameworks/Nest/Consistency.ql b/javascript/ql/test/library-tests/frameworks/Nest/Consistency.ql new file mode 100644 index 00000000000..787d0a5fdc4 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Nest/Consistency.ql @@ -0,0 +1,3 @@ +import testUtilities.ConsistencyChecking +import semmle.javascript.security.dataflow.ReflectedXss +import semmle.javascript.security.dataflow.ServerSideUrlRedirect diff --git a/javascript/ql/test/library-tests/frameworks/Nest/global/app.ts b/javascript/ql/test/library-tests/frameworks/Nest/global/app.ts new file mode 100644 index 00000000000..132f2162a9f --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Nest/global/app.ts @@ -0,0 +1,10 @@ +import { NestFactory } from '@nestjs/core'; +import { ValidationPipe } from '@nestjs/common'; +import { AppModule } from './app.module'; + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + app.useGlobalPipes(new ValidationPipe()); + await app.listen(3000); +} +bootstrap(); diff --git a/javascript/ql/test/library-tests/frameworks/Nest/global/validation.ts b/javascript/ql/test/library-tests/frameworks/Nest/global/validation.ts new file mode 100644 index 00000000000..4bf67eff4cb --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Nest/global/validation.ts @@ -0,0 +1,15 @@ +import { Get, Query } from '@nestjs/common'; +import { IsIn } from 'class-validator'; + +export class Controller { + @Get() + route1(@Query('x') validatedObj: Struct, @Query('y') unvalidated: string) { + if (Math.random()) return unvalidated; // NOT OK + return validatedObj.key; // OK + } +} + +class Struct { + @IsIn(['foo', 'bar']) + key: string; +} diff --git a/javascript/ql/test/library-tests/frameworks/Nest/local/customDecorator.ts b/javascript/ql/test/library-tests/frameworks/Nest/local/customDecorator.ts new file mode 100644 index 00000000000..032d7032bc0 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Nest/local/customDecorator.ts @@ -0,0 +1,26 @@ +import { Get, createParamDecorator, ExecutionContext } from '@nestjs/common'; + +export const SneakyQueryParam = createParamDecorator( + (data: unknown, ctx: ExecutionContext) => { + const request = ctx.switchToHttp().getRequest(); + return request.query.sneakyQueryParam; + }, +); + +export const SafeParam = createParamDecorator( + (data: unknown, ctx: ExecutionContext) => { + return 'Safe'; + }, +); + +export class Controller { + @Get() + sneaky(@SneakyQueryParam() value) { + return value; // NOT OK + } + + @Get() + safe(@SafeParam() value) { + return value; // OK + } +} diff --git a/javascript/ql/test/library-tests/frameworks/Nest/local/customPipe.ts b/javascript/ql/test/library-tests/frameworks/Nest/local/customPipe.ts new file mode 100644 index 00000000000..58f6084ab0b --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Nest/local/customPipe.ts @@ -0,0 +1,50 @@ +import { Get, Injectable, PipeTransform, Query, UsePipes } from '@nestjs/common'; + +@Injectable() +export class CustomSanitizingPipe implements PipeTransform { + transform(value: string): number | undefined { + if (value == null) return undefined; + return Number(value); + } +} + +@Injectable() +export class CustomPropagatingPipe implements PipeTransform { + transform(value: string): string { + return value.toUpperCase() + '!'; + } +} + +export class Controller { + @Get() + sanitizingPipe1(@Query('x', CustomSanitizingPipe) sanitized: number): string { + return '' + sanitized; // OK + } + + @Get() + sanitizingPipe2(@Query('x', new CustomSanitizingPipe()) sanitized: number): string { + return '' + sanitized; // OK + } + + @Get() + @UsePipes(CustomSanitizingPipe) + sanitizingPipe3(@Query('x') sanitized: number): string { + return '' + sanitized; // OK + } + + @Get() + propagatingPipe1(@Query('x', CustomPropagatingPipe) unsanitized: string): string { + return '' + unsanitized; // NOT OK + } + + @Get() + propagatingPipe2(@Query('x', new CustomPropagatingPipe()) unsanitized: string): string { + return '' + unsanitized; // NOT OK + } + + @Get() + @UsePipes(CustomPropagatingPipe) + propagatingPipe3(@Query('x') unsanitized: string): string { + return '' + unsanitized; // NOT OK + } +} diff --git a/javascript/ql/test/library-tests/frameworks/Nest/local/routes.ts b/javascript/ql/test/library-tests/frameworks/Nest/local/routes.ts new file mode 100644 index 00000000000..b94c3942318 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Nest/local/routes.ts @@ -0,0 +1,74 @@ +import { Get, Post, All, Query, Param, Body, Redirect, Req, Res, UploadedFile, UploadedFiles } from '@nestjs/common'; +import { SneakyQueryParam } from './customDecorator'; + +export class TestController { + @Get('foo') + getFoo() { + return 'foo'; + } + + @Post('foo') + postFoo() { + return 'foo'; + } + + @Get() + getRoot() { + return 'foo'; + } + + @All('bar') + bar() { + return 'bar'; + } + + @Get('requestInputs/:x') + requestInputs( + @Param('x') x, + @Query() queryObj, + @Query('name') name, + @Req() req + ) { + if (Math.random()) return x; // NOT OK + if (Math.random()) return queryObj; // NOT OK + if (Math.random()) return name; // NOT OK + if (Math.random()) return req.query.abc; // NOT OK + return; + } + + @Post('post') + post(@Body() body) { + return body.x; // NOT OK + } + + @Get('redir') + @Redirect('https://example.com') + redir() { + return { + url: '//other.example.com' // OK + }; + } + + @Get('redir') + @Redirect('https://example.com') + redir2(@Query('redirect') target) { + return { + url: target // NOT OK + }; + } + + @Get() + explicitSend(@Req() req, @Res() res) { + res.send(req.query.x) // NOT OK + } + + @Post() + upload(@UploadedFile() file) { + return file.originalname; // NOT OK + } + + @Post() + uploadMany(@UploadedFiles() files) { + return files[0].originalname; // NOT OK + } +} diff --git a/javascript/ql/test/library-tests/frameworks/Nest/local/validation.ts b/javascript/ql/test/library-tests/frameworks/Nest/local/validation.ts new file mode 100644 index 00000000000..d9771c195e1 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Nest/local/validation.ts @@ -0,0 +1,51 @@ +import { Get, Query, UsePipes, ValidationPipe } from '@nestjs/common'; +import { IsIn } from 'class-validator'; + +export class Controller { + @Get() + route1(@Query('x', new ValidationPipe()) validatedObj: Struct) { + return validatedObj.key; // OK + } + + @Get() + route2(@Query('x', ValidationPipe) validatedObj: Struct) { + return validatedObj.key; // OK + } + + @Get() + @UsePipes(new ValidationPipe()) + route3(@Query('x') validatedObj: Struct, @Query('y') unvalidated: string) { + if (Math.random()) return validatedObj.key; // OK + return unvalidated; // NOT OK + } + + @Get() + @UsePipes(ValidationPipe) + route4(@Query('x') validatedObj: Struct, @Query('y') unvalidated: string) { + if (Math.random()) return validatedObj.key; // OK + return unvalidated; // NOT OK + } +} + +@UsePipes(new ValidationPipe()) +export class Controller2 { + @Get() + route5(@Query('x') validatedObj: Struct, @Query('y') unvalidated: string) { + if (Math.random()) return validatedObj.key; // OK + return unvalidated; // NOT OK + } +} + +@UsePipes(ValidationPipe) +export class Controller3 { + @Get() + route6(@Query('x') validatedObj: Struct, @Query('y') unvalidated: string) { + if (Math.random()) return validatedObj.key; // OK + return unvalidated; // NOT OK + } +} + +class Struct { + @IsIn(['foo', 'bar']) + key: string; +} diff --git a/javascript/ql/test/library-tests/frameworks/Nest/test.expected b/javascript/ql/test/library-tests/frameworks/Nest/test.expected new file mode 100644 index 00000000000..c659295f552 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Nest/test.expected @@ -0,0 +1,90 @@ +routeHandler +| global/validation.ts:6:3:9:3 | route1( ... OK\\n } | +| local/customDecorator.ts:18:3:20:3 | sneaky( ... OK\\n } | +| local/customDecorator.ts:23:3:25:3 | safe(@S ... OK\\n } | +| local/customPipe.ts:20:5:22:5 | sanitiz ... K\\n } | +| local/customPipe.ts:25:5:27:5 | sanitiz ... K\\n } | +| local/customPipe.ts:31:5:33:5 | sanitiz ... K\\n } | +| local/customPipe.ts:36:5:38:5 | propaga ... K\\n } | +| local/customPipe.ts:41:5:43:5 | propaga ... K\\n } | +| local/customPipe.ts:47:5:49:5 | propaga ... K\\n } | +| local/routes.ts:6:3:8:3 | getFoo( ... o';\\n } | +| local/routes.ts:11:3:13:3 | postFoo ... o';\\n } | +| local/routes.ts:16:3:18:3 | getRoot ... o';\\n } | +| local/routes.ts:21:3:23:3 | bar() { ... r';\\n } | +| local/routes.ts:26:3:37:3 | request ... rn;\\n } | +| local/routes.ts:40:3:42:3 | post(@B ... OK\\n } | +| local/routes.ts:46:3:50:3 | redir() ... };\\n } | +| local/routes.ts:54:3:58:3 | redir2( ... };\\n } | +| local/routes.ts:61:3:63:3 | explici ... OK\\n } | +| local/routes.ts:66:3:68:3 | upload( ... OK\\n } | +| local/routes.ts:71:3:73:3 | uploadM ... OK\\n } | +| local/validation.ts:6:3:8:3 | route1( ... OK\\n } | +| local/validation.ts:11:3:13:3 | route2( ... OK\\n } | +| local/validation.ts:17:3:20:3 | route3( ... OK\\n } | +| local/validation.ts:24:3:27:3 | route4( ... OK\\n } | +| local/validation.ts:33:3:36:3 | route5( ... OK\\n } | +| local/validation.ts:42:3:45:3 | route6( ... OK\\n } | +requestSource +| local/customDecorator.ts:5:21:5:51 | ctx.swi ... quest() | +| local/routes.ts:30:12:30:14 | req | +| local/routes.ts:61:23:61:25 | req | +responseSource +| local/routes.ts:61:35:61:37 | res | +requestInputAccess +| body | local/routes.ts:40:16:40:19 | body | +| body | local/routes.ts:66:26:66:29 | file | +| body | local/routes.ts:71:31:71:35 | files | +| parameter | global/validation.ts:6:22:6:33 | validatedObj | +| parameter | global/validation.ts:6:56:6:66 | unvalidated | +| parameter | local/customDecorator.ts:6:12:6:41 | request ... ryParam | +| parameter | local/customPipe.ts:5:15:5:19 | value | +| parameter | local/customPipe.ts:13:15:13:19 | value | +| parameter | local/routes.ts:27:17:27:17 | x | +| parameter | local/routes.ts:28:14:28:21 | queryObj | +| parameter | local/routes.ts:29:20:29:23 | name | +| parameter | local/routes.ts:35:31:35:43 | req.query.abc | +| parameter | local/routes.ts:54:29:54:34 | target | +| parameter | local/routes.ts:62:14:62:24 | req.query.x | +| parameter | local/validation.ts:6:44:6:55 | validatedObj | +| parameter | local/validation.ts:11:38:11:49 | validatedObj | +| parameter | local/validation.ts:17:22:17:33 | validatedObj | +| parameter | local/validation.ts:17:56:17:66 | unvalidated | +| parameter | local/validation.ts:24:22:24:33 | validatedObj | +| parameter | local/validation.ts:24:56:24:66 | unvalidated | +| parameter | local/validation.ts:33:22:33:33 | validatedObj | +| parameter | local/validation.ts:33:56:33:66 | unvalidated | +| parameter | local/validation.ts:42:22:42:33 | validatedObj | +| parameter | local/validation.ts:42:56:42:66 | unvalidated | +responseSendArgument +| global/validation.ts:7:31:7:41 | unvalidated | +| global/validation.ts:8:12:8:27 | validatedObj.key | +| local/customDecorator.ts:19:12:19:16 | value | +| local/customDecorator.ts:24:12:24:16 | value | +| local/customPipe.ts:21:16:21:29 | '' + sanitized | +| local/customPipe.ts:26:16:26:29 | '' + sanitized | +| local/customPipe.ts:32:16:32:29 | '' + sanitized | +| local/customPipe.ts:37:16:37:31 | '' + unsanitized | +| local/customPipe.ts:42:16:42:31 | '' + unsanitized | +| local/customPipe.ts:48:16:48:31 | '' + unsanitized | +| local/routes.ts:32:31:32:31 | x | +| local/routes.ts:33:31:33:38 | queryObj | +| local/routes.ts:34:31:34:34 | name | +| local/routes.ts:35:31:35:43 | req.query.abc | +| local/routes.ts:41:12:41:17 | body.x | +| local/routes.ts:62:14:62:24 | req.query.x | +| local/routes.ts:67:12:67:28 | file.originalname | +| local/routes.ts:72:12:72:32 | files[0 ... nalname | +| local/validation.ts:7:12:7:27 | validatedObj.key | +| local/validation.ts:12:12:12:27 | validatedObj.key | +| local/validation.ts:18:31:18:46 | validatedObj.key | +| local/validation.ts:19:12:19:22 | unvalidated | +| local/validation.ts:25:31:25:46 | validatedObj.key | +| local/validation.ts:26:12:26:22 | unvalidated | +| local/validation.ts:34:31:34:46 | validatedObj.key | +| local/validation.ts:35:12:35:22 | unvalidated | +| local/validation.ts:43:31:43:46 | validatedObj.key | +| local/validation.ts:44:12:44:22 | unvalidated | +redirectSink +| local/routes.ts:48:12:48:32 | '//othe ... le.com' | +| local/routes.ts:56:12:56:17 | target | diff --git a/javascript/ql/test/library-tests/frameworks/Nest/test.ql b/javascript/ql/test/library-tests/frameworks/Nest/test.ql new file mode 100644 index 00000000000..120727d2548 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Nest/test.ql @@ -0,0 +1,19 @@ +import javascript +private import semmle.javascript.security.dataflow.ServerSideUrlRedirectCustomizations + +query HTTP::RouteHandler routeHandler() { any() } + +query HTTP::Servers::RequestSource requestSource() { any() } + +query HTTP::Servers::ResponseSource responseSource() { any() } + +query RemoteFlowSource requestInputAccess(string kind) { + kind = result.(HTTP::RequestInputAccess).getKind() + or + not result instanceof HTTP::RequestInputAccess and + kind = "RemoteFlowSource" +} + +query HTTP::ResponseSendArgument responseSendArgument() { any() } + +query ServerSideUrlRedirect::Sink redirectSink() { any() } diff --git a/javascript/ql/test/library-tests/frameworks/Nest/tsconfig.json b/javascript/ql/test/library-tests/frameworks/Nest/tsconfig.json new file mode 100644 index 00000000000..c5df21cd7d4 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Nest/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "experimentalDecorators": true + }, + "include": ["."] +} diff --git a/javascript/ql/test/library-tests/frameworks/Redux/exportedReducer.js b/javascript/ql/test/library-tests/frameworks/Redux/exportedReducer.js new file mode 100644 index 00000000000..4331d0f2157 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Redux/exportedReducer.js @@ -0,0 +1,13 @@ +import { combineReducers } from 'redux'; + +export default (state, action) => { + return state; +}; + +export function notAReducer(notState, notAction) { + console.log(notState, notAction); +} + +export const nestedReducer = combineReducers({ + inner: (state, action) => state +}); diff --git a/javascript/ql/test/library-tests/frameworks/Redux/react-redux.jsx b/javascript/ql/test/library-tests/frameworks/Redux/react-redux.jsx new file mode 100644 index 00000000000..e1843b5bc92 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/Redux/react-redux.jsx @@ -0,0 +1,99 @@ +import * as React from 'react'; +import { connect, useDispatch } from 'react-redux'; +import * as rt from '@reduxjs/toolkit'; + +const toolkitAction = rt.createAction('toolkitAction', (x) => { + return { + toolkitValue: x + } +}); +const toolkitReducer = rt.createReducer({}, builder => { + builder + .addCase(toolkitAction, (state, action) => { + return { + value: action.payload.toolkitValue, + ...state + }; + }) + .addCase(asyncAction.fulfilled, (state, action) => { + return { + asyncValue: action.payload.x, + ...state + }; + }); +}); + +function manualAction(x) { + return { + type: 'manualAction', + payload: x + } +} +function manualReducer(state, action) { + switch (action.type) { + case 'manualAction': { + return { ...state, manualValue: action.payload }; + } + } + if (action.type === 'manualAction') { + return { ...state, manualValue2: action.payload }; + } + if (action.type === 'manualAction') { + return { + ...state, + manualValue3: [1, 2, 3].map(x => { + return action.payload + x; + }) + }; + } + return state; +} +const asyncAction = rt.createAsyncThunk('asyncAction', (x) => { + return new Promise((resolve) => { + setTimeout(() => { + resolve({ x }); + }, 10) + }); +}); + +const store = rt.createStore(rt.combineReducers({ + toolkit: toolkitReducer, + manual: manualReducer, +})); + +function MyComponent(props) { + let dispatch = useDispatch(); + const clickHandler = React.useCallback(() => { + props.toolkitAction(source()); + props.manualAction(source()); // not currently propagated as functions are not type-tracked + dispatch(manualAction(source())); + dispatch(asyncAction(source())); + }); + + sink(props.propFromToolkitAction); // NOT OK + sink(props.propFromManualAction); // NOT OK + sink(props.propFromManualAction2); // NOT OK + sink(props.propFromManualAction3); // NOT OK + sink(props.propFromAsync); // NOT OK + + return