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/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 ffe9ac44879..582e4c7b6dc 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -250,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" @@ -439,7 +443,7 @@ ], "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", diff --git a/cpp/change-notes/2021-26-04-more-sound-expr-might-overflow.md b/cpp/change-notes/2021-04-26-more-sound-expr-might-overflow.md similarity index 100% rename from cpp/change-notes/2021-26-04-more-sound-expr-might-overflow.md rename to cpp/change-notes/2021-04-26-more-sound-expr-might-overflow.md diff --git a/cpp/change-notes/2021-10-05-comparison-with-wider-type.md b/cpp/change-notes/2021-05-10-comparison-with-wider-type.md similarity index 100% rename from cpp/change-notes/2021-10-05-comparison-with-wider-type.md rename to cpp/change-notes/2021-05-10-comparison-with-wider-type.md diff --git a/cpp/change-notes/2021-12-05-uncontrolled-arithmetic.md b/cpp/change-notes/2021-05-12-uncontrolled-arithmetic.md similarity index 100% rename from cpp/change-notes/2021-12-05-uncontrolled-arithmetic.md rename to cpp/change-notes/2021-05-12-uncontrolled-arithmetic.md 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/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 011c38ff734..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 diff --git a/cpp/ql/src/Critical/ReturnStackAllocatedObject.ql b/cpp/ql/src/Critical/ReturnStackAllocatedObject.ql index e3873e487dd..72ff93e24ab 100644 --- a/cpp/ql/src/Critical/ReturnStackAllocatedObject.ql +++ b/cpp/ql/src/Critical/ReturnStackAllocatedObject.ql @@ -4,6 +4,7 @@ * @kind problem * @id cpp/return-stack-allocated-object * @problem.severity warning + * @security-severity 2.9 * @tags reliability * security * external/cwe/cwe-562 diff --git a/cpp/ql/src/Critical/ReturnValueIgnored.qhelp b/cpp/ql/src/Critical/ReturnValueIgnored.qhelp index de0636987a0..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. 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 ecd11db43fb..6da994e6729 100644 --- a/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.ql +++ b/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.ql @@ -5,6 +5,7 @@ * unsigned integer values. * @kind problem * @problem.severity warning + * @security-severity 5.9 * @precision high * @id cpp/signed-overflow-check * @tags correctness diff --git a/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql b/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql index 66e18c4f677..19e50a3f368 100644 --- a/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql +++ b/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql @@ -6,6 +6,7 @@ * 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 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 566758c10bd..78427655c22 100644 --- a/cpp/ql/src/Likely Bugs/Format/SnprintfOverflow.ql +++ b/cpp/ql/src/Likely Bugs/Format/SnprintfOverflow.ql @@ -3,6 +3,7 @@ * @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 diff --git a/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.ql b/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.ql index 9412364183c..1147c6c66a1 100644 --- a/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.ql +++ b/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.ql @@ -4,6 +4,7 @@ * a source of security issues. * @kind problem * @problem.severity error + * @security-severity 2.9 * @precision high * @id cpp/wrong-number-format-arguments * @tags reliability 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/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/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 134f6101ea1..3035d3ba2ea 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow.ql @@ -4,6 +4,7 @@ * 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 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/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 0d3aee42c54..746a2761e49 100644 --- a/cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql +++ b/cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql @@ -6,6 +6,7 @@ * @kind path-problem * @id cpp/unsafe-use-of-this * @problem.severity error + * @security-severity 3.6 * @precision very-high * @tags correctness * language-features diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.ql b/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.ql index b803f2b1fbc..3196143c5d1 100644 --- a/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.ql +++ b/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.ql @@ -7,6 +7,7 @@ * undefined data. * @kind problem * @problem.severity error + * @security-severity 2.9 * @precision very-high * @id cpp/too-few-arguments * @tags correctness 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 6aad6cca7ce..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 @@ -19,7 +20,11 @@ import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis import TaintedWithPath predicate isUnboundedRandCall(FunctionCall fc) { - fc.getTarget().getName() = "rand" and not bounded(fc) + exists(Function func | func = fc.getTarget() | + func.hasGlobalOrStdOrBslName("rand") and + not bounded(fc) and + func.getNumberOfParameters() = 0 + ) } /** @@ -84,6 +89,10 @@ predicate bounded(Expr e) { 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) { 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 2d21ed8a3b2..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 diff --git a/cpp/ql/src/Security/CWE/CWE-190/IntegerOverflowTainted.ql b/cpp/ql/src/Security/CWE/CWE-190/IntegerOverflowTainted.ql index 7a7aa716e8a..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 diff --git a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql index 75cac365a1a..765a2519a38 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql @@ -4,11 +4,13 @@ * 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 diff --git a/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql b/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql index ff339af8b67..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 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/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.cpp b/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.cpp similarity index 87% rename from cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.cpp rename to cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.cpp index 60f8c7cc9b7..055aadcedb6 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.cpp +++ b/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.cpp @@ -21,7 +21,7 @@ void bad2(std::size_t length) noexcept { } } -// GOOD: the allocation failure is handled appropiately. +// GOOD: the allocation failure is handled appropriately. void good1(std::size_t length) noexcept { try { int* dest = new int[length]; @@ -32,7 +32,7 @@ void good1(std::size_t length) noexcept { } } -// GOOD: the allocation failure is handled appropiately. +// GOOD: the allocation failure is handled appropriately. void good2(std::size_t length) noexcept { int* dest = new int[length]; if(!dest) { diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.qhelp b/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.qhelp similarity index 100% rename from cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.qhelp rename to cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.qhelp diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql b/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql similarity index 83% rename from cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql rename to cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql index 2f67b02e856..b06df90c860 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql +++ b/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql @@ -1,6 +1,6 @@ /** * @name Incorrect allocation-error handling - * @description `operator new` throws an exception on allocation failures, while `operator new(std::nothrow)` returns a null pointer. Mixing up these two failure conditions can result in unexpected behavior. + * @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 @@ -153,12 +153,39 @@ predicate exprMayThrow(Expr e) { ) } +/** 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 - functionMayThrow(this) + // 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() ) } } 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/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-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-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 f68395f29bf..00000000000 --- a/cpp/ql/src/experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.ql +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @name Access Of Memory Location After The End Of A Buffer Using Strncat - * @description Calls of the form `strncat(dest, source, sizeof (dest) - strlen (dest))` set the third argument to one more than possible. So when `dest` is full, the expression `sizeof(dest) - strlen (dest)` will be equal to one, and not zero as the programmer might think. Making a call of this type may result in a zero byte being written just outside the `dest` buffer. - * @kind problem - * @id cpp/access-memory-location-after-end-buffer-strncat - * @problem.severity warning - * @precision medium - * @tags correctness - * security - * external/cwe/cwe-788 - */ - -import cpp -import semmle.code.cpp.models.implementations.Strcat -import semmle.code.cpp.valuenumbering.GlobalValueNumbering - -/** - * 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()) - ) -} - -from FunctionCall call, Expr sizeArg, Expr destArg, SubExpr sub, int n -where - interestringCallWithArgs(call, sizeArg, destArg) and - // 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() -select call, "Possible out-of-bounds write due to incorrect size argument." diff --git a/cpp/ql/src/semmle/code/cpp/Location.qll b/cpp/ql/src/semmle/code/cpp/Location.qll index 8d3653ea1c0..2a730ea5768 100644 --- a/cpp/ql/src/semmle/code/cpp/Location.qll +++ b/cpp/ql/src/semmle/code/cpp/Location.qll @@ -128,7 +128,9 @@ deprecated library class LocationExpr extends Location, @location_expr { } * Gets the length of the longest line in file `f`. */ pragma[nomagic] -private int maxCols(File f) { result = max(Location l | l.getFile() = f | l.getEndColumn()) } +private int maxCols(File f) { + result = max(Location l | l.getFile() = f | l.getStartColumn().maximum(l.getEndColumn())) +} /** * A C/C++ element that has a location in a file diff --git a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll index 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/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/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/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 058d66b1496..9b14db7ef88 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -532,7 +532,9 @@ private module Stage1 { } /** - * 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) { @@ -605,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 @@ -827,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`. @@ -893,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) ) } @@ -950,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 @@ -975,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 + ) } /** @@ -1006,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`. @@ -1071,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 @@ -1126,12 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1141,7 +1180,12 @@ private module Stage2 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1211,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 @@ -1459,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`. @@ -1532,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) ) } @@ -1589,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 @@ -1614,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 + ) } /** @@ -1645,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`. @@ -1710,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 @@ -1765,12 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1780,7 +1861,12 @@ private module Stage3 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1850,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 @@ -2120,8 +2215,6 @@ 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] @@ -2174,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`. @@ -2247,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) ) } @@ -2304,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 @@ -2329,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 + ) } /** @@ -2360,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`. @@ -2425,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 @@ -2480,12 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2495,7 +2616,12 @@ private module Stage4 { private predicate revFlowInToReturn( 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 + ) } /** @@ -2565,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 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 058d66b1496..9b14db7ef88 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -532,7 +532,9 @@ private module Stage1 { } /** - * 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) { @@ -605,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 @@ -827,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`. @@ -893,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) ) } @@ -950,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 @@ -975,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 + ) } /** @@ -1006,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`. @@ -1071,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 @@ -1126,12 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1141,7 +1180,12 @@ private module Stage2 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1211,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 @@ -1459,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`. @@ -1532,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) ) } @@ -1589,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 @@ -1614,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 + ) } /** @@ -1645,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`. @@ -1710,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 @@ -1765,12 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1780,7 +1861,12 @@ private module Stage3 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1850,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 @@ -2120,8 +2215,6 @@ 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] @@ -2174,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`. @@ -2247,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) ) } @@ -2304,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 @@ -2329,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 + ) } /** @@ -2360,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`. @@ -2425,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 @@ -2480,12 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2495,7 +2616,12 @@ private module Stage4 { private predicate revFlowInToReturn( 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 + ) } /** @@ -2565,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 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 058d66b1496..9b14db7ef88 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -532,7 +532,9 @@ private module Stage1 { } /** - * 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) { @@ -605,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 @@ -827,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`. @@ -893,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) ) } @@ -950,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 @@ -975,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 + ) } /** @@ -1006,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`. @@ -1071,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 @@ -1126,12 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1141,7 +1180,12 @@ private module Stage2 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1211,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 @@ -1459,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`. @@ -1532,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) ) } @@ -1589,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 @@ -1614,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 + ) } /** @@ -1645,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`. @@ -1710,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 @@ -1765,12 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1780,7 +1861,12 @@ private module Stage3 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1850,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 @@ -2120,8 +2215,6 @@ 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] @@ -2174,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`. @@ -2247,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) ) } @@ -2304,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 @@ -2329,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 + ) } /** @@ -2360,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`. @@ -2425,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 @@ -2480,12 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2495,7 +2616,12 @@ private module Stage4 { private predicate revFlowInToReturn( 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 + ) } /** @@ -2565,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 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 058d66b1496..9b14db7ef88 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -532,7 +532,9 @@ private module Stage1 { } /** - * 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) { @@ -605,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 @@ -827,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`. @@ -893,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) ) } @@ -950,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 @@ -975,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 + ) } /** @@ -1006,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`. @@ -1071,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 @@ -1126,12 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1141,7 +1180,12 @@ private module Stage2 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1211,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 @@ -1459,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`. @@ -1532,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) ) } @@ -1589,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 @@ -1614,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 + ) } /** @@ -1645,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`. @@ -1710,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 @@ -1765,12 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1780,7 +1861,12 @@ private module Stage3 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1850,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 @@ -2120,8 +2215,6 @@ 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] @@ -2174,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`. @@ -2247,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) ) } @@ -2304,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 @@ -2329,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 + ) } /** @@ -2360,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`. @@ -2425,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 @@ -2480,12 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2495,7 +2616,12 @@ private module Stage4 { private predicate revFlowInToReturn( 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 + ) } /** @@ -2565,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 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 058d66b1496..9b14db7ef88 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -532,7 +532,9 @@ private module Stage1 { } /** - * 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) { @@ -605,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 @@ -827,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`. @@ -893,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) ) } @@ -950,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 @@ -975,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 + ) } /** @@ -1006,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`. @@ -1071,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 @@ -1126,12 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1141,7 +1180,12 @@ private module Stage2 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1211,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 @@ -1459,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`. @@ -1532,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) ) } @@ -1589,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 @@ -1614,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 + ) } /** @@ -1645,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`. @@ -1710,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 @@ -1765,12 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1780,7 +1861,12 @@ private module Stage3 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1850,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 @@ -2120,8 +2215,6 @@ 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] @@ -2174,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`. @@ -2247,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) ) } @@ -2304,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 @@ -2329,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 + ) } /** @@ -2360,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`. @@ -2425,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 @@ -2480,12 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2495,7 +2616,12 @@ private module Stage4 { private predicate revFlowInToReturn( 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 + ) } /** @@ -2565,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 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 058d66b1496..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 @@ -532,7 +532,9 @@ private module Stage1 { } /** - * 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) { @@ -605,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 @@ -827,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`. @@ -893,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) ) } @@ -950,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 @@ -975,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 + ) } /** @@ -1006,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`. @@ -1071,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 @@ -1126,12 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1141,7 +1180,12 @@ private module Stage2 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1211,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 @@ -1459,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`. @@ -1532,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) ) } @@ -1589,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 @@ -1614,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 + ) } /** @@ -1645,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`. @@ -1710,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 @@ -1765,12 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1780,7 +1861,12 @@ private module Stage3 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1850,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 @@ -2120,8 +2215,6 @@ 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] @@ -2174,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`. @@ -2247,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) ) } @@ -2304,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 @@ -2329,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 + ) } /** @@ -2360,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`. @@ -2425,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 @@ -2480,12 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2495,7 +2616,12 @@ private module Stage4 { private predicate revFlowInToReturn( 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 + ) } /** @@ -2565,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 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 058d66b1496..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 @@ -532,7 +532,9 @@ private module Stage1 { } /** - * 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) { @@ -605,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 @@ -827,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`. @@ -893,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) ) } @@ -950,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 @@ -975,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 + ) } /** @@ -1006,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`. @@ -1071,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 @@ -1126,12 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1141,7 +1180,12 @@ private module Stage2 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1211,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 @@ -1459,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`. @@ -1532,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) ) } @@ -1589,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 @@ -1614,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 + ) } /** @@ -1645,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`. @@ -1710,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 @@ -1765,12 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1780,7 +1861,12 @@ private module Stage3 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1850,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 @@ -2120,8 +2215,6 @@ 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] @@ -2174,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`. @@ -2247,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) ) } @@ -2304,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 @@ -2329,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 + ) } /** @@ -2360,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`. @@ -2425,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 @@ -2480,12 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2495,7 +2616,12 @@ private module Stage4 { private predicate revFlowInToReturn( 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 + ) } /** @@ -2565,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 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 058d66b1496..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 @@ -532,7 +532,9 @@ private module Stage1 { } /** - * 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) { @@ -605,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 @@ -827,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`. @@ -893,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) ) } @@ -950,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 @@ -975,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 + ) } /** @@ -1006,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`. @@ -1071,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 @@ -1126,12 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1141,7 +1180,12 @@ private module Stage2 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1211,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 @@ -1459,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`. @@ -1532,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) ) } @@ -1589,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 @@ -1614,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 + ) } /** @@ -1645,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`. @@ -1710,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 @@ -1765,12 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1780,7 +1861,12 @@ private module Stage3 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1850,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 @@ -2120,8 +2215,6 @@ 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] @@ -2174,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`. @@ -2247,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) ) } @@ -2304,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 @@ -2329,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 + ) } /** @@ -2360,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`. @@ -2425,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 @@ -2480,12 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2495,7 +2616,12 @@ private module Stage4 { private predicate revFlowInToReturn( 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 + ) } /** @@ -2565,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 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 058d66b1496..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 @@ -532,7 +532,9 @@ private module Stage1 { } /** - * 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) { @@ -605,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 @@ -827,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`. @@ -893,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) ) } @@ -950,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 @@ -975,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 + ) } /** @@ -1006,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`. @@ -1071,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 @@ -1126,12 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1141,7 +1180,12 @@ private module Stage2 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1211,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 @@ -1459,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`. @@ -1532,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) ) } @@ -1589,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 @@ -1614,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 + ) } /** @@ -1645,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`. @@ -1710,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 @@ -1765,12 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1780,7 +1861,12 @@ private module Stage3 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1850,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 @@ -2120,8 +2215,6 @@ 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] @@ -2174,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`. @@ -2247,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) ) } @@ -2304,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 @@ -2329,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 + ) } /** @@ -2360,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`. @@ -2425,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 @@ -2480,12 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2495,7 +2616,12 @@ private module Stage4 { private predicate revFlowInToReturn( 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 + ) } /** @@ -2565,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 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 6f0d6ff92ff..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 @@ -665,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) ) } 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 e335080ad6b..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 @@ -1994,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 e26d61b9792..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 @@ -90,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 @@ -284,14 +287,24 @@ private predicate isArgumentForParameter( 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 + ) ) } @@ -325,6 +338,9 @@ predicate allocationEscapes(Configuration::Allocation allocation) { exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) ) + or + Configuration::phaseNeedsSoundEscapeAnalysis() and + resultEscapesNonReturn(allocation.getABaseInstruction()) } /** @@ -395,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 866afb64b31..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 @@ -138,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 1ba19b4a4c3..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,6 +445,9 @@ private module Cached { result = vvar.getType() ) or + instr = reusedPhiInstruction(_) and + result = instr.(OldInstruction).getResultLanguageType() + or instr = unreachedInstruction(_) and result = Language::getVoidType() } @@ -862,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. @@ -940,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 e335080ad6b..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 @@ -1994,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/SideEffects.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll index 350127a58d1..50245fafde2 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll @@ -7,8 +7,67 @@ private import cpp private import semmle.code.cpp.ir.implementation.Opcode +private import semmle.code.cpp.models.interfaces.PointerWrapper private import semmle.code.cpp.models.interfaces.SideEffect +private predicate isDeeplyConst(Type t) { + t.isConst() and + isDeeplyConstBelow(t) + or + isDeeplyConst(t.(Decltype).getBaseType()) + or + isDeeplyConst(t.(ReferenceType).getBaseType()) + or + exists(SpecifiedType specType | specType = t | + specType.getASpecifier().getName() = "const" and + isDeeplyConstBelow(specType.getBaseType()) + ) + or + isDeeplyConst(t.(ArrayType).getBaseType()) +} + +private predicate isDeeplyConstBelow(Type t) { + t instanceof BuiltInType + or + not t instanceof PointerWrapper and + t instanceof Class + or + t instanceof Enum + or + isDeeplyConstBelow(t.(Decltype).getBaseType()) + or + isDeeplyConst(t.(PointerType).getBaseType()) + or + isDeeplyConst(t.(ReferenceType).getBaseType()) + or + isDeeplyConstBelow(t.(SpecifiedType).getBaseType()) + or + isDeeplyConst(t.(ArrayType).getBaseType()) + or + isDeeplyConst(t.(GNUVectorType).getBaseType()) + or + isDeeplyConst(t.(FunctionPointerIshType).getBaseType()) + or + isDeeplyConst(t.(PointerWrapper).getTemplateArgument(0)) + or + isDeeplyConst(t.(PointerToMemberType).getBaseType()) + or + isDeeplyConstBelow(t.(TypedefType).getBaseType()) +} + +private predicate isConstPointerLike(Type t) { + ( + t instanceof PointerWrapper + or + t instanceof PointerType + or + t instanceof ArrayType + or + t instanceof ReferenceType + ) and + isDeeplyConstBelow(t) +} + /** * Holds if the specified call has a side effect that does not come from a `SideEffectFunction` * model. @@ -39,11 +98,12 @@ private predicate hasDefaultSideEffect(Call call, ParameterIndex i, boolean buff exists(Type t | t = expr.getUnspecifiedType() | t instanceof ArrayType or t instanceof PointerType or - t instanceof ReferenceType + t instanceof ReferenceType or + t instanceof PointerWrapper ) and ( isWrite = true and - not call.getTarget().getParameter(i).getType().isDeeplyConstBelow() + not isConstPointerLike(call.getTarget().getParameter(i).getUnderlyingType()) or isWrite = false ) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/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 e335080ad6b..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 @@ -1994,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 e26d61b9792..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 @@ -90,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 @@ -284,14 +287,24 @@ private predicate isArgumentForParameter( 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 + ) ) } @@ -325,6 +338,9 @@ predicate allocationEscapes(Configuration::Allocation allocation) { exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) ) + or + Configuration::phaseNeedsSoundEscapeAnalysis() and + resultEscapesNonReturn(allocation.getABaseInstruction()) } /** @@ -395,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 1ba19b4a4c3..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,6 +445,9 @@ private module Cached { result = vvar.getType() ) or + instr = reusedPhiInstruction(_) and + result = instr.(OldInstruction).getResultLanguageType() + or instr = unreachedInstruction(_) and result = Language::getVoidType() } @@ -862,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. @@ -940,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 7f9639f6373..e249a164061 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll @@ -146,8 +146,7 @@ private class SmartPtrSetterFunction extends MemberFunction, AliasFunction, Side } private FunctionInput getPointerInput() { - exists(Parameter param0 | - param0 = this.getParameter(0) and + exists(Parameter param0 | param0 = this.getParameter(0) | ( param0.getUnspecifiedType().(ReferenceType).getBaseType() instanceof SmartPtr and if this.getParameter(1).getUnspecifiedType() instanceof PointerType @@ -158,11 +157,11 @@ private class SmartPtrSetterFunction extends MemberFunction, AliasFunction, Side // 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) ) - 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/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/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-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/IncorrectAllocationErrorHandling.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.qlref deleted file mode 100644 index 4bba5039aea..00000000000 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.qlref +++ /dev/null @@ -1 +0,0 @@ -experimental/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql 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 cd160a70638..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:54:3:54:24 | ... = ... | potential unsafe or redundant assignment. | -| test.c:55:3:55:40 | ... = ... | potential unsafe or redundant assignment. | -| test.c:56:3:56:44 | ... = ... | potential unsafe or redundant assignment. | -| test.c:57:3:57:44 | ... = ... | potential unsafe or redundant assignment. | -| test.c:58:3:58:48 | ... = ... | potential unsafe or redundant assignment. | -| test.c:59:3:59:48 | ... = ... | potential unsafe or redundant assignment. | -| test.c:60:3:60:52 | ... = ... | potential unsafe or redundant assignment. | -| test.c:61:3:61:50 | ... = ... | potential unsafe or redundant assignment. | -| test.c:62:3:62:54 | ... = ... | 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 3e9791e1024..00000000000 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.expected +++ /dev/null @@ -1,5 +0,0 @@ -| test.c:8:3:8:9 | call to strncat | Possible out-of-bounds write due to incorrect size argument. | -| test.c:9:3:9:9 | call to strncat | Possible out-of-bounds write due to incorrect size argument. | -| test.c:17:3:17:9 | call to strncat | Possible out-of-bounds write due to incorrect size argument. | -| test.c:18:3:18:9 | call to strncat | Possible out-of-bounds write due to incorrect size argument. | -| test.c:46:3:46:9 | call to strncat | Possible out-of-bounds write due to incorrect size argument. | 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/test.c b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-788/semmle/tests/test.c index 6d310875630..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 @@ -2,50 +2,12 @@ char * strncat(char*, const char*, unsigned); unsigned strlen(const char*); void* malloc(unsigned); -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 - strncat(buf, "fix", sizeof(buf)-strlen(buf)); // BAD -} - -#define MAX_SIZE 80 - -void strncat_test2(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, "fix", MAX_SIZE - strlen(buf)); // BAD -} - -void strncat_test3(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] - strncat(buf, "fix", len - strlen(buf)); // BAD [NOT DETECTED] -} - -void strncat_test4(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 -} - struct buffers { unsigned char array[50]; unsigned char *pointer; } globalBuff1,*globalBuff2,globalBuff1_c,*globalBuff2_c; -void strncat_test5(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 -} - void strlen_test1(){ unsigned char buff1[12]; struct buffers buffAll; 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/fields/ir-path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected index e6234ca17f7..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 | @@ -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_] | 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 e008e2de659..3746b0e2c6c 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -7903,6 +7903,180 @@ perf-regression.cpp: # 9| v9_6(void) = AliasedUse : ~m? # 9| v9_7(void) = ExitFunction : +smart_ptr.cpp: +# 10| void call_unique_ptr_arg(int*) +# 10| Block 0 +# 10| v10_1(void) = EnterFunction : +# 10| mu10_2(unknown) = AliasedDefinition : +# 10| mu10_3(unknown) = InitializeNonLocal : +# 10| r10_4(glval) = VariableAddress[p] : +# 10| mu10_5(int *) = InitializeParameter[p] : &:r10_4 +# 10| r10_6(int *) = Load[p] : &:r10_4, ~m? +# 10| mu10_7(unknown) = InitializeIndirection[p] : &:r10_6 +# 11| r11_1(glval>>) = VariableAddress[up] : +# 11| mu11_2(unique_ptr>) = Uninitialized[up] : &:r11_1 +# 11| r11_3(glval) = FunctionAddress[unique_ptr] : +# 11| r11_4(glval) = VariableAddress[p] : +# 11| r11_5(int *) = Load[p] : &:r11_4, ~m? +# 11| v11_6(void) = Call[unique_ptr] : func:r11_3, this:r11_1, 0:r11_5 +# 11| mu11_7(unknown) = ^CallSideEffect : ~m? +# 11| mu11_8(unique_ptr>) = ^IndirectMustWriteSideEffect[-1] : &:r11_1 +# 12| r12_1(glval) = FunctionAddress[unique_ptr_arg] : +# 12| r12_2(glval>>) = VariableAddress[#temp12:20] : +# 12| r12_3(glval) = FunctionAddress[move] : +# 12| r12_4(glval>>) = VariableAddress[up] : +# 12| r12_5(unique_ptr> &) = CopyValue : r12_4 +# 12| r12_6(unique_ptr> &&) = Call[move] : func:r12_3, 0:r12_5 +# 12| r12_7(unique_ptr>) = Load[?] : &:r12_6, ~m? +# 12| mu12_8(unique_ptr>) = Store[#temp12:20] : &:r12_2, r12_7 +# 12| r12_9(unique_ptr>) = Load[#temp12:20] : &:r12_2, ~m? +# 12| v12_10(void) = Call[unique_ptr_arg] : func:r12_1, 0:r12_9 +# 12| mu12_11(unknown) = ^CallSideEffect : ~m? +# 12| v12_12(void) = ^BufferReadSideEffect[0] : &:r12_9, ~m? +# 12| mu12_13(unknown) = ^BufferMayWriteSideEffect[0] : &:r12_9 +# 13| v13_1(void) = NoOp : +# 10| v10_8(void) = ReturnIndirection[p] : &:r10_6, ~m? +# 10| v10_9(void) = ReturnVoid : +# 10| v10_10(void) = AliasedUse : ~m? +# 10| v10_11(void) = ExitFunction : + +# 17| void call_shared_ptr_arg(float*) +# 17| Block 0 +# 17| v17_1(void) = EnterFunction : +# 17| mu17_2(unknown) = AliasedDefinition : +# 17| mu17_3(unknown) = InitializeNonLocal : +# 17| r17_4(glval) = VariableAddress[p] : +# 17| mu17_5(float *) = InitializeParameter[p] : &:r17_4 +# 17| r17_6(float *) = Load[p] : &:r17_4, ~m? +# 17| mu17_7(unknown) = InitializeIndirection[p] : &:r17_6 +# 18| r18_1(glval>) = VariableAddress[sp] : +# 18| mu18_2(shared_ptr) = Uninitialized[sp] : &:r18_1 +# 18| r18_3(glval) = FunctionAddress[shared_ptr] : +# 18| r18_4(glval) = VariableAddress[p] : +# 18| r18_5(float *) = Load[p] : &:r18_4, ~m? +# 18| v18_6(void) = Call[shared_ptr] : func:r18_3, this:r18_1, 0:r18_5 +# 18| mu18_7(unknown) = ^CallSideEffect : ~m? +# 18| mu18_8(shared_ptr) = ^IndirectMustWriteSideEffect[-1] : &:r18_1 +# 19| r19_1(glval) = FunctionAddress[shared_ptr_arg] : +# 19| r19_2(glval>) = VariableAddress[#temp19:20] : +# 19| mu19_3(shared_ptr) = Uninitialized[#temp19:20] : &:r19_2 +# 19| r19_4(glval) = FunctionAddress[shared_ptr] : +# 19| r19_5(glval>) = VariableAddress[sp] : +# 19| r19_6(glval>) = Convert : r19_5 +# 19| r19_7(shared_ptr &) = CopyValue : r19_6 +# 19| v19_8(void) = Call[shared_ptr] : func:r19_4, this:r19_2, 0:r19_7 +# 19| mu19_9(unknown) = ^CallSideEffect : ~m? +# 19| mu19_10(shared_ptr) = ^IndirectMustWriteSideEffect[-1] : &:r19_2 +# 19| v19_11(void) = ^IndirectReadSideEffect[0] : &:r19_7, ~m? +# 19| r19_12(shared_ptr) = Load[#temp19:20] : &:r19_2, ~m? +# 19| v19_13(void) = Call[shared_ptr_arg] : func:r19_1, 0:r19_12 +# 19| mu19_14(unknown) = ^CallSideEffect : ~m? +# 19| v19_15(void) = ^BufferReadSideEffect[0] : &:r19_12, ~m? +# 19| mu19_16(unknown) = ^BufferMayWriteSideEffect[0] : &:r19_12 +# 20| v20_1(void) = NoOp : +# 17| v17_8(void) = ReturnIndirection[p] : &:r17_6, ~m? +# 17| v17_9(void) = ReturnVoid : +# 17| v17_10(void) = AliasedUse : ~m? +# 17| v17_11(void) = ExitFunction : + +# 28| void call_shared_ptr_consts() +# 28| Block 0 +# 28| v28_1(void) = EnterFunction : +# 28| mu28_2(unknown) = AliasedDefinition : +# 28| mu28_3(unknown) = InitializeNonLocal : +# 29| r29_1(glval>) = VariableAddress[sp_const_int] : +# 29| mu29_2(shared_ptr) = Uninitialized[sp_const_int] : &:r29_1 +# 31| r31_1(glval) = FunctionAddress[shared_ptr_const_int] : +# 31| r31_2(glval>) = VariableAddress[#temp31:26] : +# 31| mu31_3(shared_ptr) = Uninitialized[#temp31:26] : &:r31_2 +# 31| r31_4(glval) = FunctionAddress[shared_ptr] : +# 31| r31_5(glval>) = VariableAddress[sp_const_int] : +# 31| r31_6(glval>) = Convert : r31_5 +# 31| r31_7(shared_ptr &) = CopyValue : r31_6 +# 31| v31_8(void) = Call[shared_ptr] : func:r31_4, this:r31_2, 0:r31_7 +# 31| mu31_9(unknown) = ^CallSideEffect : ~m? +# 31| mu31_10(shared_ptr) = ^IndirectMustWriteSideEffect[-1] : &:r31_2 +# 31| v31_11(void) = ^IndirectReadSideEffect[0] : &:r31_7, ~m? +# 31| r31_12(shared_ptr) = Load[#temp31:26] : &:r31_2, ~m? +# 31| v31_13(void) = Call[shared_ptr_const_int] : func:r31_1, 0:r31_12 +# 31| mu31_14(unknown) = ^CallSideEffect : ~m? +# 31| v31_15(void) = ^BufferReadSideEffect[0] : &:r31_12, ~m? +# 33| r33_1(glval>) = VariableAddress[sp_const_int_pointer] : +# 33| mu33_2(shared_ptr) = Uninitialized[sp_const_int_pointer] : &:r33_1 +# 35| r35_1(glval) = FunctionAddress[shared_ptr_const_int_ptr] : +# 35| r35_2(glval>) = VariableAddress[#temp35:30] : +# 35| mu35_3(shared_ptr) = Uninitialized[#temp35:30] : &:r35_2 +# 35| r35_4(glval) = FunctionAddress[shared_ptr] : +# 35| r35_5(glval>) = VariableAddress[sp_const_int_pointer] : +# 35| r35_6(glval>) = Convert : r35_5 +# 35| r35_7(shared_ptr &) = CopyValue : r35_6 +# 35| v35_8(void) = Call[shared_ptr] : func:r35_4, this:r35_2, 0:r35_7 +# 35| mu35_9(unknown) = ^CallSideEffect : ~m? +# 35| mu35_10(shared_ptr) = ^IndirectMustWriteSideEffect[-1] : &:r35_2 +# 35| v35_11(void) = ^IndirectReadSideEffect[0] : &:r35_7, ~m? +# 35| r35_12(shared_ptr) = Load[#temp35:30] : &:r35_2, ~m? +# 35| v35_13(void) = Call[shared_ptr_const_int_ptr] : func:r35_1, 0:r35_12 +# 35| mu35_14(unknown) = ^CallSideEffect : ~m? +# 35| v35_15(void) = ^BufferReadSideEffect[0] : &:r35_12, ~m? +# 35| mu35_16(unknown) = ^BufferMayWriteSideEffect[0] : &:r35_12 +# 37| r37_1(glval>>) = VariableAddress[sp_sp_const_int] : +# 37| mu37_2(shared_ptr>) = Uninitialized[sp_sp_const_int] : &:r37_1 +# 39| r39_1(glval) = FunctionAddress[shared_ptr_shared_ptr_const_int] : +# 39| r39_2(glval>>) = VariableAddress[#temp39:37] : +# 39| mu39_3(shared_ptr>) = Uninitialized[#temp39:37] : &:r39_2 +# 39| r39_4(glval) = FunctionAddress[shared_ptr] : +# 39| r39_5(glval>>) = VariableAddress[sp_sp_const_int] : +# 39| r39_6(glval>>) = Convert : r39_5 +# 39| r39_7(shared_ptr> &) = CopyValue : r39_6 +# 39| v39_8(void) = Call[shared_ptr] : func:r39_4, this:r39_2, 0:r39_7 +# 39| mu39_9(unknown) = ^CallSideEffect : ~m? +# 39| mu39_10(shared_ptr>) = ^IndirectMustWriteSideEffect[-1] : &:r39_2 +# 39| v39_11(void) = ^IndirectReadSideEffect[0] : &:r39_7, ~m? +# 39| r39_12(shared_ptr>) = Load[#temp39:37] : &:r39_2, ~m? +# 39| v39_13(void) = Call[shared_ptr_shared_ptr_const_int] : func:r39_1, 0:r39_12 +# 39| mu39_14(unknown) = ^CallSideEffect : ~m? +# 39| v39_15(void) = ^BufferReadSideEffect[0] : &:r39_12, ~m? +# 39| mu39_16(unknown) = ^BufferMayWriteSideEffect[0] : &:r39_12 +# 41| r41_1(glval>>) = VariableAddress[sp_const_sp_int] : +# 41| mu41_2(shared_ptr>) = Uninitialized[sp_const_sp_int] : &:r41_1 +# 43| r43_1(glval) = FunctionAddress[shared_ptr_const_shared_ptr_int] : +# 43| r43_2(glval>>) = VariableAddress[#temp43:37] : +# 43| mu43_3(shared_ptr>) = Uninitialized[#temp43:37] : &:r43_2 +# 43| r43_4(glval) = FunctionAddress[shared_ptr] : +# 43| r43_5(glval>>) = VariableAddress[sp_const_sp_int] : +# 43| r43_6(glval>>) = Convert : r43_5 +# 43| r43_7(shared_ptr> &) = CopyValue : r43_6 +# 43| v43_8(void) = Call[shared_ptr] : func:r43_4, this:r43_2, 0:r43_7 +# 43| mu43_9(unknown) = ^CallSideEffect : ~m? +# 43| mu43_10(shared_ptr>) = ^IndirectMustWriteSideEffect[-1] : &:r43_2 +# 43| v43_11(void) = ^IndirectReadSideEffect[0] : &:r43_7, ~m? +# 43| r43_12(shared_ptr>) = Load[#temp43:37] : &:r43_2, ~m? +# 43| v43_13(void) = Call[shared_ptr_const_shared_ptr_int] : func:r43_1, 0:r43_12 +# 43| mu43_14(unknown) = ^CallSideEffect : ~m? +# 43| v43_15(void) = ^BufferReadSideEffect[0] : &:r43_12, ~m? +# 43| mu43_16(unknown) = ^BufferMayWriteSideEffect[0] : &:r43_12 +# 45| r45_1(glval>>) = VariableAddress[sp_const_sp_const_int] : +# 45| mu45_2(shared_ptr>) = Uninitialized[sp_const_sp_const_int] : &:r45_1 +# 47| r47_1(glval) = FunctionAddress[shared_ptr_const_shared_ptr_const_int] : +# 47| r47_2(glval>>) = VariableAddress[#temp47:43] : +# 47| mu47_3(shared_ptr>) = Uninitialized[#temp47:43] : &:r47_2 +# 47| r47_4(glval) = FunctionAddress[shared_ptr] : +# 47| r47_5(glval>>) = VariableAddress[sp_const_sp_const_int] : +# 47| r47_6(glval>>) = Convert : r47_5 +# 47| r47_7(shared_ptr> &) = CopyValue : r47_6 +# 47| v47_8(void) = Call[shared_ptr] : func:r47_4, this:r47_2, 0:r47_7 +# 47| mu47_9(unknown) = ^CallSideEffect : ~m? +# 47| mu47_10(shared_ptr>) = ^IndirectMustWriteSideEffect[-1] : &:r47_2 +# 47| v47_11(void) = ^IndirectReadSideEffect[0] : &:r47_7, ~m? +# 47| r47_12(shared_ptr>) = Load[#temp47:43] : &:r47_2, ~m? +# 47| v47_13(void) = Call[shared_ptr_const_shared_ptr_const_int] : func:r47_1, 0:r47_12 +# 47| mu47_14(unknown) = ^CallSideEffect : ~m? +# 47| v47_15(void) = ^BufferReadSideEffect[0] : &:r47_12, ~m? +# 48| v48_1(void) = NoOp : +# 28| v28_4(void) = ReturnVoid : +# 28| v28_5(void) = AliasedUse : ~m? +# 28| v28_6(void) = ExitFunction : + struct_init.cpp: # 16| void let_info_escape(Info*) # 16| Block 0 diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.ql b/cpp/ql/test/library-tests/ir/ir/raw_ir.ql new file mode 100644 index 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/ssa/aliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected index 76de50dd792..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) @@ -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 ab79d20214a..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) @@ -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 0b257db98a4..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 @@ -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 0b257db98a4..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 @@ -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/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/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-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/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/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-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/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.expected similarity index 100% rename from cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/IncorrectAllocationErrorHandling.expected rename to cpp/ql/test/query-tests/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.expected 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/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-570/test.cpp similarity index 100% rename from cpp/ql/test/experimental/query-tests/Security/CWE/CWE-570/semmle/tests/test.cpp rename to cpp/ql/test/query-tests/Security/CWE/CWE-570/test.cpp 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/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-effective-visibility.md b/csharp/change-notes/2021-06-15-effective-visibility.md new file mode 100644 index 00000000000..f14e338079a --- /dev/null +++ b/csharp/change-notes/2021-06-15-effective-visibility.md @@ -0,0 +1,2 @@ +lgtm,codescanning +* The `isEffectivelyPrivate`, `isEffectivelyInternal` and `isEffectivelyPublic` predicates on `Modifiable` have been reworked to handle `private protected` and `internal protected` visibilities and explicitly implemented interfaces. \ 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.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/Constructor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs index 3f31108b57e..e3f77d9407e 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs @@ -146,6 +146,15 @@ namespace Semmle.Extraction.CSharp.Entities 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/Method.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs index 12d0ce4cc10..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) @@ -129,6 +109,30 @@ namespace Semmle.Extraction.CSharp.Entities /// 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 => { ta.Symbol.BuildOrWriteId(m.Context, trapFile, 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: @@ -192,9 +188,7 @@ namespace Semmle.Extraction.CSharp.Entities 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) { @@ -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 806e73e3590..3d6cb01837e 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs @@ -72,15 +72,11 @@ namespace Semmle.Extraction.CSharp.Entities public static void ExtractModifiers(Context cx, TextWriter trapFile, IEntity key, ISymbol symbol) { - var interfaceDefinition = symbol.ContainingType is not null - && symbol.ContainingType.Kind == SymbolKind.NamedType - && symbol.ContainingType.TypeKind == TypeKind.Interface; - HasAccessibility(cx, trapFile, key, symbol.DeclaredAccessibility); if (symbol.Kind == SymbolKind.ErrorType) trapFile.has_modifiers(key, Modifier.Create(cx, Accessibility.Public)); - if (symbol.IsAbstract && (symbol.Kind != SymbolKind.NamedType || ((INamedTypeSymbol)symbol).TypeKind != TypeKind.Interface) && !interfaceDefinition) + if (symbol.IsAbstract && (symbol.Kind != SymbolKind.NamedType || ((INamedTypeSymbol)symbol).TypeKind != TypeKind.Interface)) HasModifier(cx, trapFile, key, "abstract"); if (symbol.IsSealed) @@ -94,10 +90,6 @@ namespace Semmle.Extraction.CSharp.Entities if (symbol.IsVirtual) HasModifier(cx, trapFile, key, "virtual"); - // For some reason, method in interfaces are "virtual", not "abstract" - if (symbol.IsAbstract && interfaceDefinition) - HasModifier(cx, trapFile, key, "virtual"); - if (symbol.Kind == SymbolKind.Field && ((IFieldSymbol)symbol).IsReadOnly) HasModifier(cx, trapFile, key, "readonly"); 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 9d69372174e..95a93d69c12 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs @@ -27,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 { @@ -270,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/Extractor/Extractor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs index 179d1f14de5..1dd6d817b01 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs @@ -416,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 0bda977a6d5..c3bc02bf480 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs @@ -373,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); @@ -480,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()) diff --git a/csharp/extractor/Semmle.Extraction/Context.cs b/csharp/extractor/Semmle.Extraction/Context.cs index b08fb98fc55..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()); + } } } } diff --git a/csharp/extractor/Semmle.Extraction/TrapExtensions.cs b/csharp/extractor/Semmle.Extraction/TrapExtensions.cs index e7d3fcec6b5..e1343019335 100644 --- a/csharp/extractor/Semmle.Extraction/TrapExtensions.cs +++ b/csharp/extractor/Semmle.Extraction/TrapExtensions.cs @@ -117,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 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/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/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/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.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/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 e335080ad6b..453838215ff 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll @@ -1994,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/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 e335080ad6b..453838215ff 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll @@ -1994,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 e26d61b9792..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 @@ -90,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 @@ -284,14 +287,24 @@ private predicate isArgumentForParameter( 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 + ) ) } @@ -325,6 +338,9 @@ predicate allocationEscapes(Configuration::Allocation allocation) { exists(IREscapeAnalysisConfiguration config | config.useSoundEscapeAnalysis() and resultEscapesNonReturn(allocation.getABaseInstruction()) ) + or + Configuration::phaseNeedsSoundEscapeAnalysis() and + resultEscapesNonReturn(allocation.getABaseInstruction()) } /** @@ -395,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/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 1ba19b4a4c3..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,6 +445,9 @@ private module Cached { result = vvar.getType() ) or + instr = reusedPhiInstruction(_) and + result = instr.(OldInstruction).getResultLanguageType() + or instr = unreachedInstruction(_) and result = Language::getVoidType() } @@ -862,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. @@ -940,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/csharp/Member.qll b/csharp/ql/src/semmle/code/csharp/Member.qll index 520df43cfb8..191c7c85516 100644 --- a/csharp/ql/src/semmle/code/csharp/Member.qll +++ b/csharp/ql/src/semmle/code/csharp/Member.qll @@ -86,32 +86,69 @@ 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") } + private predicate isReallyPrivate() { + this.isPrivate() and + not this.isProtected() and + // Rare case when a member is defined with the same name in multiple assemblies with different visibility + not this.isPublic() + } + /** - * Holds if this declaration is effectively `private` (either directly or - * because one of the enclosing types is `private`). + * Holds if this declaration is effectively `private`. A declaration is considered + * effectively `private` if it can only be referenced from + * - the declaring and its nested types, similarly to `private` declarations, and + * - the enclosing types. + * + * Note that explicit interface implementations are also considered effectively + * `private` if the implemented interface is itself effectively `private`. Finally, + * `private protected` members are not considered effectively `private`, because + * they can be overriden within the declaring assembly. */ predicate isEffectivelyPrivate() { - this.isPrivate() or - this.getDeclaringType+().isPrivate() + this.isReallyPrivate() or + this.getDeclaringType+().(Modifiable).isReallyPrivate() or + this.(Virtualizable).getExplicitlyImplementedInterface().isEffectivelyPrivate() + } + + private predicate isReallyInternal() { + ( + this.isInternal() and not this.isProtected() + or + this.isPrivate() and this.isProtected() + ) and + // Rare case when a member is defined with the same name in multiple assemblies with different visibility + not this.isPublic() } /** - * Holds if this declaration is effectively `internal` (either directly or - * because one of the enclosing types is `internal`). + * Holds if this declaration is effectively `internal`. A declaration is considered + * effectively `internal` if it can only be referenced from the declaring assembly. + * + * Note that friend assemblies declared in `InternalsVisibleToAttribute` are not + * considered. Explicit interface implementations are also considered effectively + * `internal` if the implemented interface is itself effectively `internal`. Finally, + * `internal protected` members are not considered effectively `internal`, because + * they can be overriden outside the declaring assembly. */ predicate isEffectivelyInternal() { - this.isInternal() or - this.getDeclaringType+().isInternal() + this.isReallyInternal() or + this.getDeclaringType+().(Modifiable).isReallyInternal() or + this.(Virtualizable).getExplicitlyImplementedInterface().isEffectivelyInternal() } /** - * Holds if this declaration is effectively `public`, because it - * and all enclosing types are `public`. + * Holds if this declaration is effectively `public`, meaning that it can be + * referenced outside the declaring assembly. */ predicate isEffectivelyPublic() { not isEffectivelyPrivate() and not isEffectivelyInternal() } } @@ -154,6 +191,11 @@ class Virtualizable extends Member, @virtualizable { implementsExplicitInterface() } + override predicate isPrivate() { + super.isPrivate() and + not implementsExplicitInterface() + } + /** * Gets any interface this member explicitly implements; this only applies * to members that can be declared on an interface, i.e. methods, properties, 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 8a721da5293..5a402717401 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll @@ -1466,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)) ) } @@ -1651,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 + ) ) } @@ -1887,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 + ) ) } 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 6a5944db3a2..63f6943a29c 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll @@ -77,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)) } @@ -88,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 { 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 8826ee7c1be..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 { @@ -382,8 +389,7 @@ 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.getInitializer()) @@ -620,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) } } @@ -858,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() @@ -1037,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 | @@ -1298,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 @@ -1489,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/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 32f22b97914..a7c6869a4cc 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/LibraryTypeDataFlow.qll @@ -1848,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/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 058d66b1496..9b14db7ef88 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -532,7 +532,9 @@ private module Stage1 { } /** - * 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) { @@ -605,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 @@ -827,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`. @@ -893,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) ) } @@ -950,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 @@ -975,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 + ) } /** @@ -1006,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`. @@ -1071,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 @@ -1126,12 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1141,7 +1180,12 @@ private module Stage2 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1211,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 @@ -1459,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`. @@ -1532,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) ) } @@ -1589,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 @@ -1614,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 + ) } /** @@ -1645,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`. @@ -1710,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 @@ -1765,12 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1780,7 +1861,12 @@ private module Stage3 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1850,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 @@ -2120,8 +2215,6 @@ 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] @@ -2174,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`. @@ -2247,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) ) } @@ -2304,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 @@ -2329,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 + ) } /** @@ -2360,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`. @@ -2425,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 @@ -2480,12 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2495,7 +2616,12 @@ private module Stage4 { private predicate revFlowInToReturn( 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 + ) } /** @@ -2565,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 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 058d66b1496..9b14db7ef88 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -532,7 +532,9 @@ private module Stage1 { } /** - * 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) { @@ -605,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 @@ -827,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`. @@ -893,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) ) } @@ -950,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 @@ -975,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 + ) } /** @@ -1006,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`. @@ -1071,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 @@ -1126,12 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1141,7 +1180,12 @@ private module Stage2 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1211,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 @@ -1459,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`. @@ -1532,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) ) } @@ -1589,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 @@ -1614,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 + ) } /** @@ -1645,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`. @@ -1710,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 @@ -1765,12 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1780,7 +1861,12 @@ private module Stage3 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1850,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 @@ -2120,8 +2215,6 @@ 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] @@ -2174,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`. @@ -2247,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) ) } @@ -2304,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 @@ -2329,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 + ) } /** @@ -2360,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`. @@ -2425,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 @@ -2480,12 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2495,7 +2616,12 @@ private module Stage4 { private predicate revFlowInToReturn( 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 + ) } /** @@ -2565,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 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 058d66b1496..9b14db7ef88 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -532,7 +532,9 @@ private module Stage1 { } /** - * 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) { @@ -605,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 @@ -827,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`. @@ -893,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) ) } @@ -950,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 @@ -975,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 + ) } /** @@ -1006,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`. @@ -1071,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 @@ -1126,12 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1141,7 +1180,12 @@ private module Stage2 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1211,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 @@ -1459,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`. @@ -1532,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) ) } @@ -1589,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 @@ -1614,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 + ) } /** @@ -1645,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`. @@ -1710,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 @@ -1765,12 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1780,7 +1861,12 @@ private module Stage3 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1850,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 @@ -2120,8 +2215,6 @@ 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] @@ -2174,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`. @@ -2247,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) ) } @@ -2304,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 @@ -2329,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 + ) } /** @@ -2360,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`. @@ -2425,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 @@ -2480,12 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2495,7 +2616,12 @@ private module Stage4 { private predicate revFlowInToReturn( 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 + ) } /** @@ -2565,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 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 058d66b1496..9b14db7ef88 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -532,7 +532,9 @@ private module Stage1 { } /** - * 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) { @@ -605,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 @@ -827,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`. @@ -893,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) ) } @@ -950,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 @@ -975,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 + ) } /** @@ -1006,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`. @@ -1071,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 @@ -1126,12 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1141,7 +1180,12 @@ private module Stage2 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1211,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 @@ -1459,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`. @@ -1532,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) ) } @@ -1589,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 @@ -1614,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 + ) } /** @@ -1645,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`. @@ -1710,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 @@ -1765,12 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1780,7 +1861,12 @@ private module Stage3 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1850,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 @@ -2120,8 +2215,6 @@ 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] @@ -2174,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`. @@ -2247,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) ) } @@ -2304,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 @@ -2329,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 + ) } /** @@ -2360,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`. @@ -2425,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 @@ -2480,12 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2495,7 +2616,12 @@ private module Stage4 { private predicate revFlowInToReturn( 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 + ) } /** @@ -2565,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 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 058d66b1496..9b14db7ef88 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -532,7 +532,9 @@ private module Stage1 { } /** - * 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) { @@ -605,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 @@ -827,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`. @@ -893,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) ) } @@ -950,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 @@ -975,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 + ) } /** @@ -1006,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`. @@ -1071,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 @@ -1126,12 +1167,10 @@ private module Stage2 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1141,7 +1180,12 @@ private module Stage2 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1211,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 @@ -1459,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`. @@ -1532,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) ) } @@ -1589,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 @@ -1614,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 + ) } /** @@ -1645,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`. @@ -1710,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 @@ -1765,12 +1848,10 @@ private module Stage3 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -1780,7 +1861,12 @@ private module Stage3 { private predicate revFlowInToReturn( 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 + ) } /** @@ -1850,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 @@ -2120,8 +2215,6 @@ 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] @@ -2174,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`. @@ -2247,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) ) } @@ -2304,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 @@ -2329,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 + ) } /** @@ -2360,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`. @@ -2425,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 @@ -2480,12 +2603,10 @@ private module Stage4 { } pragma[nomagic] - private predicate revFlowIn( - DataFlowCall call, ArgNode arg, boolean toReturn, ApOption returnAp, Ap ap, Configuration config - ) { + private predicate revFlowInNotToReturn(ArgNode arg, ApOption returnAp, Ap ap, Configuration config) { exists(ParamNode p, boolean allowsFieldFlow | - revFlow(p, toReturn, returnAp, ap, config) and - flowIntoCall(call, arg, p, allowsFieldFlow, config) + revFlow(p, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) | ap instanceof ApNil or allowsFieldFlow = true ) @@ -2495,7 +2616,12 @@ private module Stage4 { private predicate revFlowInToReturn( 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 + ) } /** @@ -2565,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 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 bdf5498d8c6..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 @@ -580,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/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/dispatch/Dispatch.qll b/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll index cca858d69ba..f06fbca375d 100644 --- a/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll +++ b/csharp/ql/src/semmle/code/csharp/dispatch/Dispatch.qll @@ -387,7 +387,8 @@ private module Internal { result = this.getAStaticTarget() or result.getUnboundDeclaration() = - this.getASubsumedStaticTarget0(Gvn::getGlobalValueNumber(result.getDeclaringType())) + this.getASubsumedStaticTarget0(pragma[only_bind_out](Gvn::getGlobalValueNumber(result + .getDeclaringType()))) } /** diff --git a/csharp/ql/test/experimental/ir/ir/raw_ir.expected b/csharp/ql/test/experimental/ir/ir/raw_ir.expected index c598a8778d9..5d3a1dfe6bf 100644 --- a/csharp/ql/test/experimental/ir/ir/raw_ir.expected +++ b/csharp/ql/test/experimental/ir/ir/raw_ir.expected @@ -613,48 +613,48 @@ foreach.cs: # 5| r5_28(glval) = PointerAdd[4] : r5_1, r5_27 # 5| r5_29(Int32) = Constant[7] : # 5| mu5_30(Int32) = Store[?] : &:r5_28, r5_29 -# 7| r7_1(glval) = VariableAddress[#temp7:9] : +# 7| r7_1(glval) = VariableAddress[#temp7:9] : # 7| r7_2(glval) = VariableAddress[a_array] : # 7| r7_3(Int32[]) = Load[a_array] : &:r7_2, ~m? # 7| r7_4() = FunctionAddress[GetEnumerator] : # 7| r7_5(IEnumerator) = Call[GetEnumerator] : func:r7_4, this:r7_3 # 7| mu7_6() = ^CallSideEffect : ~m? -# 7| mu7_7(Boolean) = Store[#temp7:9] : &:r7_1, r7_5 +# 7| mu7_7(IEnumerator) = Store[#temp7:9] : &:r7_1, r7_5 #-----| 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) 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/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/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 7f8a89af2e2..db98e8b9d0b 100644 --- a/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected +++ b/csharp/ql/test/library-tests/dataflow/global/GetAnOutNode.expected @@ -227,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 | @@ -313,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 faaef439eb5..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 | @@ -2305,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 | @@ -2315,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 | @@ -2346,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 | @@ -2366,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 | @@ -2376,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 | @@ -2388,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 | @@ -2715,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 | @@ -2729,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/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/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/modifiers/Effectively.expected b/csharp/ql/test/library-tests/modifiers/Effectively.expected index 454295f0065..36afd1bc288 100644 --- a/csharp/ql/test/library-tests/modifiers/Effectively.expected +++ b/csharp/ql/test/library-tests/modifiers/Effectively.expected @@ -21,3 +21,11 @@ | Modifiers.cs:54:52:54:54 | set_P1 | public | | Modifiers.cs:55:20:55:21 | P2 | public | | Modifiers.cs:55:36:55:38 | get_P2 | public | +| Modifiers.cs:60:22:60:23 | I1 | public | +| Modifiers.cs:62:14:62:15 | M1 | public | +| Modifiers.cs:63:14:63:15 | M2 | public | +| Modifiers.cs:68:14:68:15 | M1 | internal | +| Modifiers.cs:71:18:71:19 | C2 | public | +| Modifiers.cs:73:17:73:18 | M1 | internal | +| Modifiers.cs:75:32:75:33 | M2 | internal | +| Modifiers.cs:76:33:76:34 | M3 | public | diff --git a/csharp/ql/test/library-tests/modifiers/Modifiers.cs b/csharp/ql/test/library-tests/modifiers/Modifiers.cs index c0377abb9e0..3868b5f9cc4 100644 --- a/csharp/ql/test/library-tests/modifiers/Modifiers.cs +++ b/csharp/ql/test/library-tests/modifiers/Modifiers.cs @@ -56,4 +56,23 @@ namespace N /*private*/ int P3 { /*private*/ get; /*private*/ set; } } + + public interface I1 + { + void M1(); + void M2() => throw null; + } + + internal interface I2 + { + void M1() => throw null; + } + + public class C2 : I2 + { + void I2.M1() => throw null; + + protected private void M2() { } + protected internal void M3() { } + } } diff --git a/csharp/ql/test/library-tests/modifiers/Modifiers.expected b/csharp/ql/test/library-tests/modifiers/Modifiers.expected index 8f89e302453..50f6ee31a0b 100644 --- a/csharp/ql/test/library-tests/modifiers/Modifiers.expected +++ b/csharp/ql/test/library-tests/modifiers/Modifiers.expected @@ -44,3 +44,17 @@ | Modifiers.cs:57:13:57:14 | P3 | file://:0:0:0:0 | private | | Modifiers.cs:57:30:57:32 | get_P3 | file://:0:0:0:0 | private | | Modifiers.cs:57:47:57:49 | set_P3 | file://:0:0:0:0 | private | +| Modifiers.cs:60:22:60:23 | I1 | file://:0:0:0:0 | public | +| Modifiers.cs:62:14:62:15 | M1 | file://:0:0:0:0 | abstract | +| Modifiers.cs:62:14:62:15 | M1 | file://:0:0:0:0 | public | +| Modifiers.cs:63:14:63:15 | M2 | file://:0:0:0:0 | public | +| Modifiers.cs:63:14:63:15 | M2 | file://:0:0:0:0 | virtual | +| Modifiers.cs:66:24:66:25 | I2 | file://:0:0:0:0 | internal | +| Modifiers.cs:68:14:68:15 | M1 | file://:0:0:0:0 | public | +| Modifiers.cs:68:14:68:15 | M1 | file://:0:0:0:0 | virtual | +| Modifiers.cs:71:18:71:19 | C2 | file://:0:0:0:0 | public | +| Modifiers.cs:73:17:73:18 | M1 | file://:0:0:0:0 | private | +| Modifiers.cs:75:32:75:33 | M2 | file://:0:0:0:0 | private | +| Modifiers.cs:75:32:75:33 | M2 | file://:0:0:0:0 | protected | +| Modifiers.cs:76:33:76:34 | M3 | file://:0:0:0:0 | internal | +| Modifiers.cs:76:33:76:34 | M3 | file://:0:0:0:0 | protected | 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/Stubs/MinimalStubsFromSource.expected b/csharp/ql/test/query-tests/Stubs/MinimalStubsFromSource.expected index d4a68c97906..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 = 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}\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 = 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}\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/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst b/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst index 6d30fee7f65..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:: @@ -142,6 +143,13 @@ These are stored alongside the code scanning suites with names of the form: ```." +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/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/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 fb89100b401..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 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 b20bead83e2..790cfd0ada7 100644 --- a/docs/codeql/support/reusables/frameworks.rst +++ b/docs/codeql/support/reusables/frameworks.rst @@ -1,7 +1,7 @@ C and C++ built-in support ================================ -.. csv-table:: +.. csv-table:: :header-rows: 1 :class: fullWidthTable :widths: auto @@ -14,7 +14,7 @@ C and C++ built-in support C# built-in support ================================ -.. csv-table:: +.. csv-table:: :header-rows: 1 :class: fullWidthTable :widths: auto @@ -84,7 +84,7 @@ Go built-in support Java built-in support ================================== -.. csv-table:: +.. csv-table:: :header-rows: 1 :class: fullWidthTable :widths: auto @@ -109,7 +109,7 @@ Java built-in support JavaScript and TypeScript built-in support ======================================================= -.. csv-table:: +.. csv-table:: :header-rows: 1 :class: fullWidthTable :widths: auto @@ -152,15 +152,24 @@ 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 + aioch, Database + clickhouse-driver, Database 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 9bc3c855e58..74b9c196564 100644 --- a/docs/codeql/support/reusables/versions-compilers.rst +++ b/docs/codeql/support/reusables/versions-compilers.rst @@ -17,18 +17,18 @@ .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 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",Not applicable,``.py`` + 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. 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/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-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-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/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/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/Resource Leaks/CloseReader.qhelp b/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.qhelp index b0ded8e53a1..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 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/CloseWriter.qhelp b/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.qhelp index 0b348a3f9b8..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.

    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/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 6342a444a64..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 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 aef404aabd1..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 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 3426d9f6f62..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`. @@ -83,14 +84,14 @@ predicate stackTraceExpr(Expr exception, MethodAccess stackTraceString) { ) } -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 } } /** @@ -105,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) ) @@ -123,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 0bf7a164826..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 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 77980f033f0..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 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 7b026efb7ae..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 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.ql b/java/ql/src/Security/CWE/CWE-502/UnsafeDeserialization.ql index bb4df03cd4f..82dd42c3c32 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.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/Security/CWE/CWE-643/XPathInjection.ql b/java/ql/src/Security/CWE/CWE-643/XPathInjection.ql index 7e23aa7cb77..0dd73370569 100644 --- a/java/ql/src/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 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 c76527bc538..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 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/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 89d7cb496a4..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-094/JexlInjectionLib.qll +++ /dev/null @@ -1,277 +0,0 @@ -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 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 - hasGetterFlow(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() - ) -} - -/** - * 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.ql b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql index c44eee23602..c6a7f583b14 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql @@ -3,6 +3,8 @@ * @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 diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/RmiRemoteObjectWithFilter.java b/java/ql/src/experimental/Security/CWE/CWE-502/RmiRemoteObjectWithFilter.java new file mode 100644 index 00000000000..6d4d2a5357f --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-502/RmiRemoteObjectWithFilter.java @@ -0,0 +1,9 @@ +public void bindRemoteObject(Registry registry, int port) throws Exception { + ObjectInputFilter filter = info -> { + if (info.serialClass().getCanonicalName().startsWith("com.safe.package.")) { + return ObjectInputFilter.Status.ALLOWED; + } + return ObjectInputFilter.Status.REJECTED; + }; + registry.bind("safer", UnicastRemoteObject.exportObject(new RemoteObjectImpl(), port, filter)); +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/RmiSafeRemoteObject.java b/java/ql/src/experimental/Security/CWE/CWE-502/RmiSafeRemoteObject.java new file mode 100644 index 00000000000..0c8836522ca --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-502/RmiSafeRemoteObject.java @@ -0,0 +1,14 @@ +public class Server { + public void bindRemoteObject(Registry registry) throws Exception { + registry.bind("safe", new RemoteObjectImpl()); + } +} + +interface RemoteObject extends Remote { + void calculate(int a, double b) throws RemoteException; + void save(String s) throws RemoteException; +} + +class RemoteObjectImpl implements RemoteObject { + // ... +} \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeRemoteObject.java b/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeRemoteObject.java new file mode 100644 index 00000000000..96d48e9c9af --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-502/RmiUnsafeRemoteObject.java @@ -0,0 +1,13 @@ +public class Server { + public void bindRemoteObject(Registry registry) throws Exception { + registry.bind("unsafe", new RemoteObjectImpl()); + } +} + +interface RemoteObject extends Remote { + void action(Object obj) throws RemoteException; +} + +class RemoteObjectImpl implements RemoteObject { + // ... +} \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.qhelp b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.qhelp new file mode 100644 index 00000000000..02ee7d7dab1 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.qhelp @@ -0,0 +1,81 @@ + + + + +

    +Java RMI uses the default Java serialization mechanism (in other words, ObjectInputStream) +to pass parameters in remote method invocations. This mechanism is known to be unsafe when deserializing +untrusted data. If a registered remote object has a method that accepts a complex object, +an attacker can take advantage of the unsafe deserialization mechanism. +In the worst case, it results in remote code execution. +

    +
    + + +

    +Use only strings and primitive types for parameters of remotely invokable methods. +

    +

    +Set a filter for incoming serialized data by wrapping remote objects using either UnicastRemoteObject.exportObject(Remote, int, ObjectInputFilter) +or UnicastRemoteObject.exportObject(Remote, int, RMIClientSocketFactory, RMIServerSocketFactory, ObjectInputFilter) methods. +Those methods accept an ObjectInputFilter that decides which classes are allowed for deserialization. +The filter should allow deserializing only safe classes. +

    +

    +It is also possible to set a process-wide deserialization filter. +The filter can be set by with ObjectInputFilter.Config.setSerialFilter(ObjectInputFilter) method, +or by setting system or security property jdk.serialFilter. +Make sure that you use the latest Java versions that include JEP 290. +Please note that the query is not sensitive to this mitigation. +

    +

    +If switching to the latest Java versions is not possible, +consider using other implementations of remote procedure calls. For example, HTTP API with JSON. +Make sure that the underlying deserialization mechanism is properly configured +so that deserialization attacks are not possible. +

    +
    + + +

    +The following code registers a remote object +with a vulnerable method that accepts a complex object: +

    + + +

    +The next example registers a safe remote object +whose methods use only primitive types and strings: +

    + + +

    +The next example shows how to set a deserilization filter for a remote object: +

    + + +
    + + +
  • +Oracle: +Remote Method Invocation (RMI). +
  • +
  • +ITNEXT: +Java RMI for pentesters part two - reconnaissance & attack against non-JMX registries. +
  • +
  • +MOGWAI LABS: +Attacking Java RMI services after JEP 290 +
  • +
  • +OWASP: +Deserialization of untrusted data. +
  • +
  • +OpenJDK: +JEP 290: Filter Incoming Serialization Data +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.ql b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.ql new file mode 100644 index 00000000000..f870d6f423e --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-502/UnsafeDeserializationRmi.ql @@ -0,0 +1,78 @@ +/** + * @name Unsafe deserialization in a remotely callable method. + * @description If a registered remote object has a method that accepts a complex object, + * an attacker can take advantage of the unsafe deserialization mechanism + * which is used to pass parameters in RMI. + * In the worst case, it results in remote code execution. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/unsafe-deserialization-rmi + * @tags security + * external/cwe/cwe-502 + */ + +import java +import semmle.code.java.dataflow.TaintTracking +import semmle.code.java.frameworks.Rmi +import DataFlow::PathGraph + +/** + * A method that binds a name to a remote object. + */ +private class BindMethod extends Method { + BindMethod() { + ( + getDeclaringType().hasQualifiedName("java.rmi", "Naming") or + getDeclaringType().hasQualifiedName("java.rmi.registry", "Registry") + ) and + hasName(["bind", "rebind"]) + } +} + +/** + * Holds if `type` has an vulnerable remote method. + */ +private predicate hasVulnerableMethod(RefType type) { + exists(RemoteCallableMethod m, Type parameterType | + m.getDeclaringType() = type and parameterType = m.getAParamType() + | + not parameterType instanceof PrimitiveType and + not parameterType instanceof TypeString and + not parameterType.(RefType).hasQualifiedName("java.io", "ObjectInputStream") + ) +} + +/** + * A taint-tracking configuration for unsafe remote objects + * that are vulnerable to deserialization attacks. + */ +private class BindingUnsafeRemoteObjectConfig extends TaintTracking::Configuration { + BindingUnsafeRemoteObjectConfig() { this = "BindingUnsafeRemoteObjectConfig" } + + override predicate isSource(DataFlow::Node source) { + exists(ConstructorCall cc | cc = source.asExpr() | + hasVulnerableMethod(cc.getConstructedType().getASupertype*()) + ) + } + + override predicate isSink(DataFlow::Node sink) { + exists(MethodAccess ma | ma.getArgument(1) = sink.asExpr() | + ma.getMethod() instanceof BindMethod + ) + } + + override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) { + exists(MethodAccess ma, Method m | m = ma.getMethod() | + m.getDeclaringType().hasQualifiedName("java.rmi.server", "UnicastRemoteObject") and + m.hasName("exportObject") and + not m.getParameterType([2, 4]).(RefType).hasQualifiedName("java.io", "ObjectInputFilter") and + ma.getArgument(0) = fromNode.asExpr() and + ma = toNode.asExpr() + ) + } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, BindingUnsafeRemoteObjectConfig conf +where conf.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Unsafe deserialization in a remote object." diff --git a/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.java b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.java new file mode 100644 index 00000000000..387648a443e --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.java @@ -0,0 +1,38 @@ +package com.example.demo; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class DemoApplication { + + @GetMapping("/string1") + public String string1(@RequestParam(value = "input", defaultValue = "test") String input, + @RequestParam(value = "pattern", defaultValue = ".*") String pattern) { + // BAD: Unsanitized user input is used to construct a regular expression + if (input.matches("^" + pattern + "=.*$")) + return "match!"; + + return "doesn't match!"; + } + + @GetMapping("/string2") + public String string2(@RequestParam(value = "input", defaultValue = "test") String input, + @RequestParam(value = "pattern", defaultValue = ".*") String pattern) { + // GOOD: User input is sanitized before constructing the regex + if (input.matches("^" + escapeSpecialRegexChars(pattern) + "=.*$")) + return "match!"; + + return "doesn't match!"; + } + + Pattern SPECIAL_REGEX_CHARS = Pattern.compile("[{}()\\[\\]><-=!.+*?^$\\\\|]"); + + String escapeSpecialRegexChars(String str) { + return SPECIAL_REGEX_CHARS.matcher(str).replaceAll("\\\\$0"); + } +} \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.qhelp new file mode 100644 index 00000000000..6cd80b52f75 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.qhelp @@ -0,0 +1,48 @@ + + + + +

    +Constructing a regular expression with unsanitized user input is dangerous as a malicious user may +be able to modify the meaning of the expression. In particular, such a user may be able to provide +a regular expression fragment that takes exponential time in the worst case, and use that to +perform a Denial of Service attack. +

    +
    + + +

    +Before embedding user input into a regular expression, use a sanitization function +to escape meta-characters that have special meaning. +

    +
    + + +

    +The following example shows a HTTP request parameter that is used to construct a regular expression: +

    + +

    +In the first case the user-provided regex is not escaped. +If a malicious user provides a regex that has exponential worst case performance, +then this could lead to a Denial of Service. +

    +

    +In the second case, the user input is escaped using escapeSpecialRegexChars before being included +in the regular expression. This ensures that the user cannot insert characters which have a special +meaning in regular expressions. +

    +
    + + +
  • +OWASP: +Regular expression Denial of Service - ReDoS. +
  • +
  • +Wikipedia: ReDoS. +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql new file mode 100644 index 00000000000..3b8b5dc759a --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-730/RegexInjection.ql @@ -0,0 +1,89 @@ +/** + * @name Regular expression injection + * @description User input should not be used in regular expressions without first being sanitized, + * otherwise a malicious user may be able to provide a regex that could require + * exponential time on certain inputs. + * @kind path-problem + * @problem.severity error + * @precision high + * @id java/regex-injection + * @tags security + * external/cwe/cwe-730 + * external/cwe/cwe-400 + */ + +import java +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.dataflow.TaintTracking +import DataFlow::PathGraph + +/** + * A data flow sink for untrusted user input used to construct regular expressions. + */ +class RegexSink extends DataFlow::ExprNode { + RegexSink() { + exists(MethodAccess ma, Method m | m = ma.getMethod() | + ( + m.getDeclaringType() instanceof TypeString and + ( + ma.getArgument(0) = this.asExpr() and + m.hasName(["matches", "split", "replaceFirst", "replaceAll"]) + ) + or + m.getDeclaringType().hasQualifiedName("java.util.regex", "Pattern") and + ( + ma.getArgument(0) = this.asExpr() and + m.hasName(["compile", "matches"]) + ) + or + m.getDeclaringType().hasQualifiedName("org.apache.commons.lang3", "RegExUtils") and + ( + ma.getArgument(1) = this.asExpr() and + m.getParameterType(1) instanceof TypeString and + m.hasName([ + "removeAll", "removeFirst", "removePattern", "replaceAll", "replaceFirst", + "replacePattern" + ]) + ) + ) + ) + } +} + +abstract class Sanitizer extends DataFlow::ExprNode { } + +/** + * A call to a function whose name suggests that it escapes regular + * expression meta-characters. + */ +class RegExpSanitizationCall extends Sanitizer { + RegExpSanitizationCall() { + exists(string calleeName, string sanitize, string regexp | + calleeName = this.asExpr().(Call).getCallee().getName() and + sanitize = "(?:escape|saniti[sz]e)" and + regexp = "regexp?" + | + calleeName + .regexpMatch("(?i)(" + sanitize + ".*" + regexp + ".*)" + "|(" + regexp + ".*" + sanitize + + ".*)") + ) + } +} + +/** + * A taint-tracking configuration for untrusted user input used to construct regular expressions. + */ +class RegexInjectionConfiguration extends TaintTracking::Configuration { + RegexInjectionConfiguration() { this = "RegexInjectionConfiguration" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof RegexSink } + + override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, RegexInjectionConfiguration c +where c.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "$@ is user controlled.", source.getNode(), + "This regular expression pattern" diff --git a/java/ql/src/semmle/code/java/Statement.qll b/java/ql/src/semmle/code/java/Statement.qll index ba56564ae51..ddae251a443 100755 --- a/java/ql/src/semmle/code/java/Statement.qll +++ b/java/ql/src/semmle/code/java/Statement.qll @@ -73,10 +73,10 @@ class BlockStmt extends Stmt, @block { /** Gets the last statement in this block. */ Stmt getLastStmt() { result = getStmt(getNumStmt() - 1) } - /** Gets a printable representation of this statement. May include more detail than `toString()`. */ override string pp() { result = "{ ... }" } - /** This statement's Halstead ID (used to compute Halstead metrics). */ + override string toString() { result = "{ ... }" } + override string getHalsteadID() { result = "BlockStmt" } override string getAPrimaryQlClass() { result = "BlockStmt" } @@ -130,14 +130,14 @@ class IfStmt extends ConditionalStmt, @ifstmt { /** Gets the `else` branch of this `if` statement. */ Stmt getElse() { result.isNthChildOf(this, 2) } - /** Gets a printable representation of this statement. May include more detail than `toString()`. */ override string pp() { result = "if (...) " + this.getThen().pp() + " else " + this.getElse().pp() or not exists(this.getElse()) and result = "if (...) " + this.getThen().pp() } - /** This statement's Halstead ID (used to compute Halstead metrics). */ + override string toString() { result = "if (...)" } + override string getHalsteadID() { result = "IfStmt" } override string getAPrimaryQlClass() { result = "IfStmt" } @@ -201,10 +201,10 @@ class ForStmt extends ConditionalStmt, @forstmt { getCondition().getAChildExpr*() = result.getAnAccess() } - /** Gets a printable representation of this statement. May include more detail than `toString()`. */ override string pp() { result = "for (...;...;...) " + this.getStmt().pp() } - /** This statement's Halstead ID (used to compute Halstead metrics). */ + override string toString() { result = "for (...;...;...)" } + override string getHalsteadID() { result = "ForStmt" } override string getAPrimaryQlClass() { result = "ForStmt" } @@ -221,10 +221,10 @@ class EnhancedForStmt extends Stmt, @enhancedforstmt { /** Gets the body of this enhanced `for` loop. */ Stmt getStmt() { result.getParent() = this } - /** Gets a printable representation of this statement. May include more detail than `toString()`. */ - override string pp() { result = "for (...) " + this.getStmt().pp() } + override string pp() { result = "for (... : ...) " + this.getStmt().pp() } + + override string toString() { result = "for (... : ...)" } - /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "EnhancedForStmt" } override string getAPrimaryQlClass() { result = "EnhancedForStmt" } @@ -244,10 +244,10 @@ class WhileStmt extends ConditionalStmt, @whilestmt { */ deprecated override Stmt getTrueSuccessor() { result = getStmt() } - /** Gets a printable representation of this statement. May include more detail than `toString()`. */ override string pp() { result = "while (...) " + this.getStmt().pp() } - /** This statement's Halstead ID (used to compute Halstead metrics). */ + override string toString() { result = "while (...)" } + override string getHalsteadID() { result = "WhileStmt" } override string getAPrimaryQlClass() { result = "WhileStmt" } @@ -267,10 +267,10 @@ class DoStmt extends ConditionalStmt, @dostmt { */ deprecated override Stmt getTrueSuccessor() { result = getStmt() } - /** Gets a printable representation of this statement. May include more detail than `toString()`. */ override string pp() { result = "do " + this.getStmt().pp() + " while (...)" } - /** This statement's Halstead ID (used to compute Halstead metrics). */ + override string toString() { result = "do ... while (...)" } + override string getHalsteadID() { result = "DoStmt" } override string getAPrimaryQlClass() { result = "DoStmt" } @@ -356,10 +356,10 @@ class TryStmt extends Stmt, @trystmt { result = getAResourceExpr().getVariable() } - /** Gets a printable representation of this statement. May include more detail than `toString()`. */ override string pp() { result = "try " + this.getBlock().pp() + " catch (...)" } - /** This statement's Halstead ID (used to compute Halstead metrics). */ + override string toString() { result = "try ..." } + override string getHalsteadID() { result = "TryStmt" } override string getAPrimaryQlClass() { result = "TryStmt" } @@ -387,10 +387,10 @@ class CatchClause extends Stmt, @catchclause { ) } - /** Gets a printable representation of this statement. May include more detail than `toString()`. */ override string pp() { result = "catch (...) " + this.getBlock().pp() } - /** This statement's Halstead ID (used to compute Halstead metrics). */ + override string toString() { result = "catch (...)" } + override string getHalsteadID() { result = "CatchClause" } override string getAPrimaryQlClass() { result = "CatchClause" } @@ -422,10 +422,10 @@ class SwitchStmt extends Stmt, @switchstmt { /** Gets the expression of this `switch` statement. */ Expr getExpr() { result.getParent() = this } - /** Gets a printable representation of this statement. May include more detail than `toString()`. */ override string pp() { result = "switch (...)" } - /** This statement's Halstead ID (used to compute Halstead metrics). */ + override string toString() { result = "switch (...)" } + override string getHalsteadID() { result = "SwitchStmt" } override string getAPrimaryQlClass() { result = "SwitchStmt" } @@ -485,10 +485,10 @@ class ConstCase extends SwitchCase { */ Expr getValue(int i) { result.getParent() = this and result.getIndex() = i and i >= 0 } - /** Gets a printable representation of this statement. May include more detail than `toString()`. */ override string pp() { result = "case ..." } - /** This statement's Halstead ID (used to compute Halstead metrics). */ + override string toString() { result = "case ..." } + override string getHalsteadID() { result = "ConstCase" } override string getAPrimaryQlClass() { result = "ConstCase" } @@ -498,10 +498,10 @@ class ConstCase extends SwitchCase { class DefaultCase extends SwitchCase { DefaultCase() { not exists(Expr e | e.getParent() = this | e.getIndex() >= 0) } - /** Gets a printable representation of this statement. May include more detail than `toString()`. */ override string pp() { result = "default" } - /** This statement's Halstead ID (used to compute Halstead metrics). */ + override string toString() { result = "default" } + override string getHalsteadID() { result = "DefaultCase" } override string getAPrimaryQlClass() { result = "DefaultCase" } @@ -515,10 +515,10 @@ class SynchronizedStmt extends Stmt, @synchronizedstmt { /** Gets the block of this `synchronized` statement. */ Stmt getBlock() { result.getParent() = this } - /** Gets a printable representation of this statement. May include more detail than `toString()`. */ override string pp() { result = "synchronized (...) " + this.getBlock().pp() } - /** This statement's Halstead ID (used to compute Halstead metrics). */ + override string toString() { result = "synchronized (...)" } + override string getHalsteadID() { result = "SynchronizedStmt" } override string getAPrimaryQlClass() { result = "SynchronizedStmt" } @@ -529,10 +529,10 @@ class ReturnStmt extends Stmt, @returnstmt { /** Gets the expression returned by this `return` statement, if any. */ Expr getResult() { result.getParent() = this } - /** Gets a printable representation of this statement. May include more detail than `toString()`. */ override string pp() { result = "return ..." } - /** This statement's Halstead ID (used to compute Halstead metrics). */ + override string toString() { result = "return ..." } + override string getHalsteadID() { result = "ReturnStmt" } override string getAPrimaryQlClass() { result = "ReturnStmt" } @@ -543,10 +543,10 @@ class ThrowStmt extends Stmt, @throwstmt { /** Gets the expression thrown by this `throw` statement. */ Expr getExpr() { result.getParent() = this } - /** Gets a printable representation of this statement. May include more detail than `toString()`. */ override string pp() { result = "throw ..." } - /** This statement's Halstead ID (used to compute Halstead metrics). */ + override string toString() { result = "throw ..." } + override string getHalsteadID() { result = "ThrowStmt" } /** Gets the type of the expression thrown by this `throw` statement. */ @@ -638,12 +638,12 @@ class BreakStmt extends Stmt, @breakstmt { /** Holds if this `break` statement has an explicit label. */ predicate hasLabel() { exists(string s | s = this.getLabel()) } - /** Gets a printable representation of this statement. May include more detail than `toString()`. */ override string pp() { if this.hasLabel() then result = "break " + this.getLabel() else result = "break" } - /** This statement's Halstead ID (used to compute Halstead metrics). */ + override string toString() { result = "break" } + override string getHalsteadID() { result = "BreakStmt" } override string getAPrimaryQlClass() { result = "BreakStmt" } @@ -660,6 +660,8 @@ class YieldStmt extends Stmt, @yieldstmt { override string pp() { result = "yield ..." } + override string toString() { result = "yield ..." } + override string getHalsteadID() { result = "YieldStmt" } override string getAPrimaryQlClass() { result = "YieldStmt" } @@ -673,12 +675,12 @@ class ContinueStmt extends Stmt, @continuestmt { /** Holds if this `continue` statement has an explicit label. */ predicate hasLabel() { exists(string s | s = this.getLabel()) } - /** Gets a printable representation of this statement. May include more detail than `toString()`. */ override string pp() { if this.hasLabel() then result = "continue " + this.getLabel() else result = "continue" } - /** This statement's Halstead ID (used to compute Halstead metrics). */ + override string toString() { result = "continue" } + override string getHalsteadID() { result = "ContinueStmt" } override string getAPrimaryQlClass() { result = "ContinueStmt" } @@ -686,10 +688,10 @@ class ContinueStmt extends Stmt, @continuestmt { /** The empty statement. */ class EmptyStmt extends Stmt, @emptystmt { - /** Gets a printable representation of this statement. May include more detail than `toString()`. */ override string pp() { result = ";" } - /** This statement's Halstead ID (used to compute Halstead metrics). */ + override string toString() { result = ";" } + override string getHalsteadID() { result = "EmptyStmt" } override string getAPrimaryQlClass() { result = "EmptyStmt" } @@ -704,10 +706,10 @@ class ExprStmt extends Stmt, @exprstmt { /** Gets the expression of this expression statement. */ Expr getExpr() { result.getParent() = this } - /** Gets a printable representation of this statement. May include more detail than `toString()`. */ - override string pp() { result = "...;" } + override string pp() { result = ";" } + + override string toString() { result = ";" } - /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "ExprStmt" } /** Holds if this statement represents a field declaration with an initializer. */ @@ -733,13 +735,13 @@ class LabeledStmt extends Stmt, @labeledstmt { /** Gets the label of this labeled statement. */ string getLabel() { namestrings(result, _, this) } - /** Gets a printable representation of this statement. May include more detail than `toString()`. */ override string pp() { result = this.getLabel() + ": " + this.getStmt().pp() } - /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = this.getLabel() + ":" } - override string getAPrimaryQlClass() { result = "LabelStmt" } + override string toString() { result = "