diff --git a/.github/ISSUE_TEMPLATE/lgtm-com---false-positive.md b/.github/ISSUE_TEMPLATE/lgtm-com---false-positive.md
deleted file mode 100644
index 94a84906f24..00000000000
--- a/.github/ISSUE_TEMPLATE/lgtm-com---false-positive.md
+++ /dev/null
@@ -1,24 +0,0 @@
----
-name: LGTM.com - false positive
-about: Tell us about an alert that shouldn't be reported
-title: LGTM.com - false positive
-labels: false-positive
-assignees: ''
-
----
-
-**Description of the false positive**
-
-
-
-**URL to the alert on the project page on LGTM.com**
-
-
diff --git a/.github/ISSUE_TEMPLATE/ql---general.md b/.github/ISSUE_TEMPLATE/ql---general.md
index 1d5b7d244f6..f68574485c1 100644
--- a/.github/ISSUE_TEMPLATE/ql---general.md
+++ b/.github/ISSUE_TEMPLATE/ql---general.md
@@ -10,5 +10,5 @@ assignees: ''
**Description of the issue**
+If it is about a GitHub project, please include its URL. -->
diff --git a/.github/ISSUE_TEMPLATE/ql--false-positive.md b/.github/ISSUE_TEMPLATE/ql--false-positive.md
new file mode 100644
index 00000000000..b80590a1832
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/ql--false-positive.md
@@ -0,0 +1,36 @@
+---
+name: CodeQL false positive
+about: Report CodeQL alerts that you think should not have been detected (not applicable, not exploitable, etc.)
+title: False positive
+labels: false-positive
+assignees: ''
+
+---
+
+**Description of the false positive**
+
+
+
+**Code samples or links to source code**
+
+
+
+**URL to the alert on GitHub code scanning (optional)**
+
+
diff --git a/.github/actions/cache-query-compilation/action.yml b/.github/actions/cache-query-compilation/action.yml
new file mode 100644
index 00000000000..95f25cbfc68
--- /dev/null
+++ b/.github/actions/cache-query-compilation/action.yml
@@ -0,0 +1,55 @@
+name: Cache query compilation
+description: Caches CodeQL compilation caches - should be run both on PRs and pushes to main.
+
+inputs:
+ key:
+ description: 'The cache key to use - should be unique to the workflow'
+ required: true
+
+outputs:
+ cache-dir:
+ description: "The directory where the cache was stored"
+ value: ${{ steps.fill-compilation-dir.outputs.compdir }}
+
+runs:
+ using: composite
+ steps:
+ # calculate the merge-base with main, in a way that works both on PRs and pushes to main.
+ - name: Calculate merge-base
+ shell: bash
+ if: ${{ github.event_name == 'pull_request' }}
+ env:
+ BASE_BRANCH: ${{ github.base_ref }}
+ run: |
+ MERGE_BASE=$(git cat-file commit $GITHUB_SHA | grep '^parent ' | head -1 | cut -f 2 -d " ")
+ echo "merge_base=$MERGE_BASE" >> $GITHUB_ENV
+ - name: Restore read-only cache (PR)
+ if: ${{ github.event_name == 'pull_request' }}
+ uses: erik-krogh/actions-cache@a88d0603fe5fb5606db9f002dfcadeb32b5f84c6
+ with:
+ path: '**/.cache'
+ read-only: true
+ key: codeql-compile-${{ inputs.key }}-pr-${{ github.sha }}
+ restore-keys: |
+ codeql-compile-${{ inputs.key }}-${{ github.base_ref }}-${{ env.merge_base }}
+ codeql-compile-${{ inputs.key }}-${{ github.base_ref }}-
+ codeql-compile-${{ inputs.key }}-main-
+ - name: Fill cache (push)
+ if: ${{ github.event_name != 'pull_request' }}
+ uses: erik-krogh/actions-cache@a88d0603fe5fb5606db9f002dfcadeb32b5f84c6
+ with:
+ path: '**/.cache'
+ key: codeql-compile-${{ inputs.key }}-${{ github.ref_name }}-${{ github.sha }} # just fill on main
+ restore-keys: | # restore the latest cache if the exact cache is unavailable, to speed up compilation.
+ codeql-compile-${{ inputs.key }}-${{ github.ref_name }}-
+ codeql-compile-${{ inputs.key }}-main-
+ - name: Fill compilation cache directory
+ id: fill-compilation-dir
+ shell: bash
+ run: |
+ # Move all the existing cache into another folder, so we only preserve the cache for the current queries.
+ node $GITHUB_WORKSPACE/.github/actions/cache-query-compilation/move-caches.js ${COMBINED_CACHE_DIR}
+
+ echo "compdir=${COMBINED_CACHE_DIR}" >> $GITHUB_OUTPUT
+ env:
+ COMBINED_CACHE_DIR: ${{ runner.temp }}/compilation-dir
diff --git a/.github/actions/cache-query-compilation/move-caches.js b/.github/actions/cache-query-compilation/move-caches.js
new file mode 100644
index 00000000000..67fc503cdc0
--- /dev/null
+++ b/.github/actions/cache-query-compilation/move-caches.js
@@ -0,0 +1,75 @@
+// # Move all the existing cache into another folder, so we only preserve the cache for the current queries.
+// mkdir -p ${COMBINED_CACHE_DIR}
+// rm -f **/.cache/{lock,size} # -f to avoid errors if the cache is empty.
+// # copy the contents of the .cache folders into the combined cache folder.
+// cp -r **/.cache/* ${COMBINED_CACHE_DIR}/ || : # ignore missing files
+// # clean up the .cache folders
+// rm -rf **/.cache/*
+
+const fs = require("fs");
+const path = require("path");
+
+// the first argv is the cache folder to create.
+const COMBINED_CACHE_DIR = process.argv[2];
+
+function* walkCaches(dir) {
+ const files = fs.readdirSync(dir, { withFileTypes: true });
+ for (const file of files) {
+ if (file.isDirectory()) {
+ const filePath = path.join(dir, file.name);
+ yield* walkCaches(filePath);
+ if (file.name === ".cache") {
+ yield filePath;
+ }
+ }
+ }
+}
+
+async function copyDir(src, dest) {
+ for await (const file of await fs.promises.readdir(src, { withFileTypes: true })) {
+ const srcPath = path.join(src, file.name);
+ const destPath = path.join(dest, file.name);
+ if (file.isDirectory()) {
+ if (!fs.existsSync(destPath)) {
+ fs.mkdirSync(destPath);
+ }
+ await copyDir(srcPath, destPath);
+ } else {
+ await fs.promises.copyFile(srcPath, destPath);
+ }
+ }
+}
+
+async function main() {
+ const cacheDirs = [...walkCaches(".")];
+
+ for (const dir of cacheDirs) {
+ console.log(`Found .cache dir at ${dir}`);
+ }
+
+ // mkdir -p ${COMBINED_CACHE_DIR}
+ fs.mkdirSync(COMBINED_CACHE_DIR, { recursive: true });
+
+ // rm -f **/.cache/{lock,size} # -f to avoid errors if the cache is empty.
+ await Promise.all(
+ cacheDirs.map((cacheDir) =>
+ (async function () {
+ await fs.promises.rm(path.join(cacheDir, "lock"), { force: true });
+ await fs.promises.rm(path.join(cacheDir, "size"), { force: true });
+ })()
+ )
+ );
+
+ // # copy the contents of the .cache folders into the combined cache folder.
+ // cp -r **/.cache/* ${COMBINED_CACHE_DIR}/ || : # ignore missing files
+ await Promise.all(
+ cacheDirs.map((cacheDir) => copyDir(cacheDir, COMBINED_CACHE_DIR))
+ );
+
+ // # clean up the .cache folders
+ // rm -rf **/.cache/*
+ await Promise.all(
+ cacheDirs.map((cacheDir) => fs.promises.rm(cacheDir, { recursive: true }))
+ );
+}
+main();
diff --git a/.github/actions/find-latest-bundle/action.yml b/.github/actions/find-latest-bundle/action.yml
new file mode 100644
index 00000000000..59e16a6d3cb
--- /dev/null
+++ b/.github/actions/find-latest-bundle/action.yml
@@ -0,0 +1,26 @@
+name: Find Latest CodeQL Bundle
+description: Finds the URL of the latest released version of the CodeQL bundle.
+outputs:
+ url:
+ description: The download URL of the latest CodeQL bundle release
+ value: ${{ steps.find-latest.outputs.url }}
+runs:
+ using: composite
+ steps:
+ - name: Find Latest Release
+ id: find-latest
+ shell: pwsh
+ run: |
+ $Latest = gh release list --repo github/codeql-action --exclude-drafts --limit 1000 |
+ ForEach-Object { $C = $_ -split "`t"; return @{ type = $C[1]; tag = $C[2]; } } |
+ Where-Object { $_.type -eq 'Latest' }
+
+ $Tag = $Latest.tag
+ if ($Tag -eq '') {
+ throw 'Failed to find latest bundle release.'
+ }
+
+ Write-Output "Latest bundle tag is '${Tag}'."
+ "url=https://github.com/github/codeql-action/releases/download/${Tag}/codeql-bundle-linux64.tar.gz" >> $env:GITHUB_OUTPUT
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
diff --git a/.github/workflows/atm-check-query-suite.yml b/.github/workflows/atm-check-query-suite.yml
index 7317746fe62..ed93a6f8f2f 100644
--- a/.github/workflows/atm-check-query-suite.yml
+++ b/.github/workflows/atm-check-query-suite.yml
@@ -13,7 +13,7 @@ on:
jobs:
atm-check-query-suite:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v3
@@ -23,6 +23,12 @@ jobs:
with:
channel: release
+ - name: Cache compilation cache
+ id: query-cache
+ uses: ./.github/actions/cache-query-compilation
+ with:
+ key: atm-suite
+
- name: Install ATM model
run: |
set -exu
@@ -50,10 +56,13 @@ jobs:
echo "SARIF_PATH=${SARIF_PATH}" >> "${GITHUB_ENV}"
codeql database analyze \
+ --threads=0 \
+ --ram 50000 \
--format sarif-latest \
--output "${SARIF_PATH}" \
--sarif-group-rules-by-pack \
-vv \
+ --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" \
-- \
"${DB_PATH}" \
"${QUERY_PACK}/${QUERY_SUITE}"
diff --git a/.github/workflows/check-query-ids.yml b/.github/workflows/check-query-ids.yml
new file mode 100644
index 00000000000..9ce9ed5ba85
--- /dev/null
+++ b/.github/workflows/check-query-ids.yml
@@ -0,0 +1,21 @@
+name: Check query IDs
+
+on:
+ pull_request:
+ paths:
+ - "**/src/**/*.ql"
+ - misc/scripts/check-query-ids.py
+ - .github/workflows/check-query-ids.yml
+ branches:
+ - main
+ - "rc/*"
+ workflow_dispatch:
+
+jobs:
+ check:
+ name: Check query IDs
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Check for duplicate query IDs
+ run: python3 misc/scripts/check-query-ids.py
diff --git a/.github/workflows/compile-queries.yml b/.github/workflows/compile-queries.yml
index 73d1e5ac2cc..96d8e4cc30b 100644
--- a/.github/workflows/compile-queries.yml
+++ b/.github/workflows/compile-queries.yml
@@ -14,46 +14,24 @@ jobs:
steps:
- uses: actions/checkout@v3
- # calculate the merge-base with main, in a way that works both on PRs and pushes to main.
- - name: Calculate merge-base
- if: ${{ github.event_name == 'pull_request' }}
- env:
- BASE_BRANCH: ${{ github.base_ref }}
- run: |
- MERGE_BASE=$(git cat-file commit $GITHUB_SHA | grep '^parent ' | head -1 | cut -f 2 -d " ")
- echo "merge-base=$MERGE_BASE" >> $GITHUB_ENV
- - name: Read CodeQL query compilation - PR
- if: ${{ github.event_name == 'pull_request' }}
- uses: actions/cache@v3
- with:
- path: '*/ql/src/.cache'
- key: codeql-compile-pr-${{ github.sha }} # deliberately not using the `compile-compile-main` keys here.
- restore-keys: |
- codeql-compile-${{ github.base_ref }}-${{ env.merge-base }}
- codeql-compile-${{ github.base_ref }}-
- codeql-compile-main-
- - name: Fill CodeQL query compilation cache - main
- if: ${{ github.event_name != 'pull_request' }}
- uses: actions/cache@v3
- with:
- path: '*/ql/src/.cache'
- key: codeql-compile-${{ github.ref_name }}-${{ github.sha }} # just fill on main
- restore-keys: | # restore from another random commit, to speed up compilation.
- codeql-compile-${{ github.ref_name }}-
- codeql-compile-main-
- name: Setup CodeQL
uses: ./.github/actions/fetch-codeql
with:
channel: 'release'
+ - name: Cache compilation cache
+ id: query-cache
+ uses: ./.github/actions/cache-query-compilation
+ with:
+ key: all-queries
- name: check formatting
- run: codeql query format */ql/{src,lib,test}/**/*.{qll,ql} --check-only
+ run: find */ql -type f \( -name "*.qll" -o -name "*.ql" \) -print0 | xargs -0 codeql query format --check-only
- name: compile queries - check-only
# run with --check-only if running in a PR (github.sha != main)
if : ${{ github.event_name == 'pull_request' }}
shell: bash
- run: codeql query compile -j0 */ql/src --keep-going --warnings=error --check-only
+ run: codeql query compile -j0 */ql/{src,examples} --keep-going --warnings=error --check-only --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
- name: compile queries - full
# do full compile if running on main - this populates the cache
if : ${{ github.event_name != 'pull_request' }}
shell: bash
- run: codeql query compile -j0 */ql/src --keep-going --warnings=error
\ No newline at end of file
+ run: codeql query compile -j0 */ql/{src,examples} --keep-going --warnings=error --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
diff --git a/.github/workflows/csharp-qltest.yml b/.github/workflows/csharp-qltest.yml
new file mode 100644
index 00000000000..b9bbf930add
--- /dev/null
+++ b/.github/workflows/csharp-qltest.yml
@@ -0,0 +1,86 @@
+name: "C#: Run QL Tests"
+
+on:
+ push:
+ paths:
+ - "csharp/**"
+ - "shared/**"
+ - .github/actions/fetch-codeql/action.yml
+ - codeql-workspace.yml
+ branches:
+ - main
+ - "rc/*"
+ pull_request:
+ paths:
+ - "csharp/**"
+ - "shared/**"
+ - .github/workflows/csharp-qltest.yml
+ - .github/actions/fetch-codeql/action.yml
+ - codeql-workspace.yml
+ branches:
+ - main
+ - "rc/*"
+
+defaults:
+ run:
+ working-directory: csharp
+
+jobs:
+ qlupgrade:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: ./.github/actions/fetch-codeql
+ - name: Check DB upgrade scripts
+ run: |
+ echo >empty.trap
+ codeql dataset import -S ql/lib/upgrades/initial/semmlecode.csharp.dbscheme testdb empty.trap
+ codeql dataset upgrade testdb --additional-packs ql/lib
+ diff -q testdb/semmlecode.csharp.dbscheme ql/lib/semmlecode.csharp.dbscheme
+ - name: Check DB downgrade scripts
+ run: |
+ echo >empty.trap
+ rm -rf testdb; codeql dataset import -S ql/lib/semmlecode.csharp.dbscheme testdb empty.trap
+ codeql resolve upgrades --format=lines --allow-downgrades --additional-packs downgrades \
+ --dbscheme=ql/lib/semmlecode.csharp.dbscheme --target-dbscheme=downgrades/initial/semmlecode.csharp.dbscheme |
+ xargs codeql execute upgrades testdb
+ diff -q testdb/semmlecode.csharp.dbscheme downgrades/initial/semmlecode.csharp.dbscheme
+ qltest:
+ runs-on: ubuntu-latest-xl
+ strategy:
+ fail-fast: false
+ matrix:
+ slice: ["1/2", "2/2"]
+ steps:
+ - uses: actions/checkout@v3
+ - uses: ./.github/actions/fetch-codeql
+ - uses: ./csharp/actions/create-extractor-pack
+ - name: Cache compilation cache
+ id: query-cache
+ uses: ./.github/actions/cache-query-compilation
+ with:
+ key: csharp-qltest-${{ matrix.slice }}
+ - name: Run QL tests
+ run: |
+ CODEQL_PATH=$(gh codeql version --format=json | jq -r .unpackedLocation)
+ # The legacy ASP extractor is not in this repo, so take the one from the nightly build
+ mv "$CODEQL_PATH/csharp/tools/extractor-asp.jar" "${{ github.workspace }}/csharp/extractor-pack/tools"
+ # Safe guard against using the bundled extractor
+ rm -rf "$CODEQL_PATH/csharp"
+ codeql test run --threads=0 --ram 50000 --slice ${{ matrix.slice }} --search-path "${{ github.workspace }}/csharp/extractor-pack" --check-databases --check-undefined-labels --check-repeated-labels --check-redefined-labels --consistency-queries ql/consistency-queries ql/test --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ unit-tests:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Setup dotnet
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: 6.0.202
+ - name: Extractor unit tests
+ run: |
+ dotnet test -p:RuntimeFrameworkVersion=6.0.4 "${{ github.workspace }}/csharp/extractor/Semmle.Util.Tests"
+ dotnet test -p:RuntimeFrameworkVersion=6.0.4 "${{ github.workspace }}/csharp/extractor/Semmle.Extraction.Tests"
+ dotnet test -p:RuntimeFrameworkVersion=6.0.4 "${{ github.workspace }}/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests"
+ dotnet test -p:RuntimeFrameworkVersion=6.0.4 "${{ github.workspace }}/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests"
diff --git a/.github/workflows/go-tests-other-os.yml b/.github/workflows/go-tests-other-os.yml
new file mode 100644
index 00000000000..edf6eb63d49
--- /dev/null
+++ b/.github/workflows/go-tests-other-os.yml
@@ -0,0 +1,80 @@
+name: "Go: Run Tests - Other OS"
+on:
+ pull_request:
+ paths:
+ - "go/**"
+ - "!go/ql/**" # don't run other-os if only ql/ files changed
+ - .github/workflows/go-tests-other-os.yml
+ - .github/actions/**
+ - codeql-workspace.yml
+jobs:
+ test-mac:
+ name: Test MacOS
+ runs-on: macos-latest
+ steps:
+ - name: Set up Go 1.19
+ uses: actions/setup-go@v3
+ with:
+ go-version: 1.19
+ id: go
+
+ - name: Check out code
+ uses: actions/checkout@v2
+
+ - name: Set up CodeQL CLI
+ uses: ./.github/actions/fetch-codeql
+
+ - name: Enable problem matchers in repository
+ shell: bash
+ run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
+
+ - name: Build
+ run: |
+ cd go
+ make
+
+ - name: Cache compilation cache
+ id: query-cache
+ uses: ./.github/actions/cache-query-compilation
+ with:
+ key: go-qltest
+ - name: Test
+ run: |
+ cd go
+ make test cache="${{ steps.query-cache.outputs.cache-dir }}"
+
+ test-win:
+ name: Test Windows
+ runs-on: windows-latest-xl
+ steps:
+ - name: Set up Go 1.19
+ uses: actions/setup-go@v3
+ with:
+ go-version: 1.19
+ id: go
+
+ - name: Check out code
+ uses: actions/checkout@v2
+
+ - name: Set up CodeQL CLI
+ uses: ./.github/actions/fetch-codeql
+
+ - name: Enable problem matchers in repository
+ shell: bash
+ run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
+
+ - name: Build
+ run: |
+ cd go
+ make
+
+ - name: Cache compilation cache
+ id: query-cache
+ uses: ./.github/actions/cache-query-compilation
+ with:
+ key: go-qltest
+
+ - name: Test
+ run: |
+ cd go
+ make test cache="${{ steps.query-cache.outputs.cache-dir }}"
diff --git a/.github/workflows/go-tests.yml b/.github/workflows/go-tests.yml
index 3cc61eafa5c..eceabb0410a 100644
--- a/.github/workflows/go-tests.yml
+++ b/.github/workflows/go-tests.yml
@@ -1,15 +1,24 @@
name: "Go: Run Tests"
on:
+ push:
+ paths:
+ - "go/**"
+ - .github/workflows/go-tests.yml
+ - .github/actions/**
+ - codeql-workspace.yml
+ branches:
+ - main
+ - "rc/*"
pull_request:
paths:
- "go/**"
- .github/workflows/go-tests.yml
- - .github/actions/fetch-codeql/action.yml
+ - .github/actions/**
- codeql-workspace.yml
jobs:
test-linux:
name: Test Linux (Ubuntu)
- runs-on: ubuntu-latest
+ runs-on: ubuntu-latest-xl
steps:
- name: Set up Go 1.19
uses: actions/setup-go@v3
@@ -32,7 +41,7 @@ jobs:
cd go
make
- - name: Check that all QL and Go code is autoformatted
+ - name: Check that all Go code is autoformatted
run: |
cd go
make check-formatting
@@ -48,67 +57,13 @@ jobs:
name: qhelp-markdown
path: go/qhelp-out/**/*.md
- - name: Test
- run: |
- cd go
- make test
-
- test-mac:
- name: Test MacOS
- runs-on: macos-latest
- steps:
- - name: Set up Go 1.19
- uses: actions/setup-go@v3
+ - name: Cache compilation cache
+ id: query-cache
+ uses: ./.github/actions/cache-query-compilation
with:
- go-version: 1.19
- id: go
-
- - name: Check out code
- uses: actions/checkout@v2
-
- - name: Set up CodeQL CLI
- uses: ./.github/actions/fetch-codeql
-
- - name: Enable problem matchers in repository
- shell: bash
- run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
-
- - name: Build
- run: |
- cd go
- make
+ key: go-qltest
- name: Test
run: |
cd go
- make test
-
- test-win:
- name: Test Windows
- runs-on: windows-2019
- steps:
- - name: Set up Go 1.19
- uses: actions/setup-go@v3
- with:
- go-version: 1.19
- id: go
-
- - name: Check out code
- uses: actions/checkout@v2
-
- - name: Set up CodeQL CLI
- uses: ./.github/actions/fetch-codeql
-
- - name: Enable problem matchers in repository
- shell: bash
- run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
-
- - name: Build
- run: |
- cd go
- make
-
- - name: Test
- run: |
- cd go
- make test
+ make test cache="${{ steps.query-cache.outputs.cache-dir }}"
diff --git a/.github/workflows/js-ml-tests.yml b/.github/workflows/js-ml-tests.yml
index c932432530b..90cb5691126 100644
--- a/.github/workflows/js-ml-tests.yml
+++ b/.github/workflows/js-ml-tests.yml
@@ -23,22 +23,9 @@ defaults:
working-directory: javascript/ql/experimental/adaptivethreatmodeling
jobs:
- qlformat:
- name: Check QL formatting
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
-
- - uses: ./.github/actions/fetch-codeql
-
- - name: Check QL formatting
- run: |
- find . "(" -name "*.ql" -or -name "*.qll" ")" -print0 | \
- xargs -0 codeql query format --check-only
-
- qlcompile:
- name: Check QL compilation
- runs-on: ubuntu-latest
+ qltest:
+ name: Test QL
+ runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v3
@@ -46,36 +33,33 @@ jobs:
- name: Install pack dependencies
run: |
- for pack in modelbuilding src; do
+ for pack in modelbuilding src test; do
codeql pack install --mode verify -- "${pack}"
done
+
+ - name: Cache compilation cache
+ id: query-cache
+ uses: ./.github/actions/cache-query-compilation
+ with:
+ key: js-ml-test
- name: Check QL compilation
run: |
codeql query compile \
--check-only \
- --ram 5120 \
+ --ram 50000 \
--additional-packs "${{ github.workspace }}" \
--threads=0 \
+ --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" \
-- \
lib modelbuilding src
- qltest:
- name: Run QL tests
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
-
- - uses: ./.github/actions/fetch-codeql
-
- - name: Install pack dependencies
- run: codeql pack install -- test
-
- name: Run QL tests
run: |
codeql test run \
--threads=0 \
- --ram 5120 \
+ --ram 50000 \
--additional-packs "${{ github.workspace }}" \
+ --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" \
-- \
- test
+ test
\ No newline at end of file
diff --git a/.github/workflows/mad_modelDiff.yml b/.github/workflows/mad_modelDiff.yml
index 04e5f486866..586b1576bd6 100644
--- a/.github/workflows/mad_modelDiff.yml
+++ b/.github/workflows/mad_modelDiff.yml
@@ -61,8 +61,8 @@ jobs:
DATABASE=$2
cd codeql-$QL_VARIANT
SHORTNAME=`basename $DATABASE`
- python java/ql/src/utils/model-generator/GenerateFlowModel.py --with-summaries --with-sinks $DATABASE $MODELS/${SHORTNAME}.qll
- mv $MODELS/${SHORTNAME}.qll $MODELS/${SHORTNAME}Generated_${QL_VARIANT}.qll
+ python java/ql/src/utils/model-generator/GenerateFlowModel.py --with-summaries --with-sinks $DATABASE ${SHORTNAME}.temp.model.yml
+ mv java/ql/lib/ext/generated/${SHORTNAME}.temp.model.yml $MODELS/${SHORTNAME}Generated_${QL_VARIANT}.model.yml
cd ..
}
@@ -85,16 +85,16 @@ jobs:
set -x
MODELS=`pwd`/tmp-models
ls -1 tmp-models/
- for m in $MODELS/*_main.qll ; do
+ for m in $MODELS/*_main.model.yml ; do
t="${m/main/"pr"}"
basename=`basename $m`
- name="diff_${basename/_main.qll/""}"
+ name="diff_${basename/_main.model.yml/""}"
(diff -w -u $m $t | diff2html -i stdin -F $MODELS/$name.html) || true
done
- uses: actions/upload-artifact@v3
with:
name: models
- path: tmp-models/*.qll
+ path: tmp-models/*.model.yml
retention-days: 20
- uses: actions/upload-artifact@v3
with:
diff --git a/.github/workflows/mad_regenerate-models.yml b/.github/workflows/mad_regenerate-models.yml
index 0abc8936911..d92e3652d5c 100644
--- a/.github/workflows/mad_regenerate-models.yml
+++ b/.github/workflows/mad_regenerate-models.yml
@@ -53,7 +53,7 @@ jobs:
java/ql/src/utils/model-generator/RegenerateModels.py "${SLUG}" dbs/${SHORTNAME}
- name: Stage changes
run: |
- find java -name "*.qll" -print0 | xargs -0 git add
+ find java -name "*.model.yml" -print0 | xargs -0 git add
git status
git diff --cached > models.patch
- uses: actions/upload-artifact@v3
diff --git a/.github/workflows/ql-for-ql-build.yml b/.github/workflows/ql-for-ql-build.yml
index 701dc4d612c..29b8bc16300 100644
--- a/.github/workflows/ql-for-ql-build.yml
+++ b/.github/workflows/ql-for-ql-build.yml
@@ -22,11 +22,15 @@ jobs:
steps:
### Build the queries ###
- uses: actions/checkout@v3
+ - name: Find latest bundle
+ id: find-latest-bundle
+ uses: ./.github/actions/find-latest-bundle
- name: Find codeql
id: find-codeql
uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
with:
languages: javascript # does not matter
+ tools: ${{ steps.find-latest-bundle.outputs.url }}
- name: Get CodeQL version
id: get-codeql-version
run: |
@@ -138,6 +142,7 @@ jobs:
languages: ql
db-location: ${{ runner.temp }}/db
config-file: ./ql-for-ql-config.yml
+ tools: ${{ steps.find-latest-bundle.outputs.url }}
- name: Move pack cache
run: |
cp -r ${PACK}/.cache ql/ql/src/.cache
diff --git a/.github/workflows/ql-for-ql-tests.yml b/.github/workflows/ql-for-ql-tests.yml
index b820d00a3e4..ce7963e8f79 100644
--- a/.github/workflows/ql-for-ql-tests.yml
+++ b/.github/workflows/ql-for-ql-tests.yml
@@ -47,8 +47,3 @@ jobs:
find ql/ql/src "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 "${CODEQL}" query format --check-only
env:
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
- - name: Check QL compilation
- run: |
- "${CODEQL}" query compile --check-only --threads=4 --warnings=error --search-path "${{ github.workspace }}/ql/extractor-pack" "ql/ql/src" "ql/ql/examples"
- env:
- CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
diff --git a/.github/workflows/ruby-build.yml b/.github/workflows/ruby-build.yml
index f7ec215a44a..f561af9c13b 100644
--- a/.github/workflows/ruby-build.yml
+++ b/.github/workflows/ruby-build.yml
@@ -48,7 +48,19 @@ jobs:
run: |
brew install gnu-tar
echo "/usr/local/opt/gnu-tar/libexec/gnubin" >> $GITHUB_PATH
+ - name: Cache entire extractor
+ uses: actions/cache@v3
+ id: cache-extractor
+ with:
+ path: |
+ ruby/target/release/ruby-autobuilder
+ ruby/target/release/ruby-autobuilder.exe
+ ruby/target/release/ruby-extractor
+ ruby/target/release/ruby-extractor.exe
+ ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
+ key: ${{ runner.os }}-ruby-extractor-${{ hashFiles('ruby/rust-toolchain.toml', 'ruby/**/Cargo.lock') }}--${{ hashFiles('ruby/**/*.rs') }}
- uses: actions/cache@v3
+ if: steps.cache-extractor.outputs.cache-hit != 'true'
with:
path: |
~/.cargo/registry
@@ -56,15 +68,19 @@ jobs:
ruby/target
key: ${{ runner.os }}-ruby-rust-cargo-${{ hashFiles('ruby/rust-toolchain.toml', 'ruby/**/Cargo.lock') }}
- name: Check formatting
+ if: steps.cache-extractor.outputs.cache-hit != 'true'
run: cargo fmt --all -- --check
- name: Build
+ if: steps.cache-extractor.outputs.cache-hit != 'true'
run: cargo build --verbose
- name: Run tests
+ if: steps.cache-extractor.outputs.cache-hit != 'true'
run: cargo test --verbose
- name: Release build
+ if: steps.cache-extractor.outputs.cache-hit != 'true'
run: cargo build --release
- name: Generate dbscheme
- if: ${{ matrix.os == 'ubuntu-latest' }}
+ if: ${{ matrix.os == 'ubuntu-latest' && steps.cache-extractor.outputs.cache-hit != 'true'}}
run: target/release/ruby-generator --dbscheme ql/lib/ruby.dbscheme --library ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
- uses: actions/upload-artifact@v3
if: ${{ matrix.os == 'ubuntu-latest' }}
@@ -86,19 +102,24 @@ jobs:
ruby/target/release/ruby-extractor.exe
retention-days: 1
compile-queries:
- runs-on: ubuntu-latest
- env:
- CODEQL_THREADS: 4 # TODO: remove this once it's set by the CLI
+ runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v3
- name: Fetch CodeQL
uses: ./.github/actions/fetch-codeql
+ - name: Cache compilation cache
+ id: query-cache
+ uses: ./.github/actions/cache-query-compilation
+ with:
+ key: ruby-build
- name: Build Query Pack
run: |
+ rm -rf target/packs
codeql pack create ../shared/ssa --output target/packs
codeql pack create ../misc/suite-helpers --output target/packs
+ codeql pack create ../shared/regex --output target/packs
codeql pack create ql/lib --output target/packs
- codeql pack create ql/src --output target/packs
+ codeql pack create -j0 ql/src --output target/packs --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
PACK_FOLDER=$(readlink -f target/packs/codeql/ruby-queries/*)
codeql generate query-help --format=sarifv2.1.0 --output="${PACK_FOLDER}/rules.sarif" ql/src
(cd ql/src; find queries \( -name '*.qhelp' -o -name '*.rb' -o -name '*.erb' \) -exec bash -c 'mkdir -p "'"${PACK_FOLDER}"'/$(dirname "{}")"' \; -exec cp "{}" "${PACK_FOLDER}/{}" \;)
diff --git a/.github/workflows/ruby-qltest.yml b/.github/workflows/ruby-qltest.yml
index 97235b722ba..370375cea93 100644
--- a/.github/workflows/ruby-qltest.yml
+++ b/.github/workflows/ruby-qltest.yml
@@ -4,7 +4,7 @@ on:
push:
paths:
- "ruby/**"
- - .github/workflows/ruby-qltest.yml
+ - .github/workflows/ruby-build.yml
- .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
branches:
@@ -28,23 +28,6 @@ defaults:
working-directory: ruby
jobs:
- qlformat:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
- - uses: ./.github/actions/fetch-codeql
- - name: Check QL formatting
- run: find ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only
- qlcompile:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
- - uses: ./.github/actions/fetch-codeql
- - name: Check QL compilation
- run: |
- codeql query compile --check-only --threads=0 --ram 5000 --warnings=error "ql/src" "ql/examples"
- env:
- GITHUB_TOKEN: ${{ github.token }}
qlupgrade:
runs-on: ubuntu-latest
steps:
@@ -65,17 +48,20 @@ jobs:
xargs codeql execute upgrades testdb
diff -q testdb/ruby.dbscheme downgrades/initial/ruby.dbscheme
qltest:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-latest-xl
strategy:
fail-fast: false
- matrix:
- slice: ["1/2", "2/2"]
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/fetch-codeql
- uses: ./ruby/actions/create-extractor-pack
+ - name: Cache compilation cache
+ id: query-cache
+ uses: ./.github/actions/cache-query-compilation
+ with:
+ key: ruby-qltest
- name: Run QL tests
run: |
- codeql test run --threads=0 --ram 5000 --slice ${{ matrix.slice }} --search-path "${{ github.workspace }}/ruby/extractor-pack" --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --consistency-queries ql/consistency-queries ql/test
+ codeql test run --threads=0 --ram 50000 --search-path "${{ github.workspace }}/ruby/extractor-pack" --check-databases --check-undefined-labels --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --consistency-queries ql/consistency-queries ql/test --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
env:
GITHUB_TOKEN: ${{ github.token }}
diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml
index 873b94d2118..de0f11f0521 100644
--- a/.github/workflows/swift.yml
+++ b/.github/workflows/swift.yml
@@ -7,95 +7,77 @@ on:
- "misc/bazel/**"
- "*.bazel*"
- .github/workflows/swift.yml
- - .github/actions/fetch-codeql/action.yml
+ - .github/actions/**
- codeql-workspace.yml
- .pre-commit-config.yaml
- "!**/*.md"
- "!**/*.qhelp"
branches:
- main
+ - rc/*
+ push:
+ paths:
+ - "swift/**"
+ - "misc/bazel/**"
+ - "*.bazel*"
+ - .github/workflows/swift.yml
+ - .github/actions/**
+ - codeql-workspace.yml
+ - "!**/*.md"
+ - "!**/*.qhelp"
+ branches:
+ - main
+ - rc/*
jobs:
- changes:
- runs-on: ubuntu-latest
- outputs:
- codegen: ${{ steps.filter.outputs.codegen }}
- ql: ${{ steps.filter.outputs.ql }}
- steps:
- - uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50
- id: filter
- with:
- filters: |
- codegen:
- - 'github/workflows/swift.yml'
- - "misc/bazel/**"
- - "*.bazel*"
- - 'swift/actions/setup-env/**'
- - '.pre-commit-config.yaml'
- - 'swift/codegen/**'
- - 'swift/schema.py'
- - 'swift/**/*.dbscheme'
- - 'swift/ql/lib/codeql/swift/elements.qll'
- - 'swift/ql/lib/codeql/swift/elements/**'
- - 'swift/ql/lib/codeql/swift/generated/**'
- - 'swift/ql/test/extractor-tests/generated/**'
- ql:
- - 'github/workflows/swift.yml'
- - 'swift/**/*.ql'
- - 'swift/**/*.qll'
# not using a matrix as you cannot depend on a specific job in a matrix, and we want to start linux checks
# without waiting for the macOS build
build-and-test-macos:
runs-on: macos-12-xl
steps:
- uses: actions/checkout@v3
- - uses: ./swift/actions/create-extractor-pack
- - uses: ./swift/actions/run-quick-tests
- - uses: ./swift/actions/print-unextracted
+ - uses: ./swift/actions/build-and-test
build-and-test-linux:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v3
- - uses: ./swift/actions/create-extractor-pack
- - uses: ./swift/actions/run-quick-tests
- - uses: ./swift/actions/print-unextracted
+ - uses: ./swift/actions/build-and-test
qltests-linux:
needs: build-and-test-linux
- runs-on: ubuntu-latest
+ runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v3
- uses: ./swift/actions/run-ql-tests
qltests-macos:
+ if : ${{ github.event_name == 'pull_request' }}
needs: build-and-test-macos
runs-on: macos-12-xl
- strategy:
- fail-fast: false
- matrix:
- slice: ["1/2", "2/2"]
steps:
- uses: actions/checkout@v3
- uses: ./swift/actions/run-ql-tests
- with:
- flags: --slice ${{ matrix.slice }}
integration-tests-linux:
needs: build-and-test-linux
- runs-on: ubuntu-latest
+ runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v3
- uses: ./swift/actions/run-integration-tests
integration-tests-macos:
+ if : ${{ github.event_name == 'pull_request' }}
needs: build-and-test-macos
runs-on: macos-12-xl
+ timeout-minutes: 60
steps:
- uses: actions/checkout@v3
- uses: ./swift/actions/run-integration-tests
codegen:
+ if : ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
- needs: changes
- if: ${{ needs.changes.outputs.codegen == 'true' }}
steps:
- uses: actions/checkout@v3
- - uses: ./swift/actions/setup-env
+ - uses: bazelbuild/setup-bazelisk@v2
+ - uses: actions/setup-python@v4
+ with:
+ python-version-file: 'swift/.python-version'
- uses: pre-commit/action@v3.0.0
name: Check that python code is properly formatted
with:
@@ -111,13 +93,11 @@ jobs:
- uses: actions/upload-artifact@v3
with:
name: swift-generated-cpp-files
- path: swift/generated-cpp-files/**
- qlformat:
+ path: generated-cpp-files/**
+ database-upgrade-scripts:
+ if : ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
- needs: changes
- if: ${{ needs.changes.outputs.ql == 'true' }}
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/fetch-codeql
- - name: Check QL formatting
- run: find swift/ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only
+ - uses: ./swift/actions/database-upgrade-scripts
diff --git a/.gitignore b/.gitignore
index 7b8532b00d2..c81e23fc7f8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,8 +27,6 @@
# It's useful (though not required) to be able to unpack codeql in the ql checkout itself
/codeql/
-csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json
-
# Avoid committing cached package components
.codeql
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 50a6adf80d9..5f35c2c183b 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -19,7 +19,7 @@ repos:
rev: v1.6.0
hooks:
- id: autopep8
- files: ^swift/codegen/.*\.py
+ files: ^swift/.*\.py
- repo: local
hooks:
@@ -44,7 +44,7 @@ repos:
- id: swift-codegen
name: Run Swift checked in code generation
- files: ^swift/(codegen/|.*/generated/|ql/lib/(swift\.dbscheme$|codeql/swift/elements))
+ files: ^swift/(schema.py$|codegen/|.*/generated/|ql/lib/(swift\.dbscheme$|codeql/swift/elements)|ql/\.generated.list)
language: system
entry: bazel run //swift/codegen -- --quiet
pass_filenames: false
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 8b22c91bb77..1050c79b825 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,3 +1,5 @@
{
- "omnisharp.autoStart": false
+ "omnisharp.autoStart": false,
+ "cmake.sourceDirectory": "${workspaceFolder}/swift",
+ "cmake.buildDirectory": "${workspaceFolder}/bazel-cmake-build"
}
diff --git a/CODEOWNERS b/CODEOWNERS
index a18b6a51305..42fb364418f 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -5,20 +5,13 @@
/javascript/ @github/codeql-javascript
/python/ @github/codeql-python
/ruby/ @github/codeql-ruby
-/swift/ @github/codeql-c
+/swift/ @github/codeql-swift
/java/kotlin-extractor/ @github/codeql-kotlin
/java/kotlin-explorer/ @github/codeql-kotlin
# ML-powered queries
/javascript/ql/experimental/adaptivethreatmodeling/ @github/codeql-ml-powered-queries-reviewers
-# Notify members of codeql-go about PRs to the shared data-flow library files
-/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @github/codeql-java @github/codeql-go
-/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @github/codeql-java @github/codeql-go
-/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @github/codeql-java @github/codeql-go
-/java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go
-/java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go
-
# CodeQL tools and associated docs
/docs/codeql/codeql-cli/ @github/codeql-cli-reviewers
/docs/codeql/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers
@@ -45,4 +38,4 @@ WORKSPACE.bazel @github/codeql-ci-reviewers
/.github/workflows/js-ml-tests.yml @github/codeql-ml-powered-queries-reviewers
/.github/workflows/ql-for-ql-* @github/codeql-ql-for-ql-reviewers
/.github/workflows/ruby-* @github/codeql-ruby
-/.github/workflows/swift-* @github/codeql-c
+/.github/workflows/swift.yml @github/codeql-swift
diff --git a/codeql-workspace.yml b/codeql-workspace.yml
index f93ed4ac5c8..c2258bd1363 100644
--- a/codeql-workspace.yml
+++ b/codeql-workspace.yml
@@ -25,7 +25,8 @@ provide:
- "misc/suite-helpers/qlpack.yml"
- "ruby/extractor-pack/codeql-extractor.yml"
- "swift/extractor-pack/codeql-extractor.yml"
- - "ql/extractor-pack/codeql-extractor.ym"
+ - "swift/integration-tests/qlpack.yml"
+ - "ql/extractor-pack/codeql-extractor.yml"
versionPolicies:
default:
diff --git a/config/identical-files.json b/config/identical-files.json
index f5ad906f340..afef0a923d5 100644
--- a/config/identical-files.json
+++ b/config/identical-files.json
@@ -1,5 +1,5 @@
{
- "DataFlow Java/C++/C#/Python": [
+ "DataFlow Java/C++/C#/Go/Python/Ruby/Swift": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll",
@@ -27,6 +27,8 @@
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll",
+ "go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll",
+ "go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll",
@@ -38,17 +40,18 @@
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll"
],
- "DataFlow Java/C++/C#/Python Common": [
+ "DataFlow Java/C++/C#/Go/Python/Ruby/Swift Common": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll",
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll",
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll",
+ "go/ql/lib/semmle/go/dataflow/internal/DataFlowImplCommon.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplCommon.qll"
],
- "TaintTracking::Configuration Java/C++/C#/Python": [
+ "TaintTracking::Configuration Java/C++/C#/Go/Python/Ruby/Swift": [
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
@@ -62,6 +65,8 @@
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll",
+ "go/ql/lib/semmle/go/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
+ "go/ql/lib/semmle/go/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
@@ -72,7 +77,7 @@
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/tainttracking1/TaintTrackingImpl.qll"
],
- "DataFlow Java/C++/C#/Python Consistency checks": [
+ "DataFlow Java/C++/C#/Python/Ruby/Swift Consistency checks": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll",
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll",
@@ -82,9 +87,10 @@
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplConsistency.qll"
],
- "DataFlow Java/C#/Ruby/Python/Swift Flow Summaries": [
+ "DataFlow Java/C#/Go/Ruby/Python/Swift Flow Summaries": [
"java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll",
+ "go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll"
@@ -464,6 +470,10 @@
"javascript/ql/src/Comments/CommentedOutCodeReferences.inc.qhelp",
"python/ql/src/Lexical/CommentedOutCodeReferences.inc.qhelp"
],
+ "ThreadResourceAbuse qhelp": [
+ "java/ql/src/experimental/Security/CWE/CWE-400/LocalThreadResourceAbuse.qhelp",
+ "java/ql/src/experimental/Security/CWE/CWE-400/ThreadResourceAbuse.qhelp"
+ ],
"IDE Contextual Queries": [
"cpp/ql/lib/IDEContextual.qll",
"csharp/ql/lib/IDEContextual.qll",
@@ -486,40 +496,6 @@
"python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll",
"ruby/ql/lib/codeql/ruby/security/internal/SensitiveDataHeuristics.qll"
],
- "ReDoS Util Python/JS/Ruby/Java": [
- "javascript/ql/lib/semmle/javascript/security/regexp/NfaUtils.qll",
- "python/ql/lib/semmle/python/security/regexp/NfaUtils.qll",
- "ruby/ql/lib/codeql/ruby/security/regexp/NfaUtils.qll",
- "java/ql/lib/semmle/code/java/security/regexp/NfaUtils.qll"
- ],
- "ReDoS Exponential Python/JS/Ruby/Java": [
- "javascript/ql/lib/semmle/javascript/security/regexp/ExponentialBackTracking.qll",
- "python/ql/lib/semmle/python/security/regexp/ExponentialBackTracking.qll",
- "ruby/ql/lib/codeql/ruby/security/regexp/ExponentialBackTracking.qll",
- "java/ql/lib/semmle/code/java/security/regexp/ExponentialBackTracking.qll"
- ],
- "ReDoS Polynomial Python/JS/Ruby/Java": [
- "javascript/ql/lib/semmle/javascript/security/regexp/SuperlinearBackTracking.qll",
- "python/ql/lib/semmle/python/security/regexp/SuperlinearBackTracking.qll",
- "ruby/ql/lib/codeql/ruby/security/regexp/SuperlinearBackTracking.qll",
- "java/ql/lib/semmle/code/java/security/regexp/SuperlinearBackTracking.qll"
- ],
- "RegexpMatching Python/JS/Ruby": [
- "javascript/ql/lib/semmle/javascript/security/regexp/RegexpMatching.qll",
- "python/ql/lib/semmle/python/security/regexp/RegexpMatching.qll",
- "ruby/ql/lib/codeql/ruby/security/regexp/RegexpMatching.qll"
- ],
- "BadTagFilterQuery Python/JS/Ruby": [
- "javascript/ql/lib/semmle/javascript/security/BadTagFilterQuery.qll",
- "python/ql/lib/semmle/python/security/BadTagFilterQuery.qll",
- "ruby/ql/lib/codeql/ruby/security/BadTagFilterQuery.qll"
- ],
- "OverlyLargeRange Python/JS/Ruby/Java": [
- "javascript/ql/lib/semmle/javascript/security/OverlyLargeRangeQuery.qll",
- "python/ql/lib/semmle/python/security/OverlyLargeRangeQuery.qll",
- "ruby/ql/lib/codeql/ruby/security/OverlyLargeRangeQuery.qll",
- "java/ql/lib/semmle/code/java/security/OverlyLargeRangeQuery.qll"
- ],
"CFG": [
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll",
"ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll",
@@ -539,6 +515,7 @@
],
"AccessPathSyntax": [
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/AccessPathSyntax.qll",
+ "go/ql/lib/semmle/go/dataflow/internal/AccessPathSyntax.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/AccessPathSyntax.qll",
"javascript/ql/lib/semmle/javascript/frameworks/data/internal/AccessPathSyntax.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/AccessPathSyntax.qll",
@@ -554,16 +531,16 @@
"ruby/ql/lib/codeql/ruby/internal/ConceptsShared.qll",
"javascript/ql/lib/semmle/javascript/internal/ConceptsShared.qll"
],
- "Hostname Regexp queries": [
- "javascript/ql/src/Security/CWE-020/HostnameRegexpShared.qll",
- "python/ql/src/Security/CWE-020/HostnameRegexpShared.qll",
- "ruby/ql/src/queries/security/cwe-020/HostnameRegexpShared.qll"
- ],
"ApiGraphModels": [
"javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll",
"ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll",
"python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll"
],
+ "ApiGraphModelsExtensions": [
+ "javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsExtensions.qll",
+ "ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsExtensions.qll",
+ "python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsExtensions.qll"
+ ],
"TaintedFormatStringQuery Ruby/JS": [
"javascript/ql/lib/semmle/javascript/security/dataflow/TaintedFormatStringQuery.qll",
"ruby/ql/lib/codeql/ruby/security/TaintedFormatStringQuery.qll"
@@ -607,5 +584,9 @@
"IncompleteMultiCharacterSanitization JS/Ruby": [
"javascript/ql/lib/semmle/javascript/security/IncompleteMultiCharacterSanitizationQuery.qll",
"ruby/ql/lib/codeql/ruby/security/IncompleteMultiCharacterSanitizationQuery.qll"
+ ],
+ "EncryptionKeySizes Python/Java": [
+ "python/ql/lib/semmle/python/security/internal/EncryptionKeySizes.qll",
+ "java/ql/lib/semmle/code/java/security/internal/EncryptionKeySizes.qll"
]
}
diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs b/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs
index 76acba2eee4..def45890c9f 100644
--- a/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs
+++ b/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs
@@ -257,11 +257,11 @@ namespace Semmle.Autobuild.Cpp.Tests
Actions.GetCurrentDirectory = cwd;
Actions.IsWindows = isWindows;
- var options = new AutobuildOptions(Actions, Language.Cpp);
+ var options = new CppAutobuildOptions(Actions);
return new CppAutobuilder(Actions, options);
}
- void TestAutobuilderScript(Autobuilder autobuilder, int expectedOutput, int commandsRun)
+ void TestAutobuilderScript(CppAutobuilder autobuilder, int expectedOutput, int commandsRun)
{
Assert.Equal(expectedOutput, autobuilder.GetBuildScript().Run(Actions, StartCallback, EndCallback));
@@ -299,7 +299,7 @@ namespace Semmle.Autobuild.Cpp.Tests
{
Actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test.sln -DisableParallelProcessing"] = 1;
Actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test.sln -DisableParallelProcessing"] = 0;
- Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program Files ^(x86^)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && C:\odasa\tools\odasa index --auto msbuild C:\Project\test.sln /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"" /p:MvcBuildViews=true"] = 0;
+ Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program Files ^(x86^)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && msbuild C:\Project\test.sln /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"""] = 0;
Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = "";
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 1;
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = 0;
diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/Semmle.Autobuild.Cpp.Tests.csproj b/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/Semmle.Autobuild.Cpp.Tests.csproj
index dc1d945521b..421ffdd96f4 100644
--- a/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/Semmle.Autobuild.Cpp.Tests.csproj
+++ b/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/Semmle.Autobuild.Cpp.Tests.csproj
@@ -11,11 +11,12 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers
+
diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp/CppAutobuilder.cs b/cpp/autobuilder/Semmle.Autobuild.Cpp/CppAutobuilder.cs
index 44c34656a2a..1503dedb376 100644
--- a/cpp/autobuilder/Semmle.Autobuild.Cpp/CppAutobuilder.cs
+++ b/cpp/autobuilder/Semmle.Autobuild.Cpp/CppAutobuilder.cs
@@ -2,9 +2,26 @@
namespace Semmle.Autobuild.Cpp
{
- public class CppAutobuilder : Autobuilder
+ ///
+ /// Encapsulates C++ build options.
+ ///
+ public class CppAutobuildOptions : AutobuildOptionsShared
{
- public CppAutobuilder(IBuildActions actions, AutobuildOptions options) : base(actions, options) { }
+ public override Language Language => Language.Cpp;
+
+
+ ///
+ /// Reads options from environment variables.
+ /// Throws ArgumentOutOfRangeException for invalid arguments.
+ ///
+ public CppAutobuildOptions(IBuildActions actions) : base(actions)
+ {
+ }
+ }
+
+ public class CppAutobuilder : Autobuilder
+ {
+ public CppAutobuilder(IBuildActions actions, CppAutobuildOptions options) : base(actions, options) { }
public override BuildScript GetBuildScript()
{
diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp/Program.cs b/cpp/autobuilder/Semmle.Autobuild.Cpp/Program.cs
index 3f4627c53d5..a7556197bcd 100644
--- a/cpp/autobuilder/Semmle.Autobuild.Cpp/Program.cs
+++ b/cpp/autobuilder/Semmle.Autobuild.Cpp/Program.cs
@@ -11,14 +11,14 @@ namespace Semmle.Autobuild.Cpp
try
{
var actions = SystemBuildActions.Instance;
- var options = new AutobuildOptions(actions, Language.Cpp);
+ var options = new CppAutobuildOptions(actions);
try
{
Console.WriteLine("CodeQL C++ autobuilder");
var builder = new CppAutobuilder(actions, options);
return builder.AttemptBuild();
}
- catch(InvalidEnvironmentException ex)
+ catch (InvalidEnvironmentException ex)
{
Console.WriteLine("The environment is invalid: {0}", ex.Message);
}
diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj b/cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj
index e417622707a..393d4141ab9 100644
--- a/cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj
+++ b/cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj
@@ -17,7 +17,7 @@
-
+
diff --git a/cpp/ql/lib/CHANGELOG.md b/cpp/ql/lib/CHANGELOG.md
index 0a7a31b8db9..6fa6f76aabd 100644
--- a/cpp/ql/lib/CHANGELOG.md
+++ b/cpp/ql/lib/CHANGELOG.md
@@ -1,3 +1,15 @@
+## 0.4.6
+
+No user-facing changes.
+
+## 0.4.5
+
+No user-facing changes.
+
+## 0.4.4
+
+No user-facing changes.
+
## 0.4.3
### Minor Analysis Improvements
diff --git a/cpp/ql/lib/change-notes/2022-11-14-deprecate-ast-gvn.md b/cpp/ql/lib/change-notes/2022-11-14-deprecate-ast-gvn.md
new file mode 100644
index 00000000000..eb6bd755c2b
--- /dev/null
+++ b/cpp/ql/lib/change-notes/2022-11-14-deprecate-ast-gvn.md
@@ -0,0 +1,6 @@
+---
+category: deprecated
+---
+
+
+* Deprecated `semmle.code.cpp.valuenumbering.GlobalValueNumberingImpl`. Use `semmle.code.cpp.valuenumbering.GlobalValueNumbering`, which exposes the same API.
\ No newline at end of file
diff --git a/cpp/ql/lib/change-notes/2022-11-16-must-flow.md b/cpp/ql/lib/change-notes/2022-11-16-must-flow.md
new file mode 100644
index 00000000000..0f87b8d8bcd
--- /dev/null
+++ b/cpp/ql/lib/change-notes/2022-11-16-must-flow.md
@@ -0,0 +1,4 @@
+---
+category: breaking
+---
+The predicates in the `MustFlow::Configuration` class used by the `MustFlow` library (`semmle.code.cpp.ir.dataflow.MustFlow`) have changed to be defined directly in terms of the C++ IR instead of IR dataflow nodes.
diff --git a/cpp/ql/lib/change-notes/2022-11-17-deleted-deps.md b/cpp/ql/lib/change-notes/2022-11-17-deleted-deps.md
new file mode 100644
index 00000000000..bf2d5a07de6
--- /dev/null
+++ b/cpp/ql/lib/change-notes/2022-11-17-deleted-deps.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* Deleted the deprecated `getName` and `getShortName` predicates from the `Folder` class.
\ No newline at end of file
diff --git a/cpp/ql/lib/change-notes/2022-11-25-deprecate-default-taint-tracking.md b/cpp/ql/lib/change-notes/2022-11-25-deprecate-default-taint-tracking.md
new file mode 100644
index 00000000000..54d10d592d6
--- /dev/null
+++ b/cpp/ql/lib/change-notes/2022-11-25-deprecate-default-taint-tracking.md
@@ -0,0 +1,6 @@
+---
+category: deprecated
+---
+
+* Deprecated `semmle.code.cpp.ir.dataflow.DefaultTaintTracking`. Use `semmle.code.cpp.ir.dataflow.TaintTracking`.
+* Deprecated `semmle.code.cpp.security.TaintTrackingImpl`. Use `semmle.code.cpp.ir.dataflow.TaintTracking`.
diff --git a/cpp/ql/lib/change-notes/2022-12-08-support-getaddrinfo-dataflow.md b/cpp/ql/lib/change-notes/2022-12-08-support-getaddrinfo-dataflow.md
new file mode 100644
index 00000000000..6d1a6499ee9
--- /dev/null
+++ b/cpp/ql/lib/change-notes/2022-12-08-support-getaddrinfo-dataflow.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* The `getaddrinfo` function is now recognized as a flow source.
diff --git a/cpp/ql/lib/change-notes/2022-12-08-support-getenv-variants.md b/cpp/ql/lib/change-notes/2022-12-08-support-getenv-variants.md
new file mode 100644
index 00000000000..c91d549ec8a
--- /dev/null
+++ b/cpp/ql/lib/change-notes/2022-12-08-support-getenv-variants.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* The `secure_getenv` and `_wgetenv` functions are now recognized as local flow sources.
diff --git a/cpp/ql/lib/change-notes/2022-12-08-support-scanf-dataflow.md b/cpp/ql/lib/change-notes/2022-12-08-support-scanf-dataflow.md
new file mode 100644
index 00000000000..f8382f84c0f
--- /dev/null
+++ b/cpp/ql/lib/change-notes/2022-12-08-support-scanf-dataflow.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* The `scanf` and `fscanf` functions and their variants are now recognized as flow sources.
diff --git a/cpp/ql/lib/change-notes/2022-12-09-generalize-argv-source.md b/cpp/ql/lib/change-notes/2022-12-09-generalize-argv-source.md
new file mode 100644
index 00000000000..76fda20ec61
--- /dev/null
+++ b/cpp/ql/lib/change-notes/2022-12-09-generalize-argv-source.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* The `ArgvSource` flow source has been generalized to handle cases where the argument vector of `main` is not named `argv`.
diff --git a/cpp/ql/lib/change-notes/released/0.4.4.md b/cpp/ql/lib/change-notes/released/0.4.4.md
new file mode 100644
index 00000000000..33e1c91255d
--- /dev/null
+++ b/cpp/ql/lib/change-notes/released/0.4.4.md
@@ -0,0 +1,3 @@
+## 0.4.4
+
+No user-facing changes.
diff --git a/cpp/ql/lib/change-notes/released/0.4.5.md b/cpp/ql/lib/change-notes/released/0.4.5.md
new file mode 100644
index 00000000000..7ba9b2e8ade
--- /dev/null
+++ b/cpp/ql/lib/change-notes/released/0.4.5.md
@@ -0,0 +1,3 @@
+## 0.4.5
+
+No user-facing changes.
diff --git a/cpp/ql/lib/change-notes/released/0.4.6.md b/cpp/ql/lib/change-notes/released/0.4.6.md
new file mode 100644
index 00000000000..8e652998eca
--- /dev/null
+++ b/cpp/ql/lib/change-notes/released/0.4.6.md
@@ -0,0 +1,3 @@
+## 0.4.6
+
+No user-facing changes.
diff --git a/cpp/ql/lib/codeql-pack.release.yml b/cpp/ql/lib/codeql-pack.release.yml
index 1ec9c4ea5d9..2b842473675 100644
--- a/cpp/ql/lib/codeql-pack.release.yml
+++ b/cpp/ql/lib/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.4.3
+lastReleaseVersion: 0.4.6
diff --git a/cpp/ql/lib/definitions.qll b/cpp/ql/lib/definitions.qll
index cb229d66ef1..e4a2aca98f6 100644
--- a/cpp/ql/lib/definitions.qll
+++ b/cpp/ql/lib/definitions.qll
@@ -12,8 +12,8 @@ import IDEContextual
*
* In some cases it is preferable to modify locations (the
* `hasLocationInfo()` predicate) so that they are short, and
- * non-overlapping with other locations that might be highlighted in
- * the LGTM interface.
+ * non-overlapping with other locations that might be reported as
+ * code scanning alerts on GitHub.
*
* We need to give locations that may not be in the database, so
* we use `hasLocationInfo()` rather than `getLocation()`.
diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
index 2dde1c2819d..1228d00b6ba 100644
--- a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
+++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
- predicate isSink(Node source, FlowState state) { none() }
+ predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/
FlowFeature getAFeature() { none() }
+ /** Holds if sources should be grouped in the result of `hasFlowPath`. */
+ predicate sourceGrouping(Node source, string sourceGroup) { none() }
+
+ /** Holds if sinks should be grouped in the result of `hasFlowPath`. */
+ predicate sinkGrouping(Node sink, string sinkGroup) { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
* The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`.
*/
- predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
+ predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
/**
* Holds if data may flow from some source to `sink` for this configuration.
@@ -313,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -602,8 +606,27 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
+/**
+ * Holds if flow from `p` to a return node of kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[p, kind]
+private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
+ exists(ParameterPosition pos | p.isParameterOf(_, pos) |
+ not kind.(ParamUpdateReturnKind).getPosition() = pos
+ or
+ allowParameterReturnInSelfCached(p.asNode())
+ )
+}
+
private module Stage1 implements StageSig {
- class Ap = Unit;
+ class Ap extends int {
+ // workaround for bad functionality-induced joins (happens when using `Unit`)
+ pragma[nomagic]
+ Ap() { this in [0 .. 1] and this < 1 }
+ }
private class Cc = boolean;
@@ -944,6 +967,12 @@ private module Stage1 implements StageSig {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
+ pragma[nomagic]
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
+ revFlow(node, config) and
+ exists(ap)
+ }
+
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -975,21 +1004,26 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnKindExt kind |
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
+ exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
- or
- p.allowParameterReturnInSelf()
- )
+ parameterFlowThroughAllowed(p, kind)
)
}
+ pragma[nomagic]
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ ) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind() and
+ exists(argAp) and
+ exists(ap)
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1046,12 +1080,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
- viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
- Stage1::revFlow(ret, config) and
- not outBarrier(ret, config) and
- not inBarrier(out, config)
+ exists(ReturnPosition pos |
+ viableReturnPosOutNodeCand1(call, pos, out, config) and
+ pos = ret.getReturnPosition() and
+ kind = pos.getKind() and
+ Stage1::revFlow(ret, config) and
+ not outBarrier(ret, config) and
+ not inBarrier(out, config)
+ )
}
pragma[nomagic]
@@ -1081,10 +1119,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
+pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
- flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
+ flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1093,10 +1132,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
+pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
- flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
+ flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1109,12 +1149,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
) {
- flowOutOfCallNodeCand1(call, ret, out, config) and
+ flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
- b = branch(ret, config) and
- j = join(out, config) and
+ b = branch(ret, pragma[only_bind_into](config)) and
+ j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1130,10 +1171,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- flowIntoCallNodeCand1(call, arg, p, config) and
+ flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
- b = branch(arg, config) and
- j = join(p, config) and
+ b = branch(arg, pragma[only_bind_into](config)) and
+ j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1145,12 +1186,18 @@ private signature module StageSig {
predicate revFlow(NodeEx node, Configuration config);
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
+
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
+
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ );
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1216,7 +1263,8 @@ private module MkStage {
);
predicate flowOutOfCall(
- DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
);
predicate flowIntoCall(
@@ -1234,21 +1282,43 @@ private module MkStage {
import Param
/* Begin: Stage logic. */
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
+ PrevStage::revFlowAp(node, apa, config)
+ }
+
+ pragma[nomagic]
+ private predicate flowIntoCallApa(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
+ Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate flowOutOfCallApa(
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ ApApprox apa, Configuration config
+ ) {
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
+ PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ ApApprox argApa, ApApprox apa, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- matchesCall(ccc, call)
+ exists(ReturnKindExt kind |
+ flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ )
}
/**
@@ -1256,99 +1326,134 @@ private module MkStage {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
- * argument in a call, and if so, `argAp` records the access path of that
- * argument.
+ * argument in a call, and if so, `summaryCtx` and `argAp` record the
+ * corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
- NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ ApApprox apa, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
+ PrevStage::revFlow(node, state, apa, config) and
filter(node, state, ap, config)
}
+ pragma[inline]
+ additional predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ Configuration config
+ ) {
+ fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
+ }
+
pragma[nomagic]
private predicate fwdFlow0(
- NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ ApApprox apa, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
- ap = getApNil(node)
+ summaryCtx = TParamNodeNone() and
+ ap = getApNil(node) and
+ apa = getApprox(ap)
or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
+ ap = ap0 and
+ apa = apa0
or
localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
+ ap0 instanceof ApNil and
+ apa = getApprox(ap)
)
or
exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone() and
- ap = getApNil(node)
+ ap = getApNil(node) and
+ apa = getApprox(ap)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
+ fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone() and
- ap = getApNil(node)
+ ap = getApNil(node) and
+ apa = getApprox(ap)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
+ ap = apCons(tc, ap0) and
+ apa = getApprox(ap)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config) and
+ apa = getApprox(ap)
)
or
// flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParamNodeSome(node.asNode()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParamNodeNone() and argAp = apNone()
)
or
// flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
+ flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
+ inner = ret.getEnclosingCallable() and
+ cc = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ // flow through a callable
+ exists(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, boolean allowsFieldFlow, ApApprox innerArgApa
+ |
+ fwdFlowThrough(call, cc, state, ccc, summaryCtx, argAp, ap, apa, ret, innerArgApa, config) and
+ flowThroughOutOfCall(call, ccc, ret, node, allowsFieldFlow, innerArgApa, apa, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
+ ParamNodeOption summaryCtx, ApOption argAp, Configuration config
) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ exists(DataFlowType contentType, ApApprox apa1 |
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
+ PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1360,60 +1465,85 @@ private module MkStage {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
}
+ private class ApNonNil instanceof Ap {
+ pragma[nomagic]
+ ApNonNil() { not this instanceof ApNil }
+
+ string toString() { result = "" }
+ }
+
pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ private predicate fwdFlowRead0(
+ NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
Configuration config
) {
- fwdFlow(node1, state, cc, argAp, ap, config) and
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
+ PrevStage::readStepCand(node1, _, _, config)
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
+ ParamNodeOption summaryCtx, ApOption argAp, Configuration config
+ ) {
+ fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
- Ap ap, Configuration config
+ DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
+ ParamNodeOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, argAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
+ flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowRetFromArg(
+ RetNodeEx ret, FlowState state, CcCall ccc, ParamNodeEx summaryCtx, Ap argAp, ApApprox argApa,
+ Ap ap, ApApprox apa, Configuration config
) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
+ exists(ReturnKindExt kind |
+ fwdFlow(pragma[only_bind_into](ret), state, ccc,
+ TParamNodeSome(pragma[only_bind_into](summaryCtx.asNode())),
+ pragma[only_bind_into](apSome(argAp)), ap, pragma[only_bind_into](apa),
+ pragma[only_bind_into](config)) and
+ kind = ret.getKind() and
+ parameterFlowThroughAllowed(summaryCtx, kind) and
+ argApa = getApprox(argAp) and
+ PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config))
)
}
- pragma[nomagic]
- private predicate fwdFlowOutFromArg(
- DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
+ pragma[inline]
+ private predicate fwdFlowThrough0(
+ DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx,
+ ApOption argAp, Ap ap, ApApprox apa, RetNodeEx ret, ParamNodeEx innerSummaryCtx,
+ Ap innerArgAp, ApApprox innerArgApa, Configuration config
) {
- exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
- fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
- flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ fwdFlowRetFromArg(ret, state, ccc, innerSummaryCtx, innerArgAp, innerArgApa, ap, apa, config) and
+ fwdFlowIsEntered(call, cc, ccc, summaryCtx, argAp, innerSummaryCtx, innerArgAp, config)
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowThrough(
+ DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx,
+ ApOption argAp, Ap ap, ApApprox apa, RetNodeEx ret, ApApprox innerArgApa, Configuration config
+ ) {
+ fwdFlowThrough0(call, cc, state, ccc, summaryCtx, argAp, ap, apa, ret, _, _, innerArgApa,
+ config)
}
/**
@@ -1422,11 +1552,14 @@ private module MkStage {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ DataFlowCall call, Cc cc, CcCall innerCc, ParamNodeOption summaryCtx, ApOption argAp,
+ ParamNodeEx p, Ap ap, Configuration config
) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ exists(ApApprox apa |
+ fwdFlowIn(call, pragma[only_bind_into](p), _, cc, innerCc, summaryCtx, argAp, ap,
+ pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, apa, config) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config))
)
}
@@ -1434,146 +1567,194 @@ private module MkStage {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
- fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
+ fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
- fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
+ fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
+ private predicate returnFlowsThrough0(
+ DataFlowCall call, FlowState state, CcCall ccc, Ap ap, ApApprox apa, RetNodeEx ret,
+ ParamNodeEx innerSummaryCtx, Ap innerArgAp, ApApprox innerArgApa, Configuration config
+ ) {
+ fwdFlowThrough0(call, _, state, ccc, _, _, ap, apa, ret, innerSummaryCtx, innerArgAp,
+ innerArgApa, config)
+ }
+
+ pragma[nomagic]
+ private predicate returnFlowsThrough(
+ RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
+ Ap ap, Configuration config
+ ) {
+ exists(DataFlowCall call, ApApprox apa, boolean allowsFieldFlow, ApApprox innerArgApa |
+ returnFlowsThrough0(call, state, ccc, ap, apa, ret, p, argAp, innerArgApa, config) and
+ flowThroughOutOfCall(call, ccc, ret, _, allowsFieldFlow, innerArgApa, apa, config) and
+ pos = ret.getReturnPosition() and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp, Ap ap,
+ 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))
+ exists(ApApprox argApa |
+ flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
+ allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
+ pragma[only_bind_into](config)) and
+ returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ if allowsFieldFlow = false then argAp instanceof ApNil else any()
+ )
}
pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ private predicate flowIntoCallAp(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
+ Configuration config
) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
+ exists(ApApprox apa |
+ flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
+ fwdFlow(arg, _, _, _, _, ap, apa, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowOutOfCallAp(
+ DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow,
+ Ap ap, Configuration config
+ ) {
+ exists(ApApprox apa |
+ flowOutOfCallApa(call, ret, _, out, allowsFieldFlow, apa, config) and
+ fwdFlow(ret, _, _, _, _, ap, apa, config) and
+ pos = ret.getReturnPosition()
+ )
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
- * The Boolean `toReturn` records whether the node must be returned from the
- * enclosing callable in order to reach a sink, and if so, `returnAp` records
- * the access path of the returned value.
+ * The parameter `returnCtx` records whether (and how) the node must be returned
+ * from the enclosing callable in order to reach a sink, and if so, `returnAp`
+ * records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
+ Configuration config
) {
- revFlow0(node, state, toReturn, returnAp, ap, config) and
- fwdFlow(node, state, _, _, ap, config)
+ revFlow0(node, state, returnCtx, returnAp, ap, config) and
+ fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
+ Configuration config
) {
- fwdFlow(node, state, _, _, ap, config) and
+ fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
+ (
+ if hasSinkCallCtx(config)
+ then returnCtx = TReturnCtxNoFlowThrough()
+ else returnCtx = TReturnCtxNone()
+ ) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
- revFlow(mid, state0, toReturn, returnAp, ap, config)
+ revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
- revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
+ revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
- fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
+ flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ returnCtx = TReturnCtxNone()
+ )
or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ // flow through a callable
+ exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
+ revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
+ flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
)
or
// flow out of a callable
- revFlowOut(_, node, state, _, _, ap, config) and
- toReturn = true and
- if returnNodeMayFlowThrough(node, state, ap, config)
- then returnAp = apSome(ap)
- else returnAp = apNone()
+ exists(ReturnPosition pos |
+ revFlowOut(_, node, pos, state, _, _, ap, config) and
+ if returnFlowsThrough(node, pos, state, _, _, _, ap, config)
+ then (
+ returnCtx = TReturnCtxMaybeFlowThrough(pos) and
+ returnAp = apSome(ap)
+ ) else (
+ returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
+ )
+ )
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
- boolean toReturn, ApOption returnAp, Configuration config
+ ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1593,36 +1774,33 @@ private module MkStage {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx,
+ ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
- revFlow(out, state, toReturn, returnAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ revFlow(out, state, returnCtx, returnAp, ap, config) and
+ flowOutOfCallAp(call, ret, pos, out, allowsFieldFlow, ap, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate revFlowInNotToReturn(
- ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
+ private predicate revFlowParamToReturn(
+ ParamNodeEx p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap, Configuration config
) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, false, returnAp, ap, config) and
- flowIntoCall(_, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp),
+ pragma[only_bind_into](ap), pragma[only_bind_into](config)) and
+ parameterFlowThroughAllowed(p, pos.getKind()) and
+ PrevStage::parameterMayFlowThrough(p, getApprox(ap), config)
}
pragma[nomagic]
- private predicate revFlowInToReturn(
- DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
+ private predicate revFlowThrough(
+ DataFlowCall call, ReturnCtx returnCtx, ParamNodeEx p, FlowState state, ReturnPosition pos,
+ ApOption returnAp, Ap ap, Ap innerReturnAp, Configuration config
) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ revFlowParamToReturn(p, state, pos, innerReturnAp, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, pos, innerReturnAp, config)
}
/**
@@ -1632,11 +1810,12 @@ private module MkStage {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap,
+ Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
- revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
- fwdFlow(ret, state, ccc, apSome(_), ap, config) and
+ revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and
+ returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1673,6 +1852,11 @@ private module MkStage {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+ pragma[nomagic]
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
+ revFlow(node, _, _, _, ap, config)
+ }
+
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) {
@@ -1707,40 +1891,49 @@ private module MkStage {
validAp(ap, config)
}
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ pragma[nomagic]
+ private predicate parameterFlowsThroughRev(
+ ParamNodeEx p, Ap ap, ReturnPosition pos, Ap returnAp, Configuration config
) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap, config) and
+ parameterFlowThroughAllowed(p, pos.getKind())
}
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
- parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
- revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
- pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
- fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
- kind = ret.getKind() and
- p.getPosition() = pos and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = pos
- or
- p.allowParameterReturnInSelf()
- )
+ pragma[nomagic]
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, ReturnPosition pos |
+ returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, pos, _, config)
+ )
+ }
+
+ pragma[nomagic]
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ ) {
+ exists(ParamNodeEx p, ReturnPosition pos |
+ returnFlowsThrough(ret, pos, _, _, p, argAp, ap, config) and
+ parameterFlowsThroughRev(p, argAp, pos, ap, config) and
+ kind = pos.getKind()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowThroughArg(
+ DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
+ Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
+ revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
+ flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
+ revFlow(arg, state, returnCtx, returnAp, ap, config) and
+ revFlowThroughArg(call, arg, state, returnCtx, returnAp, ap, config)
)
}
@@ -1748,13 +1941,13 @@ private module MkStage {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
- nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
+ states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
+ count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)
)
or
fwd = false and
@@ -1763,8 +1956,8 @@ private module MkStage {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
- count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
- revFlow(n, state, b, retAp, ap, config)
+ count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
+ revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1909,7 +2102,7 @@ private module Stage2Param implements MkStage::StageParam {
exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1945,9 +2138,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
- DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
) {
- flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
+ flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2015,8 +2209,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
- flowIntoCallNodeCand1(_, node, next, config) or
- flowOutOfCallNodeCand1(_, node, next, config) or
+ flowIntoCallNodeCand2(_, node, next, _, config) or
+ flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2103,16 +2297,16 @@ private module LocalFlowBigStep {
pragma[nomagic]
predicate localFlowBigStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
- AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
+ DataFlowType t, Configuration config, LocalCallContext callContext
) {
- localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
+ localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
localFlowExit(node2, state1, config) and
state1 = state2
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
state1 != state2 and
preservesValue = false and
- apf = TFrontNil(node2.getDataFlowType()) and
+ t = node2.getDataFlowType() and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
@@ -2126,11 +2320,87 @@ private import LocalFlowBigStep
private module Stage3Param implements MkStage::StageParam {
private module PrevStage = Stage2;
+ class Ap = ApproxAccessPathFront;
+
+ class ApNil = ApproxAccessPathFrontNil;
+
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+
+ ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
+ }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
+
+ pragma[noinline]
+ Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
+
+ class ApOption = ApproxAccessPathFrontOption;
+
+ ApOption apNone() { result = TApproxAccessPathFrontNone() }
+
+ ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
+
+ import BooleanCallContext
+
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
+ ) {
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
+ exists(lcc)
+ }
+
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
+
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
+
+ pragma[nomagic]
+ private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
+ exists(Content c |
+ PrevStage::revFlow(node, pragma[only_bind_into](config)) and
+ PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
+ expectsContentEx(node, c) and
+ c = ap.getAHead().getContent()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ exists(state) and
+ exists(config) and
+ (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
+ (
+ notExpectsContent(node)
+ or
+ expectsContentCand(node, ap, config)
+ )
+ }
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ // We need to typecheck stores here, since reverse flow through a getter
+ // might have a different type here compared to inside the getter.
+ compatibleTypes(ap.getType(), contentType)
+ }
+}
+
+private module Stage3 implements StageSig {
+ import MkStage::Stage
+}
+
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
+
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
@@ -2150,16 +2420,42 @@ private module Stage3Param implements MkStage::StageParam {
import BooleanCallContext
+ pragma[nomagic]
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
- localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
+ PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
+ exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ pragma[nomagic]
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ exists(FlowState state |
+ flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
+ pragma[only_bind_into](config))
+ )
+ }
- predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ pragma[nomagic]
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ exists(FlowState state |
+ flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
+ pragma[only_bind_into](config))
+ )
+ }
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2215,8 +2511,8 @@ private module Stage3Param implements MkStage::StageParam {
}
}
-private module Stage3 implements StageSig {
- import MkStage::Stage
+private module Stage4 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2227,8 +2523,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
- Stage3::revFlow(node, state, true, _, apf, config) and
- Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
+ Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
+ Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
+ config)
)
}
@@ -2238,10 +2535,10 @@ private predicate flowCandSummaryCtx(
*/
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
exists(int tails, int nodes, int apLimit, int tupleLimit |
- tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
+ tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2255,11 +2552,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
private newtype TAccessPathApprox =
TNil(DataFlowType t) or
TConsNil(TypedContent tc, DataFlowType t) {
- Stage3::consCand(tc, TFrontNil(t), _) and
+ Stage4::consCand(tc, TFrontNil(t), _) and
not expensiveLen2unfolding(tc, _)
} or
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
- Stage3::consCand(tc1, TFrontHead(tc2), _) and
+ Stage4::consCand(tc1, TFrontHead(tc2), _) and
len in [2 .. accessPathLimit()] and
not expensiveLen2unfolding(tc1, _)
} or
@@ -2389,7 +2686,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
override AccessPathApprox pop(TypedContent head) {
head = tc and
(
- exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
+ exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
result = TConsCons(tc2, _, len - 1)
or
len = 2 and
@@ -2400,7 +2697,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
or
exists(DataFlowType t |
len = 1 and
- Stage3::consCand(tc, TFrontNil(t), _) and
+ Stage4::consCand(tc, TFrontNil(t), _) and
result = TNil(t)
)
)
@@ -2425,13 +2722,14 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4Param implements MkStage::StageParam {
- private module PrevStage = Stage3;
+private module Stage5Param implements MkStage::StageParam {
+ private module PrevStage = Stage4;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
+ pragma[nomagic]
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
ApNil getApNil(NodeEx node) {
@@ -2457,15 +2755,18 @@ private module Stage4Param implements MkStage::StageParam {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
- localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
+ PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
}
pragma[nomagic]
predicate flowOutOfCall(
- DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
) {
exists(FlowState state |
- flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
+ flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2493,7 +2794,7 @@ private module Stage4Param implements MkStage::StageParam {
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
-private module Stage4 = MkStage::Stage;
+private module Stage5 = MkStage::Stage;
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
@@ -2502,13 +2803,13 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
- NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
+ NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(AccessPathApprox apa0 |
- Stage4::parameterMayFlowThrough(_, c, _, _) and
- Stage4::revFlow(n, state, true, _, apa0, config) and
- Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
- n.getEnclosingCallable() = c
+ Stage5::parameterMayFlowThrough(p, _, _) and
+ Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
+ Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()),
+ TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2516,9 +2817,9 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
- exists(DataFlowCallable c |
- Stage4::parameterMayFlowThrough(_, c, apa, config) and
- nodeMayUseSummary0(n, c, state, apa, config)
+ exists(ParamNodeEx p |
+ Stage5::parameterMayFlowThrough(p, apa, config) and
+ nodeMayUseSummary0(n, p, state, apa, config)
)
}
@@ -2526,8 +2827,8 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
- Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, config)
+ Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
+ Stage5::revFlow(p, state, _, config)
)
}
@@ -2576,7 +2877,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
len = apa.len() and
result =
strictcount(AccessPathFront apf |
- Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
+ Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
config)
)
)
@@ -2585,7 +2886,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -2606,7 +2907,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
exists(TypedContent head |
apa.pop(head) = result and
- Stage4::consCand(head, result, config)
+ Stage5::consCand(head, result, config)
)
}
@@ -2688,7 +2989,7 @@ private newtype TPathNode =
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) {
// A PathNode is introduced by a source ...
- Stage4::revFlow(node, state, config) and
+ Stage5::revFlow(node, state, config) and
sourceNode(node, state, config) and
(
if hasSourceCallCtx(config)
@@ -2702,7 +3003,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
+ Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -2712,6 +3013,18 @@ private newtype TPathNode =
state = sink.getState() and
config = sink.getConfiguration()
)
+ } or
+ TPathNodeSourceGroup(string sourceGroup, Configuration config) {
+ exists(PathNodeImpl source |
+ sourceGroup = source.getSourceGroup() and
+ config = source.getConfiguration()
+ )
+ } or
+ TPathNodeSinkGroup(string sinkGroup, Configuration config) {
+ exists(PathNodeSink sink |
+ sinkGroup = sink.getSinkGroup() and
+ config = sink.getConfiguration()
+ )
}
/**
@@ -2832,7 +3145,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
override TypedContent getHead() { result = head1 }
override AccessPath getTail() {
- Stage4::consCand(head1, result.getApprox(), _) and
+ Stage5::consCand(head1, result.getApprox(), _) and
result.getHead() = head2 and
result.length() = len - 1
}
@@ -2863,7 +3176,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
override TypedContent getHead() { result = head }
override AccessPath getTail() {
- Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
+ Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
}
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
@@ -2920,6 +3233,22 @@ abstract private class PathNodeImpl extends TPathNode {
)
}
+ string getSourceGroup() {
+ this.isSource() and
+ this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result)
+ }
+
+ predicate isFlowSource() {
+ this.isSource() and not exists(this.getSourceGroup())
+ or
+ this instanceof PathNodeSourceGroup
+ }
+
+ predicate isFlowSink() {
+ this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or
+ this instanceof PathNodeSinkGroup
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2959,7 +3288,9 @@ abstract private class PathNodeImpl extends TPathNode {
/** Holds if `n` can reach a sink. */
private predicate directReach(PathNodeImpl n) {
- n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
+ n instanceof PathNodeSink or
+ n instanceof PathNodeSinkGroup or
+ directReach(n.getANonHiddenSuccessor())
}
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
@@ -3015,6 +3346,12 @@ class PathNode instanceof PathNodeImpl {
/** Holds if this node is a source. */
final predicate isSource() { super.isSource() }
+
+ /** Holds if this node is a grouping of source nodes. */
+ final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) }
+
+ /** Holds if this node is a grouping of sink nodes. */
+ final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) }
}
/**
@@ -3136,9 +3473,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNodeImpl getASuccessorImpl() { none() }
+ override PathNodeImpl getASuccessorImpl() {
+ result = TPathNodeSinkGroup(this.getSinkGroup(), config)
+ }
override predicate isSource() { sourceNode(node, state, config) }
+
+ string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
+}
+
+private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
+ string sourceGroup;
+ Configuration config;
+
+ PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
+
+ override NodeEx getNodeEx() { none() }
+
+ override FlowState getState() { none() }
+
+ override Configuration getConfiguration() { result = config }
+
+ override PathNodeImpl getASuccessorImpl() {
+ result.getSourceGroup() = sourceGroup and
+ result.getConfiguration() = config
+ }
+
+ override predicate isSource() { none() }
+
+ override string toString() { result = sourceGroup }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
+ }
+}
+
+private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
+ string sinkGroup;
+ Configuration config;
+
+ PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
+
+ override NodeEx getNodeEx() { none() }
+
+ override FlowState getState() { none() }
+
+ override Configuration getConfiguration() { result = config }
+
+ override PathNodeImpl getASuccessorImpl() { none() }
+
+ override predicate isSource() { none() }
+
+ override string toString() { result = sinkGroup }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
+ }
}
private predicate pathNode(
@@ -3174,7 +3568,8 @@ private predicate pathStep(
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
- localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
+ localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
+ localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -3216,7 +3611,7 @@ private predicate pathReadStep(
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
- Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
+ Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3226,7 +3621,7 @@ private predicate pathStoreStep(
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
+ Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3263,7 +3658,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, apa, config)
+ Stage5::revFlow(result, _, apa, config)
}
/**
@@ -3299,7 +3694,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, apa, config) and
+ Stage5::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
@@ -3354,17 +3749,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
- exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
+ exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
- pos = sc.getParameterPos() and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = pos
- or
- sc.getParamNode().allowParameterReturnInSelf()
- )
+ parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}
@@ -3495,6 +3884,15 @@ private module Subpaths {
* Will only have results if `configuration` has non-empty sources and
* sinks.
*/
+private predicate hasFlowPath(
+ PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration
+) {
+ flowsource.isFlowSource() and
+ flowsource.getConfiguration() = configuration and
+ (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
+ flowsink.isFlowSink()
+}
+
private predicate flowsTo(
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink,
Configuration configuration
@@ -3575,9 +3973,17 @@ predicate stageStats(
n = 45 and
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
or
- stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
+ stage = "5 Fwd" and
+ n = 50 and
+ Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
or
- stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
+ stage = "5 Rev" and
+ n = 55 and
+ Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
+ or
+ stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
+ or
+ stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
}
private module FlowExploration {
diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
index 2dde1c2819d..1228d00b6ba 100644
--- a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
+++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
- predicate isSink(Node source, FlowState state) { none() }
+ predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/
FlowFeature getAFeature() { none() }
+ /** Holds if sources should be grouped in the result of `hasFlowPath`. */
+ predicate sourceGrouping(Node source, string sourceGroup) { none() }
+
+ /** Holds if sinks should be grouped in the result of `hasFlowPath`. */
+ predicate sinkGrouping(Node sink, string sinkGroup) { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
* The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`.
*/
- predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
+ predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
/**
* Holds if data may flow from some source to `sink` for this configuration.
@@ -313,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -602,8 +606,27 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
+/**
+ * Holds if flow from `p` to a return node of kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[p, kind]
+private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
+ exists(ParameterPosition pos | p.isParameterOf(_, pos) |
+ not kind.(ParamUpdateReturnKind).getPosition() = pos
+ or
+ allowParameterReturnInSelfCached(p.asNode())
+ )
+}
+
private module Stage1 implements StageSig {
- class Ap = Unit;
+ class Ap extends int {
+ // workaround for bad functionality-induced joins (happens when using `Unit`)
+ pragma[nomagic]
+ Ap() { this in [0 .. 1] and this < 1 }
+ }
private class Cc = boolean;
@@ -944,6 +967,12 @@ private module Stage1 implements StageSig {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
+ pragma[nomagic]
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
+ revFlow(node, config) and
+ exists(ap)
+ }
+
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -975,21 +1004,26 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnKindExt kind |
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
+ exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
- or
- p.allowParameterReturnInSelf()
- )
+ parameterFlowThroughAllowed(p, kind)
)
}
+ pragma[nomagic]
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ ) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind() and
+ exists(argAp) and
+ exists(ap)
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1046,12 +1080,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
- viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
- Stage1::revFlow(ret, config) and
- not outBarrier(ret, config) and
- not inBarrier(out, config)
+ exists(ReturnPosition pos |
+ viableReturnPosOutNodeCand1(call, pos, out, config) and
+ pos = ret.getReturnPosition() and
+ kind = pos.getKind() and
+ Stage1::revFlow(ret, config) and
+ not outBarrier(ret, config) and
+ not inBarrier(out, config)
+ )
}
pragma[nomagic]
@@ -1081,10 +1119,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
+pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
- flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
+ flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1093,10 +1132,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
+pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
- flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
+ flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1109,12 +1149,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
) {
- flowOutOfCallNodeCand1(call, ret, out, config) and
+ flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
- b = branch(ret, config) and
- j = join(out, config) and
+ b = branch(ret, pragma[only_bind_into](config)) and
+ j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1130,10 +1171,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- flowIntoCallNodeCand1(call, arg, p, config) and
+ flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
- b = branch(arg, config) and
- j = join(p, config) and
+ b = branch(arg, pragma[only_bind_into](config)) and
+ j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1145,12 +1186,18 @@ private signature module StageSig {
predicate revFlow(NodeEx node, Configuration config);
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
+
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
+
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ );
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1216,7 +1263,8 @@ private module MkStage {
);
predicate flowOutOfCall(
- DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
);
predicate flowIntoCall(
@@ -1234,21 +1282,43 @@ private module MkStage {
import Param
/* Begin: Stage logic. */
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
+ PrevStage::revFlowAp(node, apa, config)
+ }
+
+ pragma[nomagic]
+ private predicate flowIntoCallApa(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
+ Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate flowOutOfCallApa(
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ ApApprox apa, Configuration config
+ ) {
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
+ PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ ApApprox argApa, ApApprox apa, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- matchesCall(ccc, call)
+ exists(ReturnKindExt kind |
+ flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ )
}
/**
@@ -1256,99 +1326,134 @@ private module MkStage {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
- * argument in a call, and if so, `argAp` records the access path of that
- * argument.
+ * argument in a call, and if so, `summaryCtx` and `argAp` record the
+ * corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
- NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ ApApprox apa, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
+ PrevStage::revFlow(node, state, apa, config) and
filter(node, state, ap, config)
}
+ pragma[inline]
+ additional predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ Configuration config
+ ) {
+ fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
+ }
+
pragma[nomagic]
private predicate fwdFlow0(
- NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ ApApprox apa, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
- ap = getApNil(node)
+ summaryCtx = TParamNodeNone() and
+ ap = getApNil(node) and
+ apa = getApprox(ap)
or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
+ ap = ap0 and
+ apa = apa0
or
localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
+ ap0 instanceof ApNil and
+ apa = getApprox(ap)
)
or
exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone() and
- ap = getApNil(node)
+ ap = getApNil(node) and
+ apa = getApprox(ap)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
+ fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone() and
- ap = getApNil(node)
+ ap = getApNil(node) and
+ apa = getApprox(ap)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
+ ap = apCons(tc, ap0) and
+ apa = getApprox(ap)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config) and
+ apa = getApprox(ap)
)
or
// flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParamNodeSome(node.asNode()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParamNodeNone() and argAp = apNone()
)
or
// flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
+ flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
+ inner = ret.getEnclosingCallable() and
+ cc = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ // flow through a callable
+ exists(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, boolean allowsFieldFlow, ApApprox innerArgApa
+ |
+ fwdFlowThrough(call, cc, state, ccc, summaryCtx, argAp, ap, apa, ret, innerArgApa, config) and
+ flowThroughOutOfCall(call, ccc, ret, node, allowsFieldFlow, innerArgApa, apa, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
+ ParamNodeOption summaryCtx, ApOption argAp, Configuration config
) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ exists(DataFlowType contentType, ApApprox apa1 |
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
+ PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1360,60 +1465,85 @@ private module MkStage {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
}
+ private class ApNonNil instanceof Ap {
+ pragma[nomagic]
+ ApNonNil() { not this instanceof ApNil }
+
+ string toString() { result = "" }
+ }
+
pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ private predicate fwdFlowRead0(
+ NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
Configuration config
) {
- fwdFlow(node1, state, cc, argAp, ap, config) and
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
+ PrevStage::readStepCand(node1, _, _, config)
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
+ ParamNodeOption summaryCtx, ApOption argAp, Configuration config
+ ) {
+ fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
- Ap ap, Configuration config
+ DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
+ ParamNodeOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, argAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
+ flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowRetFromArg(
+ RetNodeEx ret, FlowState state, CcCall ccc, ParamNodeEx summaryCtx, Ap argAp, ApApprox argApa,
+ Ap ap, ApApprox apa, Configuration config
) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
+ exists(ReturnKindExt kind |
+ fwdFlow(pragma[only_bind_into](ret), state, ccc,
+ TParamNodeSome(pragma[only_bind_into](summaryCtx.asNode())),
+ pragma[only_bind_into](apSome(argAp)), ap, pragma[only_bind_into](apa),
+ pragma[only_bind_into](config)) and
+ kind = ret.getKind() and
+ parameterFlowThroughAllowed(summaryCtx, kind) and
+ argApa = getApprox(argAp) and
+ PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config))
)
}
- pragma[nomagic]
- private predicate fwdFlowOutFromArg(
- DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
+ pragma[inline]
+ private predicate fwdFlowThrough0(
+ DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx,
+ ApOption argAp, Ap ap, ApApprox apa, RetNodeEx ret, ParamNodeEx innerSummaryCtx,
+ Ap innerArgAp, ApApprox innerArgApa, Configuration config
) {
- exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
- fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
- flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ fwdFlowRetFromArg(ret, state, ccc, innerSummaryCtx, innerArgAp, innerArgApa, ap, apa, config) and
+ fwdFlowIsEntered(call, cc, ccc, summaryCtx, argAp, innerSummaryCtx, innerArgAp, config)
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowThrough(
+ DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx,
+ ApOption argAp, Ap ap, ApApprox apa, RetNodeEx ret, ApApprox innerArgApa, Configuration config
+ ) {
+ fwdFlowThrough0(call, cc, state, ccc, summaryCtx, argAp, ap, apa, ret, _, _, innerArgApa,
+ config)
}
/**
@@ -1422,11 +1552,14 @@ private module MkStage {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ DataFlowCall call, Cc cc, CcCall innerCc, ParamNodeOption summaryCtx, ApOption argAp,
+ ParamNodeEx p, Ap ap, Configuration config
) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ exists(ApApprox apa |
+ fwdFlowIn(call, pragma[only_bind_into](p), _, cc, innerCc, summaryCtx, argAp, ap,
+ pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, apa, config) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config))
)
}
@@ -1434,146 +1567,194 @@ private module MkStage {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
- fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
+ fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
- fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
+ fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
+ private predicate returnFlowsThrough0(
+ DataFlowCall call, FlowState state, CcCall ccc, Ap ap, ApApprox apa, RetNodeEx ret,
+ ParamNodeEx innerSummaryCtx, Ap innerArgAp, ApApprox innerArgApa, Configuration config
+ ) {
+ fwdFlowThrough0(call, _, state, ccc, _, _, ap, apa, ret, innerSummaryCtx, innerArgAp,
+ innerArgApa, config)
+ }
+
+ pragma[nomagic]
+ private predicate returnFlowsThrough(
+ RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
+ Ap ap, Configuration config
+ ) {
+ exists(DataFlowCall call, ApApprox apa, boolean allowsFieldFlow, ApApprox innerArgApa |
+ returnFlowsThrough0(call, state, ccc, ap, apa, ret, p, argAp, innerArgApa, config) and
+ flowThroughOutOfCall(call, ccc, ret, _, allowsFieldFlow, innerArgApa, apa, config) and
+ pos = ret.getReturnPosition() and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp, Ap ap,
+ 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))
+ exists(ApApprox argApa |
+ flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
+ allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
+ pragma[only_bind_into](config)) and
+ returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ if allowsFieldFlow = false then argAp instanceof ApNil else any()
+ )
}
pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ private predicate flowIntoCallAp(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
+ Configuration config
) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
+ exists(ApApprox apa |
+ flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
+ fwdFlow(arg, _, _, _, _, ap, apa, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowOutOfCallAp(
+ DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow,
+ Ap ap, Configuration config
+ ) {
+ exists(ApApprox apa |
+ flowOutOfCallApa(call, ret, _, out, allowsFieldFlow, apa, config) and
+ fwdFlow(ret, _, _, _, _, ap, apa, config) and
+ pos = ret.getReturnPosition()
+ )
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
- * The Boolean `toReturn` records whether the node must be returned from the
- * enclosing callable in order to reach a sink, and if so, `returnAp` records
- * the access path of the returned value.
+ * The parameter `returnCtx` records whether (and how) the node must be returned
+ * from the enclosing callable in order to reach a sink, and if so, `returnAp`
+ * records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
+ Configuration config
) {
- revFlow0(node, state, toReturn, returnAp, ap, config) and
- fwdFlow(node, state, _, _, ap, config)
+ revFlow0(node, state, returnCtx, returnAp, ap, config) and
+ fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
+ Configuration config
) {
- fwdFlow(node, state, _, _, ap, config) and
+ fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
+ (
+ if hasSinkCallCtx(config)
+ then returnCtx = TReturnCtxNoFlowThrough()
+ else returnCtx = TReturnCtxNone()
+ ) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
- revFlow(mid, state0, toReturn, returnAp, ap, config)
+ revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
- revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
+ revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
- fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
+ flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ returnCtx = TReturnCtxNone()
+ )
or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ // flow through a callable
+ exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
+ revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
+ flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
)
or
// flow out of a callable
- revFlowOut(_, node, state, _, _, ap, config) and
- toReturn = true and
- if returnNodeMayFlowThrough(node, state, ap, config)
- then returnAp = apSome(ap)
- else returnAp = apNone()
+ exists(ReturnPosition pos |
+ revFlowOut(_, node, pos, state, _, _, ap, config) and
+ if returnFlowsThrough(node, pos, state, _, _, _, ap, config)
+ then (
+ returnCtx = TReturnCtxMaybeFlowThrough(pos) and
+ returnAp = apSome(ap)
+ ) else (
+ returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
+ )
+ )
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
- boolean toReturn, ApOption returnAp, Configuration config
+ ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1593,36 +1774,33 @@ private module MkStage {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx,
+ ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
- revFlow(out, state, toReturn, returnAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ revFlow(out, state, returnCtx, returnAp, ap, config) and
+ flowOutOfCallAp(call, ret, pos, out, allowsFieldFlow, ap, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate revFlowInNotToReturn(
- ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
+ private predicate revFlowParamToReturn(
+ ParamNodeEx p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap, Configuration config
) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, false, returnAp, ap, config) and
- flowIntoCall(_, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp),
+ pragma[only_bind_into](ap), pragma[only_bind_into](config)) and
+ parameterFlowThroughAllowed(p, pos.getKind()) and
+ PrevStage::parameterMayFlowThrough(p, getApprox(ap), config)
}
pragma[nomagic]
- private predicate revFlowInToReturn(
- DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
+ private predicate revFlowThrough(
+ DataFlowCall call, ReturnCtx returnCtx, ParamNodeEx p, FlowState state, ReturnPosition pos,
+ ApOption returnAp, Ap ap, Ap innerReturnAp, Configuration config
) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ revFlowParamToReturn(p, state, pos, innerReturnAp, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, pos, innerReturnAp, config)
}
/**
@@ -1632,11 +1810,12 @@ private module MkStage {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap,
+ Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
- revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
- fwdFlow(ret, state, ccc, apSome(_), ap, config) and
+ revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and
+ returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1673,6 +1852,11 @@ private module MkStage {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+ pragma[nomagic]
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
+ revFlow(node, _, _, _, ap, config)
+ }
+
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) {
@@ -1707,40 +1891,49 @@ private module MkStage {
validAp(ap, config)
}
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ pragma[nomagic]
+ private predicate parameterFlowsThroughRev(
+ ParamNodeEx p, Ap ap, ReturnPosition pos, Ap returnAp, Configuration config
) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap, config) and
+ parameterFlowThroughAllowed(p, pos.getKind())
}
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
- parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
- revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
- pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
- fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
- kind = ret.getKind() and
- p.getPosition() = pos and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = pos
- or
- p.allowParameterReturnInSelf()
- )
+ pragma[nomagic]
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, ReturnPosition pos |
+ returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, pos, _, config)
+ )
+ }
+
+ pragma[nomagic]
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ ) {
+ exists(ParamNodeEx p, ReturnPosition pos |
+ returnFlowsThrough(ret, pos, _, _, p, argAp, ap, config) and
+ parameterFlowsThroughRev(p, argAp, pos, ap, config) and
+ kind = pos.getKind()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowThroughArg(
+ DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
+ Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
+ revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
+ flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
+ revFlow(arg, state, returnCtx, returnAp, ap, config) and
+ revFlowThroughArg(call, arg, state, returnCtx, returnAp, ap, config)
)
}
@@ -1748,13 +1941,13 @@ private module MkStage {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
- nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
+ states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
+ count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)
)
or
fwd = false and
@@ -1763,8 +1956,8 @@ private module MkStage {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
- count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
- revFlow(n, state, b, retAp, ap, config)
+ count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
+ revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1909,7 +2102,7 @@ private module Stage2Param implements MkStage::StageParam {
exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1945,9 +2138,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
- DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
) {
- flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
+ flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2015,8 +2209,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
- flowIntoCallNodeCand1(_, node, next, config) or
- flowOutOfCallNodeCand1(_, node, next, config) or
+ flowIntoCallNodeCand2(_, node, next, _, config) or
+ flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2103,16 +2297,16 @@ private module LocalFlowBigStep {
pragma[nomagic]
predicate localFlowBigStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
- AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
+ DataFlowType t, Configuration config, LocalCallContext callContext
) {
- localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
+ localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
localFlowExit(node2, state1, config) and
state1 = state2
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
state1 != state2 and
preservesValue = false and
- apf = TFrontNil(node2.getDataFlowType()) and
+ t = node2.getDataFlowType() and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
@@ -2126,11 +2320,87 @@ private import LocalFlowBigStep
private module Stage3Param implements MkStage::StageParam {
private module PrevStage = Stage2;
+ class Ap = ApproxAccessPathFront;
+
+ class ApNil = ApproxAccessPathFrontNil;
+
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+
+ ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
+ }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
+
+ pragma[noinline]
+ Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
+
+ class ApOption = ApproxAccessPathFrontOption;
+
+ ApOption apNone() { result = TApproxAccessPathFrontNone() }
+
+ ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
+
+ import BooleanCallContext
+
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
+ ) {
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
+ exists(lcc)
+ }
+
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
+
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
+
+ pragma[nomagic]
+ private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
+ exists(Content c |
+ PrevStage::revFlow(node, pragma[only_bind_into](config)) and
+ PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
+ expectsContentEx(node, c) and
+ c = ap.getAHead().getContent()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ exists(state) and
+ exists(config) and
+ (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
+ (
+ notExpectsContent(node)
+ or
+ expectsContentCand(node, ap, config)
+ )
+ }
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ // We need to typecheck stores here, since reverse flow through a getter
+ // might have a different type here compared to inside the getter.
+ compatibleTypes(ap.getType(), contentType)
+ }
+}
+
+private module Stage3 implements StageSig {
+ import MkStage::Stage
+}
+
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
+
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
@@ -2150,16 +2420,42 @@ private module Stage3Param implements MkStage::StageParam {
import BooleanCallContext
+ pragma[nomagic]
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
- localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
+ PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
+ exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ pragma[nomagic]
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ exists(FlowState state |
+ flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
+ pragma[only_bind_into](config))
+ )
+ }
- predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ pragma[nomagic]
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ exists(FlowState state |
+ flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
+ pragma[only_bind_into](config))
+ )
+ }
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2215,8 +2511,8 @@ private module Stage3Param implements MkStage::StageParam {
}
}
-private module Stage3 implements StageSig {
- import MkStage::Stage
+private module Stage4 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2227,8 +2523,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
- Stage3::revFlow(node, state, true, _, apf, config) and
- Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
+ Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
+ Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
+ config)
)
}
@@ -2238,10 +2535,10 @@ private predicate flowCandSummaryCtx(
*/
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
exists(int tails, int nodes, int apLimit, int tupleLimit |
- tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
+ tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2255,11 +2552,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
private newtype TAccessPathApprox =
TNil(DataFlowType t) or
TConsNil(TypedContent tc, DataFlowType t) {
- Stage3::consCand(tc, TFrontNil(t), _) and
+ Stage4::consCand(tc, TFrontNil(t), _) and
not expensiveLen2unfolding(tc, _)
} or
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
- Stage3::consCand(tc1, TFrontHead(tc2), _) and
+ Stage4::consCand(tc1, TFrontHead(tc2), _) and
len in [2 .. accessPathLimit()] and
not expensiveLen2unfolding(tc1, _)
} or
@@ -2389,7 +2686,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
override AccessPathApprox pop(TypedContent head) {
head = tc and
(
- exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
+ exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
result = TConsCons(tc2, _, len - 1)
or
len = 2 and
@@ -2400,7 +2697,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
or
exists(DataFlowType t |
len = 1 and
- Stage3::consCand(tc, TFrontNil(t), _) and
+ Stage4::consCand(tc, TFrontNil(t), _) and
result = TNil(t)
)
)
@@ -2425,13 +2722,14 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4Param implements MkStage::StageParam {
- private module PrevStage = Stage3;
+private module Stage5Param implements MkStage::StageParam {
+ private module PrevStage = Stage4;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
+ pragma[nomagic]
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
ApNil getApNil(NodeEx node) {
@@ -2457,15 +2755,18 @@ private module Stage4Param implements MkStage::StageParam {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
- localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
+ PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
}
pragma[nomagic]
predicate flowOutOfCall(
- DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
) {
exists(FlowState state |
- flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
+ flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2493,7 +2794,7 @@ private module Stage4Param implements MkStage::StageParam {
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
-private module Stage4 = MkStage::Stage;
+private module Stage5 = MkStage::Stage;
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
@@ -2502,13 +2803,13 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
- NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
+ NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(AccessPathApprox apa0 |
- Stage4::parameterMayFlowThrough(_, c, _, _) and
- Stage4::revFlow(n, state, true, _, apa0, config) and
- Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
- n.getEnclosingCallable() = c
+ Stage5::parameterMayFlowThrough(p, _, _) and
+ Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
+ Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()),
+ TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2516,9 +2817,9 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
- exists(DataFlowCallable c |
- Stage4::parameterMayFlowThrough(_, c, apa, config) and
- nodeMayUseSummary0(n, c, state, apa, config)
+ exists(ParamNodeEx p |
+ Stage5::parameterMayFlowThrough(p, apa, config) and
+ nodeMayUseSummary0(n, p, state, apa, config)
)
}
@@ -2526,8 +2827,8 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
- Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, config)
+ Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
+ Stage5::revFlow(p, state, _, config)
)
}
@@ -2576,7 +2877,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
len = apa.len() and
result =
strictcount(AccessPathFront apf |
- Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
+ Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
config)
)
)
@@ -2585,7 +2886,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -2606,7 +2907,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
exists(TypedContent head |
apa.pop(head) = result and
- Stage4::consCand(head, result, config)
+ Stage5::consCand(head, result, config)
)
}
@@ -2688,7 +2989,7 @@ private newtype TPathNode =
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) {
// A PathNode is introduced by a source ...
- Stage4::revFlow(node, state, config) and
+ Stage5::revFlow(node, state, config) and
sourceNode(node, state, config) and
(
if hasSourceCallCtx(config)
@@ -2702,7 +3003,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
+ Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -2712,6 +3013,18 @@ private newtype TPathNode =
state = sink.getState() and
config = sink.getConfiguration()
)
+ } or
+ TPathNodeSourceGroup(string sourceGroup, Configuration config) {
+ exists(PathNodeImpl source |
+ sourceGroup = source.getSourceGroup() and
+ config = source.getConfiguration()
+ )
+ } or
+ TPathNodeSinkGroup(string sinkGroup, Configuration config) {
+ exists(PathNodeSink sink |
+ sinkGroup = sink.getSinkGroup() and
+ config = sink.getConfiguration()
+ )
}
/**
@@ -2832,7 +3145,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
override TypedContent getHead() { result = head1 }
override AccessPath getTail() {
- Stage4::consCand(head1, result.getApprox(), _) and
+ Stage5::consCand(head1, result.getApprox(), _) and
result.getHead() = head2 and
result.length() = len - 1
}
@@ -2863,7 +3176,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
override TypedContent getHead() { result = head }
override AccessPath getTail() {
- Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
+ Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
}
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
@@ -2920,6 +3233,22 @@ abstract private class PathNodeImpl extends TPathNode {
)
}
+ string getSourceGroup() {
+ this.isSource() and
+ this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result)
+ }
+
+ predicate isFlowSource() {
+ this.isSource() and not exists(this.getSourceGroup())
+ or
+ this instanceof PathNodeSourceGroup
+ }
+
+ predicate isFlowSink() {
+ this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or
+ this instanceof PathNodeSinkGroup
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2959,7 +3288,9 @@ abstract private class PathNodeImpl extends TPathNode {
/** Holds if `n` can reach a sink. */
private predicate directReach(PathNodeImpl n) {
- n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
+ n instanceof PathNodeSink or
+ n instanceof PathNodeSinkGroup or
+ directReach(n.getANonHiddenSuccessor())
}
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
@@ -3015,6 +3346,12 @@ class PathNode instanceof PathNodeImpl {
/** Holds if this node is a source. */
final predicate isSource() { super.isSource() }
+
+ /** Holds if this node is a grouping of source nodes. */
+ final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) }
+
+ /** Holds if this node is a grouping of sink nodes. */
+ final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) }
}
/**
@@ -3136,9 +3473,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNodeImpl getASuccessorImpl() { none() }
+ override PathNodeImpl getASuccessorImpl() {
+ result = TPathNodeSinkGroup(this.getSinkGroup(), config)
+ }
override predicate isSource() { sourceNode(node, state, config) }
+
+ string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
+}
+
+private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
+ string sourceGroup;
+ Configuration config;
+
+ PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
+
+ override NodeEx getNodeEx() { none() }
+
+ override FlowState getState() { none() }
+
+ override Configuration getConfiguration() { result = config }
+
+ override PathNodeImpl getASuccessorImpl() {
+ result.getSourceGroup() = sourceGroup and
+ result.getConfiguration() = config
+ }
+
+ override predicate isSource() { none() }
+
+ override string toString() { result = sourceGroup }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
+ }
+}
+
+private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
+ string sinkGroup;
+ Configuration config;
+
+ PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
+
+ override NodeEx getNodeEx() { none() }
+
+ override FlowState getState() { none() }
+
+ override Configuration getConfiguration() { result = config }
+
+ override PathNodeImpl getASuccessorImpl() { none() }
+
+ override predicate isSource() { none() }
+
+ override string toString() { result = sinkGroup }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
+ }
}
private predicate pathNode(
@@ -3174,7 +3568,8 @@ private predicate pathStep(
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
- localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
+ localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
+ localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -3216,7 +3611,7 @@ private predicate pathReadStep(
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
- Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
+ Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3226,7 +3621,7 @@ private predicate pathStoreStep(
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
+ Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3263,7 +3658,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, apa, config)
+ Stage5::revFlow(result, _, apa, config)
}
/**
@@ -3299,7 +3694,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, apa, config) and
+ Stage5::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
@@ -3354,17 +3749,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
- exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
+ exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
- pos = sc.getParameterPos() and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = pos
- or
- sc.getParamNode().allowParameterReturnInSelf()
- )
+ parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}
@@ -3495,6 +3884,15 @@ private module Subpaths {
* Will only have results if `configuration` has non-empty sources and
* sinks.
*/
+private predicate hasFlowPath(
+ PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration
+) {
+ flowsource.isFlowSource() and
+ flowsource.getConfiguration() = configuration and
+ (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
+ flowsink.isFlowSink()
+}
+
private predicate flowsTo(
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink,
Configuration configuration
@@ -3575,9 +3973,17 @@ predicate stageStats(
n = 45 and
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
or
- stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
+ stage = "5 Fwd" and
+ n = 50 and
+ Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
or
- stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
+ stage = "5 Rev" and
+ n = 55 and
+ Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
+ or
+ stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
+ or
+ stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
}
private module FlowExploration {
diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
index 2dde1c2819d..1228d00b6ba 100644
--- a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
+++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
- predicate isSink(Node source, FlowState state) { none() }
+ predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/
FlowFeature getAFeature() { none() }
+ /** Holds if sources should be grouped in the result of `hasFlowPath`. */
+ predicate sourceGrouping(Node source, string sourceGroup) { none() }
+
+ /** Holds if sinks should be grouped in the result of `hasFlowPath`. */
+ predicate sinkGrouping(Node sink, string sinkGroup) { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
* The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`.
*/
- predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
+ predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
/**
* Holds if data may flow from some source to `sink` for this configuration.
@@ -313,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -602,8 +606,27 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
+/**
+ * Holds if flow from `p` to a return node of kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[p, kind]
+private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
+ exists(ParameterPosition pos | p.isParameterOf(_, pos) |
+ not kind.(ParamUpdateReturnKind).getPosition() = pos
+ or
+ allowParameterReturnInSelfCached(p.asNode())
+ )
+}
+
private module Stage1 implements StageSig {
- class Ap = Unit;
+ class Ap extends int {
+ // workaround for bad functionality-induced joins (happens when using `Unit`)
+ pragma[nomagic]
+ Ap() { this in [0 .. 1] and this < 1 }
+ }
private class Cc = boolean;
@@ -944,6 +967,12 @@ private module Stage1 implements StageSig {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
+ pragma[nomagic]
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
+ revFlow(node, config) and
+ exists(ap)
+ }
+
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -975,21 +1004,26 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnKindExt kind |
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
+ exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
- or
- p.allowParameterReturnInSelf()
- )
+ parameterFlowThroughAllowed(p, kind)
)
}
+ pragma[nomagic]
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ ) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind() and
+ exists(argAp) and
+ exists(ap)
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1046,12 +1080,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
- viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
- Stage1::revFlow(ret, config) and
- not outBarrier(ret, config) and
- not inBarrier(out, config)
+ exists(ReturnPosition pos |
+ viableReturnPosOutNodeCand1(call, pos, out, config) and
+ pos = ret.getReturnPosition() and
+ kind = pos.getKind() and
+ Stage1::revFlow(ret, config) and
+ not outBarrier(ret, config) and
+ not inBarrier(out, config)
+ )
}
pragma[nomagic]
@@ -1081,10 +1119,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
+pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
- flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
+ flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1093,10 +1132,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
+pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
- flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
+ flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1109,12 +1149,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
) {
- flowOutOfCallNodeCand1(call, ret, out, config) and
+ flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
- b = branch(ret, config) and
- j = join(out, config) and
+ b = branch(ret, pragma[only_bind_into](config)) and
+ j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1130,10 +1171,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- flowIntoCallNodeCand1(call, arg, p, config) and
+ flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
- b = branch(arg, config) and
- j = join(p, config) and
+ b = branch(arg, pragma[only_bind_into](config)) and
+ j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1145,12 +1186,18 @@ private signature module StageSig {
predicate revFlow(NodeEx node, Configuration config);
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
+
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
+
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ );
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1216,7 +1263,8 @@ private module MkStage {
);
predicate flowOutOfCall(
- DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
);
predicate flowIntoCall(
@@ -1234,21 +1282,43 @@ private module MkStage {
import Param
/* Begin: Stage logic. */
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
+ PrevStage::revFlowAp(node, apa, config)
+ }
+
+ pragma[nomagic]
+ private predicate flowIntoCallApa(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
+ Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate flowOutOfCallApa(
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ ApApprox apa, Configuration config
+ ) {
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
+ PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ ApApprox argApa, ApApprox apa, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- matchesCall(ccc, call)
+ exists(ReturnKindExt kind |
+ flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ )
}
/**
@@ -1256,99 +1326,134 @@ private module MkStage {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
- * argument in a call, and if so, `argAp` records the access path of that
- * argument.
+ * argument in a call, and if so, `summaryCtx` and `argAp` record the
+ * corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
- NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ ApApprox apa, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
+ PrevStage::revFlow(node, state, apa, config) and
filter(node, state, ap, config)
}
+ pragma[inline]
+ additional predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ Configuration config
+ ) {
+ fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
+ }
+
pragma[nomagic]
private predicate fwdFlow0(
- NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ ApApprox apa, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
- ap = getApNil(node)
+ summaryCtx = TParamNodeNone() and
+ ap = getApNil(node) and
+ apa = getApprox(ap)
or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
+ ap = ap0 and
+ apa = apa0
or
localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
+ ap0 instanceof ApNil and
+ apa = getApprox(ap)
)
or
exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone() and
- ap = getApNil(node)
+ ap = getApNil(node) and
+ apa = getApprox(ap)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
+ fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone() and
- ap = getApNil(node)
+ ap = getApNil(node) and
+ apa = getApprox(ap)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
+ ap = apCons(tc, ap0) and
+ apa = getApprox(ap)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config) and
+ apa = getApprox(ap)
)
or
// flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParamNodeSome(node.asNode()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParamNodeNone() and argAp = apNone()
)
or
// flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
+ flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
+ inner = ret.getEnclosingCallable() and
+ cc = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ // flow through a callable
+ exists(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, boolean allowsFieldFlow, ApApprox innerArgApa
+ |
+ fwdFlowThrough(call, cc, state, ccc, summaryCtx, argAp, ap, apa, ret, innerArgApa, config) and
+ flowThroughOutOfCall(call, ccc, ret, node, allowsFieldFlow, innerArgApa, apa, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
+ ParamNodeOption summaryCtx, ApOption argAp, Configuration config
) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ exists(DataFlowType contentType, ApApprox apa1 |
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
+ PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1360,60 +1465,85 @@ private module MkStage {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
}
+ private class ApNonNil instanceof Ap {
+ pragma[nomagic]
+ ApNonNil() { not this instanceof ApNil }
+
+ string toString() { result = "" }
+ }
+
pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ private predicate fwdFlowRead0(
+ NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
Configuration config
) {
- fwdFlow(node1, state, cc, argAp, ap, config) and
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
+ PrevStage::readStepCand(node1, _, _, config)
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
+ ParamNodeOption summaryCtx, ApOption argAp, Configuration config
+ ) {
+ fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
- Ap ap, Configuration config
+ DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
+ ParamNodeOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, argAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
+ flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowRetFromArg(
+ RetNodeEx ret, FlowState state, CcCall ccc, ParamNodeEx summaryCtx, Ap argAp, ApApprox argApa,
+ Ap ap, ApApprox apa, Configuration config
) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
+ exists(ReturnKindExt kind |
+ fwdFlow(pragma[only_bind_into](ret), state, ccc,
+ TParamNodeSome(pragma[only_bind_into](summaryCtx.asNode())),
+ pragma[only_bind_into](apSome(argAp)), ap, pragma[only_bind_into](apa),
+ pragma[only_bind_into](config)) and
+ kind = ret.getKind() and
+ parameterFlowThroughAllowed(summaryCtx, kind) and
+ argApa = getApprox(argAp) and
+ PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config))
)
}
- pragma[nomagic]
- private predicate fwdFlowOutFromArg(
- DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
+ pragma[inline]
+ private predicate fwdFlowThrough0(
+ DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx,
+ ApOption argAp, Ap ap, ApApprox apa, RetNodeEx ret, ParamNodeEx innerSummaryCtx,
+ Ap innerArgAp, ApApprox innerArgApa, Configuration config
) {
- exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
- fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
- flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ fwdFlowRetFromArg(ret, state, ccc, innerSummaryCtx, innerArgAp, innerArgApa, ap, apa, config) and
+ fwdFlowIsEntered(call, cc, ccc, summaryCtx, argAp, innerSummaryCtx, innerArgAp, config)
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowThrough(
+ DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx,
+ ApOption argAp, Ap ap, ApApprox apa, RetNodeEx ret, ApApprox innerArgApa, Configuration config
+ ) {
+ fwdFlowThrough0(call, cc, state, ccc, summaryCtx, argAp, ap, apa, ret, _, _, innerArgApa,
+ config)
}
/**
@@ -1422,11 +1552,14 @@ private module MkStage {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ DataFlowCall call, Cc cc, CcCall innerCc, ParamNodeOption summaryCtx, ApOption argAp,
+ ParamNodeEx p, Ap ap, Configuration config
) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ exists(ApApprox apa |
+ fwdFlowIn(call, pragma[only_bind_into](p), _, cc, innerCc, summaryCtx, argAp, ap,
+ pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, apa, config) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config))
)
}
@@ -1434,146 +1567,194 @@ private module MkStage {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
- fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
+ fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
- fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
+ fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
+ private predicate returnFlowsThrough0(
+ DataFlowCall call, FlowState state, CcCall ccc, Ap ap, ApApprox apa, RetNodeEx ret,
+ ParamNodeEx innerSummaryCtx, Ap innerArgAp, ApApprox innerArgApa, Configuration config
+ ) {
+ fwdFlowThrough0(call, _, state, ccc, _, _, ap, apa, ret, innerSummaryCtx, innerArgAp,
+ innerArgApa, config)
+ }
+
+ pragma[nomagic]
+ private predicate returnFlowsThrough(
+ RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
+ Ap ap, Configuration config
+ ) {
+ exists(DataFlowCall call, ApApprox apa, boolean allowsFieldFlow, ApApprox innerArgApa |
+ returnFlowsThrough0(call, state, ccc, ap, apa, ret, p, argAp, innerArgApa, config) and
+ flowThroughOutOfCall(call, ccc, ret, _, allowsFieldFlow, innerArgApa, apa, config) and
+ pos = ret.getReturnPosition() and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp, Ap ap,
+ 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))
+ exists(ApApprox argApa |
+ flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
+ allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
+ pragma[only_bind_into](config)) and
+ returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ if allowsFieldFlow = false then argAp instanceof ApNil else any()
+ )
}
pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ private predicate flowIntoCallAp(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
+ Configuration config
) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
+ exists(ApApprox apa |
+ flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
+ fwdFlow(arg, _, _, _, _, ap, apa, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowOutOfCallAp(
+ DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow,
+ Ap ap, Configuration config
+ ) {
+ exists(ApApprox apa |
+ flowOutOfCallApa(call, ret, _, out, allowsFieldFlow, apa, config) and
+ fwdFlow(ret, _, _, _, _, ap, apa, config) and
+ pos = ret.getReturnPosition()
+ )
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
- * The Boolean `toReturn` records whether the node must be returned from the
- * enclosing callable in order to reach a sink, and if so, `returnAp` records
- * the access path of the returned value.
+ * The parameter `returnCtx` records whether (and how) the node must be returned
+ * from the enclosing callable in order to reach a sink, and if so, `returnAp`
+ * records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
+ Configuration config
) {
- revFlow0(node, state, toReturn, returnAp, ap, config) and
- fwdFlow(node, state, _, _, ap, config)
+ revFlow0(node, state, returnCtx, returnAp, ap, config) and
+ fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
+ Configuration config
) {
- fwdFlow(node, state, _, _, ap, config) and
+ fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
+ (
+ if hasSinkCallCtx(config)
+ then returnCtx = TReturnCtxNoFlowThrough()
+ else returnCtx = TReturnCtxNone()
+ ) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
- revFlow(mid, state0, toReturn, returnAp, ap, config)
+ revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
- revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
+ revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
- fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
+ flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ returnCtx = TReturnCtxNone()
+ )
or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ // flow through a callable
+ exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
+ revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
+ flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
)
or
// flow out of a callable
- revFlowOut(_, node, state, _, _, ap, config) and
- toReturn = true and
- if returnNodeMayFlowThrough(node, state, ap, config)
- then returnAp = apSome(ap)
- else returnAp = apNone()
+ exists(ReturnPosition pos |
+ revFlowOut(_, node, pos, state, _, _, ap, config) and
+ if returnFlowsThrough(node, pos, state, _, _, _, ap, config)
+ then (
+ returnCtx = TReturnCtxMaybeFlowThrough(pos) and
+ returnAp = apSome(ap)
+ ) else (
+ returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
+ )
+ )
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
- boolean toReturn, ApOption returnAp, Configuration config
+ ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1593,36 +1774,33 @@ private module MkStage {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx,
+ ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
- revFlow(out, state, toReturn, returnAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ revFlow(out, state, returnCtx, returnAp, ap, config) and
+ flowOutOfCallAp(call, ret, pos, out, allowsFieldFlow, ap, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate revFlowInNotToReturn(
- ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
+ private predicate revFlowParamToReturn(
+ ParamNodeEx p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap, Configuration config
) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, false, returnAp, ap, config) and
- flowIntoCall(_, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp),
+ pragma[only_bind_into](ap), pragma[only_bind_into](config)) and
+ parameterFlowThroughAllowed(p, pos.getKind()) and
+ PrevStage::parameterMayFlowThrough(p, getApprox(ap), config)
}
pragma[nomagic]
- private predicate revFlowInToReturn(
- DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
+ private predicate revFlowThrough(
+ DataFlowCall call, ReturnCtx returnCtx, ParamNodeEx p, FlowState state, ReturnPosition pos,
+ ApOption returnAp, Ap ap, Ap innerReturnAp, Configuration config
) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ revFlowParamToReturn(p, state, pos, innerReturnAp, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, pos, innerReturnAp, config)
}
/**
@@ -1632,11 +1810,12 @@ private module MkStage {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap,
+ Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
- revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
- fwdFlow(ret, state, ccc, apSome(_), ap, config) and
+ revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and
+ returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1673,6 +1852,11 @@ private module MkStage {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+ pragma[nomagic]
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
+ revFlow(node, _, _, _, ap, config)
+ }
+
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) {
@@ -1707,40 +1891,49 @@ private module MkStage {
validAp(ap, config)
}
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ pragma[nomagic]
+ private predicate parameterFlowsThroughRev(
+ ParamNodeEx p, Ap ap, ReturnPosition pos, Ap returnAp, Configuration config
) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap, config) and
+ parameterFlowThroughAllowed(p, pos.getKind())
}
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
- parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
- revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
- pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
- fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
- kind = ret.getKind() and
- p.getPosition() = pos and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = pos
- or
- p.allowParameterReturnInSelf()
- )
+ pragma[nomagic]
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, ReturnPosition pos |
+ returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, pos, _, config)
+ )
+ }
+
+ pragma[nomagic]
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ ) {
+ exists(ParamNodeEx p, ReturnPosition pos |
+ returnFlowsThrough(ret, pos, _, _, p, argAp, ap, config) and
+ parameterFlowsThroughRev(p, argAp, pos, ap, config) and
+ kind = pos.getKind()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowThroughArg(
+ DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
+ Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
+ revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
+ flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
+ revFlow(arg, state, returnCtx, returnAp, ap, config) and
+ revFlowThroughArg(call, arg, state, returnCtx, returnAp, ap, config)
)
}
@@ -1748,13 +1941,13 @@ private module MkStage {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
- nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
+ states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
+ count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)
)
or
fwd = false and
@@ -1763,8 +1956,8 @@ private module MkStage {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
- count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
- revFlow(n, state, b, retAp, ap, config)
+ count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
+ revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1909,7 +2102,7 @@ private module Stage2Param implements MkStage::StageParam {
exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1945,9 +2138,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
- DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
) {
- flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
+ flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2015,8 +2209,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
- flowIntoCallNodeCand1(_, node, next, config) or
- flowOutOfCallNodeCand1(_, node, next, config) or
+ flowIntoCallNodeCand2(_, node, next, _, config) or
+ flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2103,16 +2297,16 @@ private module LocalFlowBigStep {
pragma[nomagic]
predicate localFlowBigStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
- AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
+ DataFlowType t, Configuration config, LocalCallContext callContext
) {
- localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
+ localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
localFlowExit(node2, state1, config) and
state1 = state2
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
state1 != state2 and
preservesValue = false and
- apf = TFrontNil(node2.getDataFlowType()) and
+ t = node2.getDataFlowType() and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
@@ -2126,11 +2320,87 @@ private import LocalFlowBigStep
private module Stage3Param implements MkStage::StageParam {
private module PrevStage = Stage2;
+ class Ap = ApproxAccessPathFront;
+
+ class ApNil = ApproxAccessPathFrontNil;
+
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+
+ ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
+ }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
+
+ pragma[noinline]
+ Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
+
+ class ApOption = ApproxAccessPathFrontOption;
+
+ ApOption apNone() { result = TApproxAccessPathFrontNone() }
+
+ ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
+
+ import BooleanCallContext
+
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
+ ) {
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
+ exists(lcc)
+ }
+
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
+
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
+
+ pragma[nomagic]
+ private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
+ exists(Content c |
+ PrevStage::revFlow(node, pragma[only_bind_into](config)) and
+ PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
+ expectsContentEx(node, c) and
+ c = ap.getAHead().getContent()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ exists(state) and
+ exists(config) and
+ (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
+ (
+ notExpectsContent(node)
+ or
+ expectsContentCand(node, ap, config)
+ )
+ }
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ // We need to typecheck stores here, since reverse flow through a getter
+ // might have a different type here compared to inside the getter.
+ compatibleTypes(ap.getType(), contentType)
+ }
+}
+
+private module Stage3 implements StageSig {
+ import MkStage::Stage
+}
+
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
+
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
@@ -2150,16 +2420,42 @@ private module Stage3Param implements MkStage::StageParam {
import BooleanCallContext
+ pragma[nomagic]
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
- localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
+ PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
+ exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ pragma[nomagic]
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ exists(FlowState state |
+ flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
+ pragma[only_bind_into](config))
+ )
+ }
- predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ pragma[nomagic]
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ exists(FlowState state |
+ flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
+ pragma[only_bind_into](config))
+ )
+ }
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2215,8 +2511,8 @@ private module Stage3Param implements MkStage::StageParam {
}
}
-private module Stage3 implements StageSig {
- import MkStage::Stage
+private module Stage4 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2227,8 +2523,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
- Stage3::revFlow(node, state, true, _, apf, config) and
- Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
+ Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
+ Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
+ config)
)
}
@@ -2238,10 +2535,10 @@ private predicate flowCandSummaryCtx(
*/
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
exists(int tails, int nodes, int apLimit, int tupleLimit |
- tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
+ tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2255,11 +2552,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
private newtype TAccessPathApprox =
TNil(DataFlowType t) or
TConsNil(TypedContent tc, DataFlowType t) {
- Stage3::consCand(tc, TFrontNil(t), _) and
+ Stage4::consCand(tc, TFrontNil(t), _) and
not expensiveLen2unfolding(tc, _)
} or
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
- Stage3::consCand(tc1, TFrontHead(tc2), _) and
+ Stage4::consCand(tc1, TFrontHead(tc2), _) and
len in [2 .. accessPathLimit()] and
not expensiveLen2unfolding(tc1, _)
} or
@@ -2389,7 +2686,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
override AccessPathApprox pop(TypedContent head) {
head = tc and
(
- exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
+ exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
result = TConsCons(tc2, _, len - 1)
or
len = 2 and
@@ -2400,7 +2697,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
or
exists(DataFlowType t |
len = 1 and
- Stage3::consCand(tc, TFrontNil(t), _) and
+ Stage4::consCand(tc, TFrontNil(t), _) and
result = TNil(t)
)
)
@@ -2425,13 +2722,14 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4Param implements MkStage::StageParam {
- private module PrevStage = Stage3;
+private module Stage5Param implements MkStage::StageParam {
+ private module PrevStage = Stage4;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
+ pragma[nomagic]
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
ApNil getApNil(NodeEx node) {
@@ -2457,15 +2755,18 @@ private module Stage4Param implements MkStage::StageParam {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
- localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
+ PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
}
pragma[nomagic]
predicate flowOutOfCall(
- DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
) {
exists(FlowState state |
- flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
+ flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2493,7 +2794,7 @@ private module Stage4Param implements MkStage::StageParam {
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
-private module Stage4 = MkStage::Stage;
+private module Stage5 = MkStage::Stage;
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
@@ -2502,13 +2803,13 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
- NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
+ NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(AccessPathApprox apa0 |
- Stage4::parameterMayFlowThrough(_, c, _, _) and
- Stage4::revFlow(n, state, true, _, apa0, config) and
- Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
- n.getEnclosingCallable() = c
+ Stage5::parameterMayFlowThrough(p, _, _) and
+ Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
+ Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()),
+ TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2516,9 +2817,9 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
- exists(DataFlowCallable c |
- Stage4::parameterMayFlowThrough(_, c, apa, config) and
- nodeMayUseSummary0(n, c, state, apa, config)
+ exists(ParamNodeEx p |
+ Stage5::parameterMayFlowThrough(p, apa, config) and
+ nodeMayUseSummary0(n, p, state, apa, config)
)
}
@@ -2526,8 +2827,8 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
- Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, config)
+ Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
+ Stage5::revFlow(p, state, _, config)
)
}
@@ -2576,7 +2877,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
len = apa.len() and
result =
strictcount(AccessPathFront apf |
- Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
+ Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
config)
)
)
@@ -2585,7 +2886,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -2606,7 +2907,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
exists(TypedContent head |
apa.pop(head) = result and
- Stage4::consCand(head, result, config)
+ Stage5::consCand(head, result, config)
)
}
@@ -2688,7 +2989,7 @@ private newtype TPathNode =
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) {
// A PathNode is introduced by a source ...
- Stage4::revFlow(node, state, config) and
+ Stage5::revFlow(node, state, config) and
sourceNode(node, state, config) and
(
if hasSourceCallCtx(config)
@@ -2702,7 +3003,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
+ Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -2712,6 +3013,18 @@ private newtype TPathNode =
state = sink.getState() and
config = sink.getConfiguration()
)
+ } or
+ TPathNodeSourceGroup(string sourceGroup, Configuration config) {
+ exists(PathNodeImpl source |
+ sourceGroup = source.getSourceGroup() and
+ config = source.getConfiguration()
+ )
+ } or
+ TPathNodeSinkGroup(string sinkGroup, Configuration config) {
+ exists(PathNodeSink sink |
+ sinkGroup = sink.getSinkGroup() and
+ config = sink.getConfiguration()
+ )
}
/**
@@ -2832,7 +3145,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
override TypedContent getHead() { result = head1 }
override AccessPath getTail() {
- Stage4::consCand(head1, result.getApprox(), _) and
+ Stage5::consCand(head1, result.getApprox(), _) and
result.getHead() = head2 and
result.length() = len - 1
}
@@ -2863,7 +3176,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
override TypedContent getHead() { result = head }
override AccessPath getTail() {
- Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
+ Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
}
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
@@ -2920,6 +3233,22 @@ abstract private class PathNodeImpl extends TPathNode {
)
}
+ string getSourceGroup() {
+ this.isSource() and
+ this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result)
+ }
+
+ predicate isFlowSource() {
+ this.isSource() and not exists(this.getSourceGroup())
+ or
+ this instanceof PathNodeSourceGroup
+ }
+
+ predicate isFlowSink() {
+ this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or
+ this instanceof PathNodeSinkGroup
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2959,7 +3288,9 @@ abstract private class PathNodeImpl extends TPathNode {
/** Holds if `n` can reach a sink. */
private predicate directReach(PathNodeImpl n) {
- n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
+ n instanceof PathNodeSink or
+ n instanceof PathNodeSinkGroup or
+ directReach(n.getANonHiddenSuccessor())
}
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
@@ -3015,6 +3346,12 @@ class PathNode instanceof PathNodeImpl {
/** Holds if this node is a source. */
final predicate isSource() { super.isSource() }
+
+ /** Holds if this node is a grouping of source nodes. */
+ final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) }
+
+ /** Holds if this node is a grouping of sink nodes. */
+ final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) }
}
/**
@@ -3136,9 +3473,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNodeImpl getASuccessorImpl() { none() }
+ override PathNodeImpl getASuccessorImpl() {
+ result = TPathNodeSinkGroup(this.getSinkGroup(), config)
+ }
override predicate isSource() { sourceNode(node, state, config) }
+
+ string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
+}
+
+private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
+ string sourceGroup;
+ Configuration config;
+
+ PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
+
+ override NodeEx getNodeEx() { none() }
+
+ override FlowState getState() { none() }
+
+ override Configuration getConfiguration() { result = config }
+
+ override PathNodeImpl getASuccessorImpl() {
+ result.getSourceGroup() = sourceGroup and
+ result.getConfiguration() = config
+ }
+
+ override predicate isSource() { none() }
+
+ override string toString() { result = sourceGroup }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
+ }
+}
+
+private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
+ string sinkGroup;
+ Configuration config;
+
+ PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
+
+ override NodeEx getNodeEx() { none() }
+
+ override FlowState getState() { none() }
+
+ override Configuration getConfiguration() { result = config }
+
+ override PathNodeImpl getASuccessorImpl() { none() }
+
+ override predicate isSource() { none() }
+
+ override string toString() { result = sinkGroup }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
+ }
}
private predicate pathNode(
@@ -3174,7 +3568,8 @@ private predicate pathStep(
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
- localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
+ localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
+ localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -3216,7 +3611,7 @@ private predicate pathReadStep(
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
- Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
+ Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3226,7 +3621,7 @@ private predicate pathStoreStep(
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
+ Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3263,7 +3658,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, apa, config)
+ Stage5::revFlow(result, _, apa, config)
}
/**
@@ -3299,7 +3694,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, apa, config) and
+ Stage5::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
@@ -3354,17 +3749,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
- exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
+ exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
- pos = sc.getParameterPos() and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = pos
- or
- sc.getParamNode().allowParameterReturnInSelf()
- )
+ parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}
@@ -3495,6 +3884,15 @@ private module Subpaths {
* Will only have results if `configuration` has non-empty sources and
* sinks.
*/
+private predicate hasFlowPath(
+ PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration
+) {
+ flowsource.isFlowSource() and
+ flowsource.getConfiguration() = configuration and
+ (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
+ flowsink.isFlowSink()
+}
+
private predicate flowsTo(
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink,
Configuration configuration
@@ -3575,9 +3973,17 @@ predicate stageStats(
n = 45 and
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
or
- stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
+ stage = "5 Fwd" and
+ n = 50 and
+ Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
or
- stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
+ stage = "5 Rev" and
+ n = 55 and
+ Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
+ or
+ stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
+ or
+ stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
}
private module FlowExploration {
diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
index 2dde1c2819d..1228d00b6ba 100644
--- a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
+++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
- predicate isSink(Node source, FlowState state) { none() }
+ predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/
FlowFeature getAFeature() { none() }
+ /** Holds if sources should be grouped in the result of `hasFlowPath`. */
+ predicate sourceGrouping(Node source, string sourceGroup) { none() }
+
+ /** Holds if sinks should be grouped in the result of `hasFlowPath`. */
+ predicate sinkGrouping(Node sink, string sinkGroup) { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
* The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`.
*/
- predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
+ predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
/**
* Holds if data may flow from some source to `sink` for this configuration.
@@ -313,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -602,8 +606,27 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
+/**
+ * Holds if flow from `p` to a return node of kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[p, kind]
+private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
+ exists(ParameterPosition pos | p.isParameterOf(_, pos) |
+ not kind.(ParamUpdateReturnKind).getPosition() = pos
+ or
+ allowParameterReturnInSelfCached(p.asNode())
+ )
+}
+
private module Stage1 implements StageSig {
- class Ap = Unit;
+ class Ap extends int {
+ // workaround for bad functionality-induced joins (happens when using `Unit`)
+ pragma[nomagic]
+ Ap() { this in [0 .. 1] and this < 1 }
+ }
private class Cc = boolean;
@@ -944,6 +967,12 @@ private module Stage1 implements StageSig {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
+ pragma[nomagic]
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
+ revFlow(node, config) and
+ exists(ap)
+ }
+
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -975,21 +1004,26 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnKindExt kind |
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
+ exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
- or
- p.allowParameterReturnInSelf()
- )
+ parameterFlowThroughAllowed(p, kind)
)
}
+ pragma[nomagic]
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ ) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind() and
+ exists(argAp) and
+ exists(ap)
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1046,12 +1080,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
- viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
- Stage1::revFlow(ret, config) and
- not outBarrier(ret, config) and
- not inBarrier(out, config)
+ exists(ReturnPosition pos |
+ viableReturnPosOutNodeCand1(call, pos, out, config) and
+ pos = ret.getReturnPosition() and
+ kind = pos.getKind() and
+ Stage1::revFlow(ret, config) and
+ not outBarrier(ret, config) and
+ not inBarrier(out, config)
+ )
}
pragma[nomagic]
@@ -1081,10 +1119,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
+pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
- flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
+ flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1093,10 +1132,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
+pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
- flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
+ flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1109,12 +1149,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
) {
- flowOutOfCallNodeCand1(call, ret, out, config) and
+ flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
- b = branch(ret, config) and
- j = join(out, config) and
+ b = branch(ret, pragma[only_bind_into](config)) and
+ j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1130,10 +1171,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- flowIntoCallNodeCand1(call, arg, p, config) and
+ flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
- b = branch(arg, config) and
- j = join(p, config) and
+ b = branch(arg, pragma[only_bind_into](config)) and
+ j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1145,12 +1186,18 @@ private signature module StageSig {
predicate revFlow(NodeEx node, Configuration config);
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
+
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
+
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ );
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1216,7 +1263,8 @@ private module MkStage {
);
predicate flowOutOfCall(
- DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
);
predicate flowIntoCall(
@@ -1234,21 +1282,43 @@ private module MkStage {
import Param
/* Begin: Stage logic. */
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
+ PrevStage::revFlowAp(node, apa, config)
+ }
+
+ pragma[nomagic]
+ private predicate flowIntoCallApa(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
+ Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate flowOutOfCallApa(
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ ApApprox apa, Configuration config
+ ) {
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
+ PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ ApApprox argApa, ApApprox apa, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- matchesCall(ccc, call)
+ exists(ReturnKindExt kind |
+ flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ )
}
/**
@@ -1256,99 +1326,134 @@ private module MkStage {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
- * argument in a call, and if so, `argAp` records the access path of that
- * argument.
+ * argument in a call, and if so, `summaryCtx` and `argAp` record the
+ * corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
- NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ ApApprox apa, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
+ PrevStage::revFlow(node, state, apa, config) and
filter(node, state, ap, config)
}
+ pragma[inline]
+ additional predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ Configuration config
+ ) {
+ fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
+ }
+
pragma[nomagic]
private predicate fwdFlow0(
- NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ ApApprox apa, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
- ap = getApNil(node)
+ summaryCtx = TParamNodeNone() and
+ ap = getApNil(node) and
+ apa = getApprox(ap)
or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
+ ap = ap0 and
+ apa = apa0
or
localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
+ ap0 instanceof ApNil and
+ apa = getApprox(ap)
)
or
exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone() and
- ap = getApNil(node)
+ ap = getApNil(node) and
+ apa = getApprox(ap)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
+ fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone() and
- ap = getApNil(node)
+ ap = getApNil(node) and
+ apa = getApprox(ap)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
+ ap = apCons(tc, ap0) and
+ apa = getApprox(ap)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config) and
+ apa = getApprox(ap)
)
or
// flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParamNodeSome(node.asNode()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParamNodeNone() and argAp = apNone()
)
or
// flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
+ flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
+ inner = ret.getEnclosingCallable() and
+ cc = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ // flow through a callable
+ exists(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, boolean allowsFieldFlow, ApApprox innerArgApa
+ |
+ fwdFlowThrough(call, cc, state, ccc, summaryCtx, argAp, ap, apa, ret, innerArgApa, config) and
+ flowThroughOutOfCall(call, ccc, ret, node, allowsFieldFlow, innerArgApa, apa, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
+ ParamNodeOption summaryCtx, ApOption argAp, Configuration config
) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ exists(DataFlowType contentType, ApApprox apa1 |
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
+ PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1360,60 +1465,85 @@ private module MkStage {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
}
+ private class ApNonNil instanceof Ap {
+ pragma[nomagic]
+ ApNonNil() { not this instanceof ApNil }
+
+ string toString() { result = "" }
+ }
+
pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ private predicate fwdFlowRead0(
+ NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
Configuration config
) {
- fwdFlow(node1, state, cc, argAp, ap, config) and
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
+ PrevStage::readStepCand(node1, _, _, config)
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
+ ParamNodeOption summaryCtx, ApOption argAp, Configuration config
+ ) {
+ fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
- Ap ap, Configuration config
+ DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
+ ParamNodeOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, argAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
+ flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowRetFromArg(
+ RetNodeEx ret, FlowState state, CcCall ccc, ParamNodeEx summaryCtx, Ap argAp, ApApprox argApa,
+ Ap ap, ApApprox apa, Configuration config
) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
+ exists(ReturnKindExt kind |
+ fwdFlow(pragma[only_bind_into](ret), state, ccc,
+ TParamNodeSome(pragma[only_bind_into](summaryCtx.asNode())),
+ pragma[only_bind_into](apSome(argAp)), ap, pragma[only_bind_into](apa),
+ pragma[only_bind_into](config)) and
+ kind = ret.getKind() and
+ parameterFlowThroughAllowed(summaryCtx, kind) and
+ argApa = getApprox(argAp) and
+ PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config))
)
}
- pragma[nomagic]
- private predicate fwdFlowOutFromArg(
- DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
+ pragma[inline]
+ private predicate fwdFlowThrough0(
+ DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx,
+ ApOption argAp, Ap ap, ApApprox apa, RetNodeEx ret, ParamNodeEx innerSummaryCtx,
+ Ap innerArgAp, ApApprox innerArgApa, Configuration config
) {
- exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
- fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
- flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ fwdFlowRetFromArg(ret, state, ccc, innerSummaryCtx, innerArgAp, innerArgApa, ap, apa, config) and
+ fwdFlowIsEntered(call, cc, ccc, summaryCtx, argAp, innerSummaryCtx, innerArgAp, config)
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowThrough(
+ DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx,
+ ApOption argAp, Ap ap, ApApprox apa, RetNodeEx ret, ApApprox innerArgApa, Configuration config
+ ) {
+ fwdFlowThrough0(call, cc, state, ccc, summaryCtx, argAp, ap, apa, ret, _, _, innerArgApa,
+ config)
}
/**
@@ -1422,11 +1552,14 @@ private module MkStage {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ DataFlowCall call, Cc cc, CcCall innerCc, ParamNodeOption summaryCtx, ApOption argAp,
+ ParamNodeEx p, Ap ap, Configuration config
) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ exists(ApApprox apa |
+ fwdFlowIn(call, pragma[only_bind_into](p), _, cc, innerCc, summaryCtx, argAp, ap,
+ pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, apa, config) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config))
)
}
@@ -1434,146 +1567,194 @@ private module MkStage {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
- fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
+ fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
- fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
+ fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
+ private predicate returnFlowsThrough0(
+ DataFlowCall call, FlowState state, CcCall ccc, Ap ap, ApApprox apa, RetNodeEx ret,
+ ParamNodeEx innerSummaryCtx, Ap innerArgAp, ApApprox innerArgApa, Configuration config
+ ) {
+ fwdFlowThrough0(call, _, state, ccc, _, _, ap, apa, ret, innerSummaryCtx, innerArgAp,
+ innerArgApa, config)
+ }
+
+ pragma[nomagic]
+ private predicate returnFlowsThrough(
+ RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
+ Ap ap, Configuration config
+ ) {
+ exists(DataFlowCall call, ApApprox apa, boolean allowsFieldFlow, ApApprox innerArgApa |
+ returnFlowsThrough0(call, state, ccc, ap, apa, ret, p, argAp, innerArgApa, config) and
+ flowThroughOutOfCall(call, ccc, ret, _, allowsFieldFlow, innerArgApa, apa, config) and
+ pos = ret.getReturnPosition() and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp, Ap ap,
+ 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))
+ exists(ApApprox argApa |
+ flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
+ allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
+ pragma[only_bind_into](config)) and
+ returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ if allowsFieldFlow = false then argAp instanceof ApNil else any()
+ )
}
pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ private predicate flowIntoCallAp(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
+ Configuration config
) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
+ exists(ApApprox apa |
+ flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
+ fwdFlow(arg, _, _, _, _, ap, apa, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowOutOfCallAp(
+ DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow,
+ Ap ap, Configuration config
+ ) {
+ exists(ApApprox apa |
+ flowOutOfCallApa(call, ret, _, out, allowsFieldFlow, apa, config) and
+ fwdFlow(ret, _, _, _, _, ap, apa, config) and
+ pos = ret.getReturnPosition()
+ )
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
- * The Boolean `toReturn` records whether the node must be returned from the
- * enclosing callable in order to reach a sink, and if so, `returnAp` records
- * the access path of the returned value.
+ * The parameter `returnCtx` records whether (and how) the node must be returned
+ * from the enclosing callable in order to reach a sink, and if so, `returnAp`
+ * records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
+ Configuration config
) {
- revFlow0(node, state, toReturn, returnAp, ap, config) and
- fwdFlow(node, state, _, _, ap, config)
+ revFlow0(node, state, returnCtx, returnAp, ap, config) and
+ fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
+ Configuration config
) {
- fwdFlow(node, state, _, _, ap, config) and
+ fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
+ (
+ if hasSinkCallCtx(config)
+ then returnCtx = TReturnCtxNoFlowThrough()
+ else returnCtx = TReturnCtxNone()
+ ) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
- revFlow(mid, state0, toReturn, returnAp, ap, config)
+ revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
- revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
+ revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
- fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
+ flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ returnCtx = TReturnCtxNone()
+ )
or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ // flow through a callable
+ exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
+ revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
+ flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
)
or
// flow out of a callable
- revFlowOut(_, node, state, _, _, ap, config) and
- toReturn = true and
- if returnNodeMayFlowThrough(node, state, ap, config)
- then returnAp = apSome(ap)
- else returnAp = apNone()
+ exists(ReturnPosition pos |
+ revFlowOut(_, node, pos, state, _, _, ap, config) and
+ if returnFlowsThrough(node, pos, state, _, _, _, ap, config)
+ then (
+ returnCtx = TReturnCtxMaybeFlowThrough(pos) and
+ returnAp = apSome(ap)
+ ) else (
+ returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
+ )
+ )
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
- boolean toReturn, ApOption returnAp, Configuration config
+ ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1593,36 +1774,33 @@ private module MkStage {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx,
+ ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
- revFlow(out, state, toReturn, returnAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ revFlow(out, state, returnCtx, returnAp, ap, config) and
+ flowOutOfCallAp(call, ret, pos, out, allowsFieldFlow, ap, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate revFlowInNotToReturn(
- ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
+ private predicate revFlowParamToReturn(
+ ParamNodeEx p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap, Configuration config
) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, false, returnAp, ap, config) and
- flowIntoCall(_, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp),
+ pragma[only_bind_into](ap), pragma[only_bind_into](config)) and
+ parameterFlowThroughAllowed(p, pos.getKind()) and
+ PrevStage::parameterMayFlowThrough(p, getApprox(ap), config)
}
pragma[nomagic]
- private predicate revFlowInToReturn(
- DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
+ private predicate revFlowThrough(
+ DataFlowCall call, ReturnCtx returnCtx, ParamNodeEx p, FlowState state, ReturnPosition pos,
+ ApOption returnAp, Ap ap, Ap innerReturnAp, Configuration config
) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ revFlowParamToReturn(p, state, pos, innerReturnAp, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, pos, innerReturnAp, config)
}
/**
@@ -1632,11 +1810,12 @@ private module MkStage {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap,
+ Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
- revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
- fwdFlow(ret, state, ccc, apSome(_), ap, config) and
+ revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and
+ returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1673,6 +1852,11 @@ private module MkStage {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+ pragma[nomagic]
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
+ revFlow(node, _, _, _, ap, config)
+ }
+
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) {
@@ -1707,40 +1891,49 @@ private module MkStage {
validAp(ap, config)
}
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ pragma[nomagic]
+ private predicate parameterFlowsThroughRev(
+ ParamNodeEx p, Ap ap, ReturnPosition pos, Ap returnAp, Configuration config
) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap, config) and
+ parameterFlowThroughAllowed(p, pos.getKind())
}
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
- parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
- revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
- pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
- fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
- kind = ret.getKind() and
- p.getPosition() = pos and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = pos
- or
- p.allowParameterReturnInSelf()
- )
+ pragma[nomagic]
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, ReturnPosition pos |
+ returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, pos, _, config)
+ )
+ }
+
+ pragma[nomagic]
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ ) {
+ exists(ParamNodeEx p, ReturnPosition pos |
+ returnFlowsThrough(ret, pos, _, _, p, argAp, ap, config) and
+ parameterFlowsThroughRev(p, argAp, pos, ap, config) and
+ kind = pos.getKind()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowThroughArg(
+ DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
+ Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
+ revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
+ flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
+ revFlow(arg, state, returnCtx, returnAp, ap, config) and
+ revFlowThroughArg(call, arg, state, returnCtx, returnAp, ap, config)
)
}
@@ -1748,13 +1941,13 @@ private module MkStage {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
- nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
+ states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
+ count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)
)
or
fwd = false and
@@ -1763,8 +1956,8 @@ private module MkStage {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
- count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
- revFlow(n, state, b, retAp, ap, config)
+ count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
+ revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1909,7 +2102,7 @@ private module Stage2Param implements MkStage::StageParam {
exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1945,9 +2138,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
- DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
) {
- flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
+ flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2015,8 +2209,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
- flowIntoCallNodeCand1(_, node, next, config) or
- flowOutOfCallNodeCand1(_, node, next, config) or
+ flowIntoCallNodeCand2(_, node, next, _, config) or
+ flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2103,16 +2297,16 @@ private module LocalFlowBigStep {
pragma[nomagic]
predicate localFlowBigStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
- AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
+ DataFlowType t, Configuration config, LocalCallContext callContext
) {
- localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
+ localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
localFlowExit(node2, state1, config) and
state1 = state2
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
state1 != state2 and
preservesValue = false and
- apf = TFrontNil(node2.getDataFlowType()) and
+ t = node2.getDataFlowType() and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
@@ -2126,11 +2320,87 @@ private import LocalFlowBigStep
private module Stage3Param implements MkStage::StageParam {
private module PrevStage = Stage2;
+ class Ap = ApproxAccessPathFront;
+
+ class ApNil = ApproxAccessPathFrontNil;
+
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+
+ ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
+ }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
+
+ pragma[noinline]
+ Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
+
+ class ApOption = ApproxAccessPathFrontOption;
+
+ ApOption apNone() { result = TApproxAccessPathFrontNone() }
+
+ ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
+
+ import BooleanCallContext
+
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
+ ) {
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
+ exists(lcc)
+ }
+
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
+
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
+
+ pragma[nomagic]
+ private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
+ exists(Content c |
+ PrevStage::revFlow(node, pragma[only_bind_into](config)) and
+ PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
+ expectsContentEx(node, c) and
+ c = ap.getAHead().getContent()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ exists(state) and
+ exists(config) and
+ (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
+ (
+ notExpectsContent(node)
+ or
+ expectsContentCand(node, ap, config)
+ )
+ }
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ // We need to typecheck stores here, since reverse flow through a getter
+ // might have a different type here compared to inside the getter.
+ compatibleTypes(ap.getType(), contentType)
+ }
+}
+
+private module Stage3 implements StageSig {
+ import MkStage::Stage
+}
+
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
+
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
@@ -2150,16 +2420,42 @@ private module Stage3Param implements MkStage::StageParam {
import BooleanCallContext
+ pragma[nomagic]
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
- localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
+ PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
+ exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ pragma[nomagic]
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ exists(FlowState state |
+ flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
+ pragma[only_bind_into](config))
+ )
+ }
- predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ pragma[nomagic]
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ exists(FlowState state |
+ flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
+ pragma[only_bind_into](config))
+ )
+ }
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2215,8 +2511,8 @@ private module Stage3Param implements MkStage::StageParam {
}
}
-private module Stage3 implements StageSig {
- import MkStage::Stage
+private module Stage4 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2227,8 +2523,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
- Stage3::revFlow(node, state, true, _, apf, config) and
- Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
+ Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
+ Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
+ config)
)
}
@@ -2238,10 +2535,10 @@ private predicate flowCandSummaryCtx(
*/
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
exists(int tails, int nodes, int apLimit, int tupleLimit |
- tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
+ tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2255,11 +2552,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
private newtype TAccessPathApprox =
TNil(DataFlowType t) or
TConsNil(TypedContent tc, DataFlowType t) {
- Stage3::consCand(tc, TFrontNil(t), _) and
+ Stage4::consCand(tc, TFrontNil(t), _) and
not expensiveLen2unfolding(tc, _)
} or
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
- Stage3::consCand(tc1, TFrontHead(tc2), _) and
+ Stage4::consCand(tc1, TFrontHead(tc2), _) and
len in [2 .. accessPathLimit()] and
not expensiveLen2unfolding(tc1, _)
} or
@@ -2389,7 +2686,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
override AccessPathApprox pop(TypedContent head) {
head = tc and
(
- exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
+ exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
result = TConsCons(tc2, _, len - 1)
or
len = 2 and
@@ -2400,7 +2697,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
or
exists(DataFlowType t |
len = 1 and
- Stage3::consCand(tc, TFrontNil(t), _) and
+ Stage4::consCand(tc, TFrontNil(t), _) and
result = TNil(t)
)
)
@@ -2425,13 +2722,14 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4Param implements MkStage::StageParam {
- private module PrevStage = Stage3;
+private module Stage5Param implements MkStage::StageParam {
+ private module PrevStage = Stage4;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
+ pragma[nomagic]
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
ApNil getApNil(NodeEx node) {
@@ -2457,15 +2755,18 @@ private module Stage4Param implements MkStage::StageParam {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
- localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
+ PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
}
pragma[nomagic]
predicate flowOutOfCall(
- DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
) {
exists(FlowState state |
- flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
+ flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2493,7 +2794,7 @@ private module Stage4Param implements MkStage::StageParam {
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
-private module Stage4 = MkStage::Stage;
+private module Stage5 = MkStage::Stage;
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
@@ -2502,13 +2803,13 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
- NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
+ NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(AccessPathApprox apa0 |
- Stage4::parameterMayFlowThrough(_, c, _, _) and
- Stage4::revFlow(n, state, true, _, apa0, config) and
- Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
- n.getEnclosingCallable() = c
+ Stage5::parameterMayFlowThrough(p, _, _) and
+ Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
+ Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()),
+ TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2516,9 +2817,9 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
- exists(DataFlowCallable c |
- Stage4::parameterMayFlowThrough(_, c, apa, config) and
- nodeMayUseSummary0(n, c, state, apa, config)
+ exists(ParamNodeEx p |
+ Stage5::parameterMayFlowThrough(p, apa, config) and
+ nodeMayUseSummary0(n, p, state, apa, config)
)
}
@@ -2526,8 +2827,8 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
- Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, config)
+ Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
+ Stage5::revFlow(p, state, _, config)
)
}
@@ -2576,7 +2877,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
len = apa.len() and
result =
strictcount(AccessPathFront apf |
- Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
+ Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
config)
)
)
@@ -2585,7 +2886,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -2606,7 +2907,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
exists(TypedContent head |
apa.pop(head) = result and
- Stage4::consCand(head, result, config)
+ Stage5::consCand(head, result, config)
)
}
@@ -2688,7 +2989,7 @@ private newtype TPathNode =
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) {
// A PathNode is introduced by a source ...
- Stage4::revFlow(node, state, config) and
+ Stage5::revFlow(node, state, config) and
sourceNode(node, state, config) and
(
if hasSourceCallCtx(config)
@@ -2702,7 +3003,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
+ Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -2712,6 +3013,18 @@ private newtype TPathNode =
state = sink.getState() and
config = sink.getConfiguration()
)
+ } or
+ TPathNodeSourceGroup(string sourceGroup, Configuration config) {
+ exists(PathNodeImpl source |
+ sourceGroup = source.getSourceGroup() and
+ config = source.getConfiguration()
+ )
+ } or
+ TPathNodeSinkGroup(string sinkGroup, Configuration config) {
+ exists(PathNodeSink sink |
+ sinkGroup = sink.getSinkGroup() and
+ config = sink.getConfiguration()
+ )
}
/**
@@ -2832,7 +3145,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
override TypedContent getHead() { result = head1 }
override AccessPath getTail() {
- Stage4::consCand(head1, result.getApprox(), _) and
+ Stage5::consCand(head1, result.getApprox(), _) and
result.getHead() = head2 and
result.length() = len - 1
}
@@ -2863,7 +3176,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
override TypedContent getHead() { result = head }
override AccessPath getTail() {
- Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
+ Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
}
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
@@ -2920,6 +3233,22 @@ abstract private class PathNodeImpl extends TPathNode {
)
}
+ string getSourceGroup() {
+ this.isSource() and
+ this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result)
+ }
+
+ predicate isFlowSource() {
+ this.isSource() and not exists(this.getSourceGroup())
+ or
+ this instanceof PathNodeSourceGroup
+ }
+
+ predicate isFlowSink() {
+ this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or
+ this instanceof PathNodeSinkGroup
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2959,7 +3288,9 @@ abstract private class PathNodeImpl extends TPathNode {
/** Holds if `n` can reach a sink. */
private predicate directReach(PathNodeImpl n) {
- n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
+ n instanceof PathNodeSink or
+ n instanceof PathNodeSinkGroup or
+ directReach(n.getANonHiddenSuccessor())
}
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
@@ -3015,6 +3346,12 @@ class PathNode instanceof PathNodeImpl {
/** Holds if this node is a source. */
final predicate isSource() { super.isSource() }
+
+ /** Holds if this node is a grouping of source nodes. */
+ final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) }
+
+ /** Holds if this node is a grouping of sink nodes. */
+ final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) }
}
/**
@@ -3136,9 +3473,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNodeImpl getASuccessorImpl() { none() }
+ override PathNodeImpl getASuccessorImpl() {
+ result = TPathNodeSinkGroup(this.getSinkGroup(), config)
+ }
override predicate isSource() { sourceNode(node, state, config) }
+
+ string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
+}
+
+private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
+ string sourceGroup;
+ Configuration config;
+
+ PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
+
+ override NodeEx getNodeEx() { none() }
+
+ override FlowState getState() { none() }
+
+ override Configuration getConfiguration() { result = config }
+
+ override PathNodeImpl getASuccessorImpl() {
+ result.getSourceGroup() = sourceGroup and
+ result.getConfiguration() = config
+ }
+
+ override predicate isSource() { none() }
+
+ override string toString() { result = sourceGroup }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
+ }
+}
+
+private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
+ string sinkGroup;
+ Configuration config;
+
+ PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
+
+ override NodeEx getNodeEx() { none() }
+
+ override FlowState getState() { none() }
+
+ override Configuration getConfiguration() { result = config }
+
+ override PathNodeImpl getASuccessorImpl() { none() }
+
+ override predicate isSource() { none() }
+
+ override string toString() { result = sinkGroup }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
+ }
}
private predicate pathNode(
@@ -3174,7 +3568,8 @@ private predicate pathStep(
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
- localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
+ localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
+ localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -3216,7 +3611,7 @@ private predicate pathReadStep(
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
- Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
+ Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3226,7 +3621,7 @@ private predicate pathStoreStep(
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
+ Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3263,7 +3658,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, apa, config)
+ Stage5::revFlow(result, _, apa, config)
}
/**
@@ -3299,7 +3694,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, apa, config) and
+ Stage5::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
@@ -3354,17 +3749,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
- exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
+ exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
- pos = sc.getParameterPos() and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = pos
- or
- sc.getParamNode().allowParameterReturnInSelf()
- )
+ parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}
@@ -3495,6 +3884,15 @@ private module Subpaths {
* Will only have results if `configuration` has non-empty sources and
* sinks.
*/
+private predicate hasFlowPath(
+ PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration
+) {
+ flowsource.isFlowSource() and
+ flowsource.getConfiguration() = configuration and
+ (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
+ flowsink.isFlowSink()
+}
+
private predicate flowsTo(
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink,
Configuration configuration
@@ -3575,9 +3973,17 @@ predicate stageStats(
n = 45 and
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
or
- stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
+ stage = "5 Fwd" and
+ n = 50 and
+ Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
or
- stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
+ stage = "5 Rev" and
+ n = 55 and
+ Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
+ or
+ stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
+ or
+ stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
}
private module FlowExploration {
diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
index ae9c6f3f12e..9aa6a529a5b 100644
--- a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
@@ -915,18 +915,57 @@ private module Cached {
TDataFlowCallNone() or
TDataFlowCallSome(DataFlowCall call)
+ cached
+ newtype TParamNodeOption =
+ TParamNodeNone() or
+ TParamNodeSome(ParamNode p)
+
+ cached
+ newtype TReturnCtx =
+ TReturnCtxNone() or
+ TReturnCtxNoFlowThrough() or
+ TReturnCtxMaybeFlowThrough(ReturnPosition pos)
+
+ cached
+ newtype TTypedContentApprox =
+ MkTypedContentApprox(ContentApprox c, DataFlowType t) {
+ exists(Content cont |
+ c = getContentApprox(cont) and
+ store(_, cont, _, _, t)
+ )
+ }
+
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
+ cached
+ TypedContent getATypedContent(TypedContentApprox c) {
+ exists(ContentApprox cls, DataFlowType t, Content cont |
+ c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
+ result = MkTypedContent(cont, pragma[only_bind_into](t)) and
+ cls = getContentApprox(cont)
+ )
+ }
+
cached
newtype TAccessPathFront =
TFrontNil(DataFlowType t) or
TFrontHead(TypedContent tc)
+ cached
+ newtype TApproxAccessPathFront =
+ TApproxFrontNil(DataFlowType t) or
+ TApproxFrontHead(TypedContentApprox tc)
+
cached
newtype TAccessPathFrontOption =
TAccessPathFrontNone() or
TAccessPathFrontSome(AccessPathFront apf)
+
+ cached
+ newtype TApproxAccessPathFrontOption =
+ TApproxAccessPathFrontNone() or
+ TApproxAccessPathFrontSome(ApproxAccessPathFront apf)
}
/**
@@ -1304,6 +1343,113 @@ class DataFlowCallOption extends TDataFlowCallOption {
}
}
+/** An optional `ParamNode`. */
+class ParamNodeOption extends TParamNodeOption {
+ string toString() {
+ this = TParamNodeNone() and
+ result = "(none)"
+ or
+ exists(ParamNode p |
+ this = TParamNodeSome(p) and
+ result = p.toString()
+ )
+ }
+}
+
+/**
+ * A return context used to calculate flow summaries in reverse flow.
+ *
+ * The possible values are:
+ *
+ * - `TReturnCtxNone()`: no return flow.
+ * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible.
+ * - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and
+ * flow through may be possible.
+ */
+class ReturnCtx extends TReturnCtx {
+ string toString() {
+ this = TReturnCtxNone() and
+ result = "(none)"
+ or
+ this = TReturnCtxNoFlowThrough() and
+ result = "(no flow through)"
+ or
+ exists(ReturnPosition pos |
+ this = TReturnCtxMaybeFlowThrough(pos) and
+ result = pos.toString()
+ )
+ }
+}
+
+/** An approximated `Content` tagged with the type of a containing object. */
+class TypedContentApprox extends MkTypedContentApprox {
+ private ContentApprox c;
+ private DataFlowType t;
+
+ TypedContentApprox() { this = MkTypedContentApprox(c, t) }
+
+ /** Gets a typed content approximated by this value. */
+ TypedContent getATypedContent() { result = getATypedContent(this) }
+
+ /** Gets the container type. */
+ DataFlowType getContainerType() { result = t }
+
+ /** Gets a textual representation of this approximated content. */
+ string toString() { result = c.toString() }
+}
+
+/**
+ * The front of an approximated access path. This is either a head or a nil.
+ */
+abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
+ abstract string toString();
+
+ abstract DataFlowType getType();
+
+ abstract boolean toBoolNonEmpty();
+
+ pragma[nomagic]
+ TypedContent getAHead() {
+ exists(TypedContentApprox cont |
+ this = TApproxFrontHead(cont) and
+ result = cont.getATypedContent()
+ )
+ }
+}
+
+class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
+ private DataFlowType t;
+
+ ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
+
+ override string toString() { result = ppReprType(t) }
+
+ override DataFlowType getType() { result = t }
+
+ override boolean toBoolNonEmpty() { result = false }
+}
+
+class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
+ private TypedContentApprox tc;
+
+ ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
+
+ override string toString() { result = tc.toString() }
+
+ override DataFlowType getType() { result = tc.getContainerType() }
+
+ override boolean toBoolNonEmpty() { result = true }
+}
+
+/** An optional approximated access path front. */
+class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
+ string toString() {
+ this = TApproxAccessPathFrontNone() and result = ""
+ or
+ this = TApproxAccessPathFrontSome(any(ApproxAccessPathFront apf | result = apf.toString()))
+ }
+}
+
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
@@ -1336,7 +1482,7 @@ abstract class AccessPathFront extends TAccessPathFront {
abstract DataFlowType getType();
- abstract boolean toBoolNonEmpty();
+ abstract ApproxAccessPathFront toApprox();
TypedContent getHead() { this = TFrontHead(result) }
}
@@ -1350,7 +1496,7 @@ class AccessPathFrontNil extends AccessPathFront, TFrontNil {
override DataFlowType getType() { result = t }
- override boolean toBoolNonEmpty() { result = false }
+ override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
}
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
@@ -1362,7 +1508,7 @@ class AccessPathFrontHead extends AccessPathFront, TFrontHead {
override DataFlowType getType() { result = tc.getContainerType() }
- override boolean toBoolNonEmpty() { result = true }
+ override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
}
/** An optional access path front. */
diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll
index dde16ab5a2a..e85e0cd92ec 100644
--- a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll
+++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll
@@ -136,6 +136,18 @@ module Consistency {
msg = "Local flow step does not preserve enclosing callable."
}
+ query predicate readStepIsLocal(Node n1, Node n2, string msg) {
+ readStep(n1, _, n2) and
+ nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
+ msg = "Read step does not preserve enclosing callable."
+ }
+
+ query predicate storeStepIsLocal(Node n1, Node n2, string msg) {
+ storeStep(n1, _, n2) and
+ nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
+ msg = "Store step does not preserve enclosing callable."
+ }
+
private DataFlowType typeRepr() { result = getNodeType(_) }
query predicate compatibleTypesReflexive(DataFlowType t, string msg) {
@@ -232,4 +244,25 @@ module Consistency {
not callable = viableCallable(call) and
not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable)
}
+
+ query predicate uniqueParameterNodeAtPosition(
+ DataFlowCallable c, ParameterPosition pos, Node p, string msg
+ ) {
+ isParameterNode(p, c, pos) and
+ not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
+ msg = "Parameters with overlapping positions."
+ }
+
+ query predicate uniqueParameterNodePosition(
+ DataFlowCallable c, ParameterPosition pos, Node p, string msg
+ ) {
+ isParameterNode(p, c, pos) and
+ not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
+ msg = "Parameter node with multiple positions."
+ }
+
+ query predicate uniqueContentApprox(Content c, string msg) {
+ not exists(unique(ContentApprox approx | approx = getContentApprox(c))) and
+ msg = "Non-unique content approximation."
+ }
}
diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
index 69e08a9a5d2..58569ef2425 100644
--- a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
@@ -551,6 +551,13 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
*/
predicate allowParameterReturnInSelf(ParameterNode p) { none() }
+/** An approximated `Content`. */
+class ContentApprox = Unit;
+
+/** Gets an approximated value for content `c`. */
+pragma[inline]
+ContentApprox getContentApprox(Content c) { any() }
+
private class MyConsistencyConfiguration extends Consistency::ConsistencyConfiguration {
override predicate argHasPostUpdateExclude(ArgumentNode n) {
// The rules for whether an IR argument gets a post-update node are too
diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/semantic/SemanticExprSpecific.qll b/cpp/ql/lib/experimental/semmle/code/cpp/semantic/SemanticExprSpecific.qll
index f7e5abd25b7..f72edf2f806 100644
--- a/cpp/ql/lib/experimental/semmle/code/cpp/semantic/SemanticExprSpecific.qll
+++ b/cpp/ql/lib/experimental/semmle/code/cpp/semantic/SemanticExprSpecific.qll
@@ -292,12 +292,8 @@ module SemanticExprConfig {
final Location getLocation() { result = super.getLocation() }
}
- private class ValueNumberBound extends Bound {
- IRBound::ValueNumberBound bound;
-
- ValueNumberBound() { bound = this }
-
- override string toString() { result = bound.toString() }
+ private class ValueNumberBound extends Bound instanceof IRBound::ValueNumberBound {
+ override string toString() { result = IRBound::ValueNumberBound.super.toString() }
}
predicate zeroBound(Bound bound) { bound instanceof IRBound::ZeroBound }
diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/SignAnalysisCommon.qll b/cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/SignAnalysisCommon.qll
index 27c3083fecc..970a6c4e5a9 100644
--- a/cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/SignAnalysisCommon.qll
+++ b/cpp/ql/lib/experimental/semmle/code/cpp/semantic/analysis/SignAnalysisCommon.qll
@@ -33,23 +33,15 @@ abstract private class FlowSignDef extends SignDef {
}
/** An SSA definition whose sign is determined by the sign of that definitions source expression. */
-private class ExplicitSignDef extends FlowSignDef {
- SemSsaExplicitUpdate update;
-
- ExplicitSignDef() { update = this }
-
- final override Sign getSign() { result = semExprSign(update.getSourceExpr()) }
+private class ExplicitSignDef extends FlowSignDef instanceof SemSsaExplicitUpdate {
+ final override Sign getSign() { result = semExprSign(super.getSourceExpr()) }
}
/** An SSA Phi definition, whose sign is the union of the signs of its inputs. */
-private class PhiSignDef extends FlowSignDef {
- SemSsaPhiNode phi;
-
- PhiSignDef() { phi = this }
-
+private class PhiSignDef extends FlowSignDef instanceof SemSsaPhiNode {
final override Sign getSign() {
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
- edge.phiInput(phi, inp) and
+ edge.phiInput(this, inp) and
result = semSsaSign(inp, edge)
)
}
diff --git a/cpp/ql/lib/qlpack.yml b/cpp/ql/lib/qlpack.yml
index ef1fd2099a3..ee8913624dc 100644
--- a/cpp/ql/lib/qlpack.yml
+++ b/cpp/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/cpp-all
-version: 0.4.4-dev
+version: 0.5.0-dev
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp
diff --git a/cpp/ql/lib/semmle/code/cpp/File.qll b/cpp/ql/lib/semmle/code/cpp/File.qll
index e58467fac20..b2e4e0a41a5 100644
--- a/cpp/ql/lib/semmle/code/cpp/File.qll
+++ b/cpp/ql/lib/semmle/code/cpp/File.qll
@@ -189,18 +189,6 @@ class Folder extends Container, @folder {
* Gets the URL of this folder.
*/
deprecated override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
-
- /**
- * DEPRECATED: use `getAbsolutePath` instead.
- * Gets the name of this folder.
- */
- deprecated string getName() { folders(underlyingElement(this), result) }
-
- /**
- * DEPRECATED: use `getBaseName` instead.
- * Gets the last part of the folder name.
- */
- deprecated string getShortName() { result = this.getBaseName() }
}
/**
diff --git a/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll b/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll
index d063e8fa19c..51d2e294b36 100644
--- a/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll
+++ b/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll
@@ -397,11 +397,8 @@ private int lengthInBase16(float f) {
/**
* A class to represent format strings that occur as arguments to invocations of formatting functions.
*/
-class FormatLiteral extends Literal {
- FormatLiteral() {
- exists(FormattingFunctionCall ffc | ffc.getFormat() = this) and
- this instanceof StringLiteral
- }
+class FormatLiteral extends Literal instanceof StringLiteral {
+ FormatLiteral() { exists(FormattingFunctionCall ffc | ffc.getFormat() = this) }
/**
* Gets the function call where this format string is used.
diff --git a/cpp/ql/lib/semmle/code/cpp/commons/Scanf.qll b/cpp/ql/lib/semmle/code/cpp/commons/Scanf.qll
index 0d51c948170..376829c2eef 100644
--- a/cpp/ql/lib/semmle/code/cpp/commons/Scanf.qll
+++ b/cpp/ql/lib/semmle/code/cpp/commons/Scanf.qll
@@ -30,15 +30,12 @@ abstract class ScanfFunction extends Function {
/**
* The standard function `scanf` (and variations).
*/
-class Scanf extends ScanfFunction {
+class Scanf extends ScanfFunction instanceof TopLevelFunction {
Scanf() {
- this instanceof TopLevelFunction and
- (
- this.hasGlobalOrStdOrBslName("scanf") or // scanf(format, args...)
- this.hasGlobalOrStdOrBslName("wscanf") or // wscanf(format, args...)
- this.hasGlobalName("_scanf_l") or // _scanf_l(format, locale, args...)
- this.hasGlobalName("_wscanf_l") // _wscanf_l(format, locale, args...)
- )
+ this.hasGlobalOrStdOrBslName("scanf") or // scanf(format, args...)
+ this.hasGlobalOrStdOrBslName("wscanf") or // wscanf(format, args...)
+ this.hasGlobalName("_scanf_l") or // _scanf_l(format, locale, args...)
+ this.hasGlobalName("_wscanf_l")
}
override int getInputParameterIndex() { none() }
@@ -49,15 +46,12 @@ class Scanf extends ScanfFunction {
/**
* The standard function `fscanf` (and variations).
*/
-class Fscanf extends ScanfFunction {
+class Fscanf extends ScanfFunction instanceof TopLevelFunction {
Fscanf() {
- this instanceof TopLevelFunction and
- (
- this.hasGlobalOrStdOrBslName("fscanf") or // fscanf(src_stream, format, args...)
- this.hasGlobalOrStdOrBslName("fwscanf") or // fwscanf(src_stream, format, args...)
- this.hasGlobalName("_fscanf_l") or // _fscanf_l(src_stream, format, locale, args...)
- this.hasGlobalName("_fwscanf_l") // _fwscanf_l(src_stream, format, locale, args...)
- )
+ this.hasGlobalOrStdOrBslName("fscanf") or // fscanf(src_stream, format, args...)
+ this.hasGlobalOrStdOrBslName("fwscanf") or // fwscanf(src_stream, format, args...)
+ this.hasGlobalName("_fscanf_l") or // _fscanf_l(src_stream, format, locale, args...)
+ this.hasGlobalName("_fwscanf_l")
}
override int getInputParameterIndex() { result = 0 }
@@ -68,15 +62,12 @@ class Fscanf extends ScanfFunction {
/**
* The standard function `sscanf` (and variations).
*/
-class Sscanf extends ScanfFunction {
+class Sscanf extends ScanfFunction instanceof TopLevelFunction {
Sscanf() {
- this instanceof TopLevelFunction and
- (
- this.hasGlobalOrStdOrBslName("sscanf") or // sscanf(src_stream, format, args...)
- this.hasGlobalOrStdOrBslName("swscanf") or // swscanf(src, format, args...)
- this.hasGlobalName("_sscanf_l") or // _sscanf_l(src, format, locale, args...)
- this.hasGlobalName("_swscanf_l") // _swscanf_l(src, format, locale, args...)
- )
+ this.hasGlobalOrStdOrBslName("sscanf") or // sscanf(src_stream, format, args...)
+ this.hasGlobalOrStdOrBslName("swscanf") or // swscanf(src, format, args...)
+ this.hasGlobalName("_sscanf_l") or // _sscanf_l(src, format, locale, args...)
+ this.hasGlobalName("_swscanf_l")
}
override int getInputParameterIndex() { result = 0 }
@@ -87,17 +78,12 @@ class Sscanf extends ScanfFunction {
/**
* The standard(ish) function `snscanf` (and variations).
*/
-class Snscanf extends ScanfFunction {
+class Snscanf extends ScanfFunction instanceof TopLevelFunction {
Snscanf() {
- this instanceof TopLevelFunction and
- (
- this.hasGlobalName("_snscanf") or // _snscanf(src, max_amount, format, args...)
- this.hasGlobalName("_snwscanf") or // _snwscanf(src, max_amount, format, args...)
- this.hasGlobalName("_snscanf_l") or // _snscanf_l(src, max_amount, format, locale, args...)
- this.hasGlobalName("_snwscanf_l") // _snwscanf_l(src, max_amount, format, locale, args...)
- // note that the max_amount is not a limit on the output length, it's an input length
- // limit used with non null-terminated strings.
- )
+ this.hasGlobalName("_snscanf") or // _snscanf(src, max_amount, format, args...)
+ this.hasGlobalName("_snwscanf") or // _snwscanf(src, max_amount, format, args...)
+ this.hasGlobalName("_snscanf_l") or // _snscanf_l(src, max_amount, format, locale, args...)
+ this.hasGlobalName("_snwscanf_l")
}
override int getInputParameterIndex() { result = 0 }
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
index 2dde1c2819d..1228d00b6ba 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
- predicate isSink(Node source, FlowState state) { none() }
+ predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/
FlowFeature getAFeature() { none() }
+ /** Holds if sources should be grouped in the result of `hasFlowPath`. */
+ predicate sourceGrouping(Node source, string sourceGroup) { none() }
+
+ /** Holds if sinks should be grouped in the result of `hasFlowPath`. */
+ predicate sinkGrouping(Node sink, string sinkGroup) { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
* The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`.
*/
- predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
+ predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
/**
* Holds if data may flow from some source to `sink` for this configuration.
@@ -313,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -602,8 +606,27 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
+/**
+ * Holds if flow from `p` to a return node of kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[p, kind]
+private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
+ exists(ParameterPosition pos | p.isParameterOf(_, pos) |
+ not kind.(ParamUpdateReturnKind).getPosition() = pos
+ or
+ allowParameterReturnInSelfCached(p.asNode())
+ )
+}
+
private module Stage1 implements StageSig {
- class Ap = Unit;
+ class Ap extends int {
+ // workaround for bad functionality-induced joins (happens when using `Unit`)
+ pragma[nomagic]
+ Ap() { this in [0 .. 1] and this < 1 }
+ }
private class Cc = boolean;
@@ -944,6 +967,12 @@ private module Stage1 implements StageSig {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
+ pragma[nomagic]
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
+ revFlow(node, config) and
+ exists(ap)
+ }
+
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -975,21 +1004,26 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnKindExt kind |
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
+ exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
- or
- p.allowParameterReturnInSelf()
- )
+ parameterFlowThroughAllowed(p, kind)
)
}
+ pragma[nomagic]
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ ) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind() and
+ exists(argAp) and
+ exists(ap)
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1046,12 +1080,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
- viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
- Stage1::revFlow(ret, config) and
- not outBarrier(ret, config) and
- not inBarrier(out, config)
+ exists(ReturnPosition pos |
+ viableReturnPosOutNodeCand1(call, pos, out, config) and
+ pos = ret.getReturnPosition() and
+ kind = pos.getKind() and
+ Stage1::revFlow(ret, config) and
+ not outBarrier(ret, config) and
+ not inBarrier(out, config)
+ )
}
pragma[nomagic]
@@ -1081,10 +1119,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
+pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
- flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
+ flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1093,10 +1132,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
+pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
- flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
+ flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1109,12 +1149,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
) {
- flowOutOfCallNodeCand1(call, ret, out, config) and
+ flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
- b = branch(ret, config) and
- j = join(out, config) and
+ b = branch(ret, pragma[only_bind_into](config)) and
+ j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1130,10 +1171,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- flowIntoCallNodeCand1(call, arg, p, config) and
+ flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
- b = branch(arg, config) and
- j = join(p, config) and
+ b = branch(arg, pragma[only_bind_into](config)) and
+ j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1145,12 +1186,18 @@ private signature module StageSig {
predicate revFlow(NodeEx node, Configuration config);
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
+
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
+
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ );
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1216,7 +1263,8 @@ private module MkStage {
);
predicate flowOutOfCall(
- DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
);
predicate flowIntoCall(
@@ -1234,21 +1282,43 @@ private module MkStage {
import Param
/* Begin: Stage logic. */
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
+ PrevStage::revFlowAp(node, apa, config)
+ }
+
+ pragma[nomagic]
+ private predicate flowIntoCallApa(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
+ Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate flowOutOfCallApa(
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ ApApprox apa, Configuration config
+ ) {
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
+ PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ ApApprox argApa, ApApprox apa, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- matchesCall(ccc, call)
+ exists(ReturnKindExt kind |
+ flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ )
}
/**
@@ -1256,99 +1326,134 @@ private module MkStage {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
- * argument in a call, and if so, `argAp` records the access path of that
- * argument.
+ * argument in a call, and if so, `summaryCtx` and `argAp` record the
+ * corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
- NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ ApApprox apa, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
+ PrevStage::revFlow(node, state, apa, config) and
filter(node, state, ap, config)
}
+ pragma[inline]
+ additional predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ Configuration config
+ ) {
+ fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
+ }
+
pragma[nomagic]
private predicate fwdFlow0(
- NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ ApApprox apa, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
- ap = getApNil(node)
+ summaryCtx = TParamNodeNone() and
+ ap = getApNil(node) and
+ apa = getApprox(ap)
or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
+ ap = ap0 and
+ apa = apa0
or
localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
+ ap0 instanceof ApNil and
+ apa = getApprox(ap)
)
or
exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone() and
- ap = getApNil(node)
+ ap = getApNil(node) and
+ apa = getApprox(ap)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
+ fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone() and
- ap = getApNil(node)
+ ap = getApNil(node) and
+ apa = getApprox(ap)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
+ ap = apCons(tc, ap0) and
+ apa = getApprox(ap)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config) and
+ apa = getApprox(ap)
)
or
// flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParamNodeSome(node.asNode()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParamNodeNone() and argAp = apNone()
)
or
// flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
+ flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
+ inner = ret.getEnclosingCallable() and
+ cc = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ // flow through a callable
+ exists(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, boolean allowsFieldFlow, ApApprox innerArgApa
+ |
+ fwdFlowThrough(call, cc, state, ccc, summaryCtx, argAp, ap, apa, ret, innerArgApa, config) and
+ flowThroughOutOfCall(call, ccc, ret, node, allowsFieldFlow, innerArgApa, apa, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
+ ParamNodeOption summaryCtx, ApOption argAp, Configuration config
) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ exists(DataFlowType contentType, ApApprox apa1 |
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
+ PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1360,60 +1465,85 @@ private module MkStage {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
}
+ private class ApNonNil instanceof Ap {
+ pragma[nomagic]
+ ApNonNil() { not this instanceof ApNil }
+
+ string toString() { result = "" }
+ }
+
pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ private predicate fwdFlowRead0(
+ NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
Configuration config
) {
- fwdFlow(node1, state, cc, argAp, ap, config) and
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
+ PrevStage::readStepCand(node1, _, _, config)
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
+ ParamNodeOption summaryCtx, ApOption argAp, Configuration config
+ ) {
+ fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
- Ap ap, Configuration config
+ DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
+ ParamNodeOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, argAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
+ flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowRetFromArg(
+ RetNodeEx ret, FlowState state, CcCall ccc, ParamNodeEx summaryCtx, Ap argAp, ApApprox argApa,
+ Ap ap, ApApprox apa, Configuration config
) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
+ exists(ReturnKindExt kind |
+ fwdFlow(pragma[only_bind_into](ret), state, ccc,
+ TParamNodeSome(pragma[only_bind_into](summaryCtx.asNode())),
+ pragma[only_bind_into](apSome(argAp)), ap, pragma[only_bind_into](apa),
+ pragma[only_bind_into](config)) and
+ kind = ret.getKind() and
+ parameterFlowThroughAllowed(summaryCtx, kind) and
+ argApa = getApprox(argAp) and
+ PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config))
)
}
- pragma[nomagic]
- private predicate fwdFlowOutFromArg(
- DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
+ pragma[inline]
+ private predicate fwdFlowThrough0(
+ DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx,
+ ApOption argAp, Ap ap, ApApprox apa, RetNodeEx ret, ParamNodeEx innerSummaryCtx,
+ Ap innerArgAp, ApApprox innerArgApa, Configuration config
) {
- exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
- fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
- flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ fwdFlowRetFromArg(ret, state, ccc, innerSummaryCtx, innerArgAp, innerArgApa, ap, apa, config) and
+ fwdFlowIsEntered(call, cc, ccc, summaryCtx, argAp, innerSummaryCtx, innerArgAp, config)
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowThrough(
+ DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx,
+ ApOption argAp, Ap ap, ApApprox apa, RetNodeEx ret, ApApprox innerArgApa, Configuration config
+ ) {
+ fwdFlowThrough0(call, cc, state, ccc, summaryCtx, argAp, ap, apa, ret, _, _, innerArgApa,
+ config)
}
/**
@@ -1422,11 +1552,14 @@ private module MkStage {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ DataFlowCall call, Cc cc, CcCall innerCc, ParamNodeOption summaryCtx, ApOption argAp,
+ ParamNodeEx p, Ap ap, Configuration config
) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ exists(ApApprox apa |
+ fwdFlowIn(call, pragma[only_bind_into](p), _, cc, innerCc, summaryCtx, argAp, ap,
+ pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, apa, config) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config))
)
}
@@ -1434,146 +1567,194 @@ private module MkStage {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
- fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
+ fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
- fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
+ fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
+ private predicate returnFlowsThrough0(
+ DataFlowCall call, FlowState state, CcCall ccc, Ap ap, ApApprox apa, RetNodeEx ret,
+ ParamNodeEx innerSummaryCtx, Ap innerArgAp, ApApprox innerArgApa, Configuration config
+ ) {
+ fwdFlowThrough0(call, _, state, ccc, _, _, ap, apa, ret, innerSummaryCtx, innerArgAp,
+ innerArgApa, config)
+ }
+
+ pragma[nomagic]
+ private predicate returnFlowsThrough(
+ RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
+ Ap ap, Configuration config
+ ) {
+ exists(DataFlowCall call, ApApprox apa, boolean allowsFieldFlow, ApApprox innerArgApa |
+ returnFlowsThrough0(call, state, ccc, ap, apa, ret, p, argAp, innerArgApa, config) and
+ flowThroughOutOfCall(call, ccc, ret, _, allowsFieldFlow, innerArgApa, apa, config) and
+ pos = ret.getReturnPosition() and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp, Ap ap,
+ 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))
+ exists(ApApprox argApa |
+ flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
+ allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
+ pragma[only_bind_into](config)) and
+ returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ if allowsFieldFlow = false then argAp instanceof ApNil else any()
+ )
}
pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ private predicate flowIntoCallAp(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
+ Configuration config
) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
+ exists(ApApprox apa |
+ flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
+ fwdFlow(arg, _, _, _, _, ap, apa, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowOutOfCallAp(
+ DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow,
+ Ap ap, Configuration config
+ ) {
+ exists(ApApprox apa |
+ flowOutOfCallApa(call, ret, _, out, allowsFieldFlow, apa, config) and
+ fwdFlow(ret, _, _, _, _, ap, apa, config) and
+ pos = ret.getReturnPosition()
+ )
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
- * The Boolean `toReturn` records whether the node must be returned from the
- * enclosing callable in order to reach a sink, and if so, `returnAp` records
- * the access path of the returned value.
+ * The parameter `returnCtx` records whether (and how) the node must be returned
+ * from the enclosing callable in order to reach a sink, and if so, `returnAp`
+ * records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
+ Configuration config
) {
- revFlow0(node, state, toReturn, returnAp, ap, config) and
- fwdFlow(node, state, _, _, ap, config)
+ revFlow0(node, state, returnCtx, returnAp, ap, config) and
+ fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
+ Configuration config
) {
- fwdFlow(node, state, _, _, ap, config) and
+ fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
+ (
+ if hasSinkCallCtx(config)
+ then returnCtx = TReturnCtxNoFlowThrough()
+ else returnCtx = TReturnCtxNone()
+ ) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
- revFlow(mid, state0, toReturn, returnAp, ap, config)
+ revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
- revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
+ revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
- fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
+ flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ returnCtx = TReturnCtxNone()
+ )
or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ // flow through a callable
+ exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
+ revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
+ flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
)
or
// flow out of a callable
- revFlowOut(_, node, state, _, _, ap, config) and
- toReturn = true and
- if returnNodeMayFlowThrough(node, state, ap, config)
- then returnAp = apSome(ap)
- else returnAp = apNone()
+ exists(ReturnPosition pos |
+ revFlowOut(_, node, pos, state, _, _, ap, config) and
+ if returnFlowsThrough(node, pos, state, _, _, _, ap, config)
+ then (
+ returnCtx = TReturnCtxMaybeFlowThrough(pos) and
+ returnAp = apSome(ap)
+ ) else (
+ returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
+ )
+ )
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
- boolean toReturn, ApOption returnAp, Configuration config
+ ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1593,36 +1774,33 @@ private module MkStage {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx,
+ ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
- revFlow(out, state, toReturn, returnAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ revFlow(out, state, returnCtx, returnAp, ap, config) and
+ flowOutOfCallAp(call, ret, pos, out, allowsFieldFlow, ap, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate revFlowInNotToReturn(
- ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
+ private predicate revFlowParamToReturn(
+ ParamNodeEx p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap, Configuration config
) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, false, returnAp, ap, config) and
- flowIntoCall(_, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp),
+ pragma[only_bind_into](ap), pragma[only_bind_into](config)) and
+ parameterFlowThroughAllowed(p, pos.getKind()) and
+ PrevStage::parameterMayFlowThrough(p, getApprox(ap), config)
}
pragma[nomagic]
- private predicate revFlowInToReturn(
- DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
+ private predicate revFlowThrough(
+ DataFlowCall call, ReturnCtx returnCtx, ParamNodeEx p, FlowState state, ReturnPosition pos,
+ ApOption returnAp, Ap ap, Ap innerReturnAp, Configuration config
) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ revFlowParamToReturn(p, state, pos, innerReturnAp, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, pos, innerReturnAp, config)
}
/**
@@ -1632,11 +1810,12 @@ private module MkStage {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap,
+ Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
- revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
- fwdFlow(ret, state, ccc, apSome(_), ap, config) and
+ revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and
+ returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1673,6 +1852,11 @@ private module MkStage {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+ pragma[nomagic]
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
+ revFlow(node, _, _, _, ap, config)
+ }
+
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) {
@@ -1707,40 +1891,49 @@ private module MkStage {
validAp(ap, config)
}
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ pragma[nomagic]
+ private predicate parameterFlowsThroughRev(
+ ParamNodeEx p, Ap ap, ReturnPosition pos, Ap returnAp, Configuration config
) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap, config) and
+ parameterFlowThroughAllowed(p, pos.getKind())
}
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
- parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
- revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
- pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
- fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
- kind = ret.getKind() and
- p.getPosition() = pos and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = pos
- or
- p.allowParameterReturnInSelf()
- )
+ pragma[nomagic]
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, ReturnPosition pos |
+ returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, pos, _, config)
+ )
+ }
+
+ pragma[nomagic]
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ ) {
+ exists(ParamNodeEx p, ReturnPosition pos |
+ returnFlowsThrough(ret, pos, _, _, p, argAp, ap, config) and
+ parameterFlowsThroughRev(p, argAp, pos, ap, config) and
+ kind = pos.getKind()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowThroughArg(
+ DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
+ Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
+ revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
+ flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
+ revFlow(arg, state, returnCtx, returnAp, ap, config) and
+ revFlowThroughArg(call, arg, state, returnCtx, returnAp, ap, config)
)
}
@@ -1748,13 +1941,13 @@ private module MkStage {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
- nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
+ states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
+ count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)
)
or
fwd = false and
@@ -1763,8 +1956,8 @@ private module MkStage {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
- count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
- revFlow(n, state, b, retAp, ap, config)
+ count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
+ revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1909,7 +2102,7 @@ private module Stage2Param implements MkStage::StageParam {
exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1945,9 +2138,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
- DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
) {
- flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
+ flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2015,8 +2209,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
- flowIntoCallNodeCand1(_, node, next, config) or
- flowOutOfCallNodeCand1(_, node, next, config) or
+ flowIntoCallNodeCand2(_, node, next, _, config) or
+ flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2103,16 +2297,16 @@ private module LocalFlowBigStep {
pragma[nomagic]
predicate localFlowBigStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
- AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
+ DataFlowType t, Configuration config, LocalCallContext callContext
) {
- localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
+ localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
localFlowExit(node2, state1, config) and
state1 = state2
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
state1 != state2 and
preservesValue = false and
- apf = TFrontNil(node2.getDataFlowType()) and
+ t = node2.getDataFlowType() and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
@@ -2126,11 +2320,87 @@ private import LocalFlowBigStep
private module Stage3Param implements MkStage::StageParam {
private module PrevStage = Stage2;
+ class Ap = ApproxAccessPathFront;
+
+ class ApNil = ApproxAccessPathFrontNil;
+
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+
+ ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
+ }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
+
+ pragma[noinline]
+ Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
+
+ class ApOption = ApproxAccessPathFrontOption;
+
+ ApOption apNone() { result = TApproxAccessPathFrontNone() }
+
+ ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
+
+ import BooleanCallContext
+
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
+ ) {
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
+ exists(lcc)
+ }
+
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
+
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
+
+ pragma[nomagic]
+ private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
+ exists(Content c |
+ PrevStage::revFlow(node, pragma[only_bind_into](config)) and
+ PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
+ expectsContentEx(node, c) and
+ c = ap.getAHead().getContent()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ exists(state) and
+ exists(config) and
+ (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
+ (
+ notExpectsContent(node)
+ or
+ expectsContentCand(node, ap, config)
+ )
+ }
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ // We need to typecheck stores here, since reverse flow through a getter
+ // might have a different type here compared to inside the getter.
+ compatibleTypes(ap.getType(), contentType)
+ }
+}
+
+private module Stage3 implements StageSig {
+ import MkStage::Stage
+}
+
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
+
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
@@ -2150,16 +2420,42 @@ private module Stage3Param implements MkStage::StageParam {
import BooleanCallContext
+ pragma[nomagic]
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
- localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
+ PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
+ exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ pragma[nomagic]
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ exists(FlowState state |
+ flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
+ pragma[only_bind_into](config))
+ )
+ }
- predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ pragma[nomagic]
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ exists(FlowState state |
+ flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
+ pragma[only_bind_into](config))
+ )
+ }
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2215,8 +2511,8 @@ private module Stage3Param implements MkStage::StageParam {
}
}
-private module Stage3 implements StageSig {
- import MkStage::Stage
+private module Stage4 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2227,8 +2523,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
- Stage3::revFlow(node, state, true, _, apf, config) and
- Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
+ Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
+ Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
+ config)
)
}
@@ -2238,10 +2535,10 @@ private predicate flowCandSummaryCtx(
*/
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
exists(int tails, int nodes, int apLimit, int tupleLimit |
- tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
+ tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2255,11 +2552,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
private newtype TAccessPathApprox =
TNil(DataFlowType t) or
TConsNil(TypedContent tc, DataFlowType t) {
- Stage3::consCand(tc, TFrontNil(t), _) and
+ Stage4::consCand(tc, TFrontNil(t), _) and
not expensiveLen2unfolding(tc, _)
} or
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
- Stage3::consCand(tc1, TFrontHead(tc2), _) and
+ Stage4::consCand(tc1, TFrontHead(tc2), _) and
len in [2 .. accessPathLimit()] and
not expensiveLen2unfolding(tc1, _)
} or
@@ -2389,7 +2686,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
override AccessPathApprox pop(TypedContent head) {
head = tc and
(
- exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
+ exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
result = TConsCons(tc2, _, len - 1)
or
len = 2 and
@@ -2400,7 +2697,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
or
exists(DataFlowType t |
len = 1 and
- Stage3::consCand(tc, TFrontNil(t), _) and
+ Stage4::consCand(tc, TFrontNil(t), _) and
result = TNil(t)
)
)
@@ -2425,13 +2722,14 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4Param implements MkStage::StageParam {
- private module PrevStage = Stage3;
+private module Stage5Param implements MkStage::StageParam {
+ private module PrevStage = Stage4;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
+ pragma[nomagic]
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
ApNil getApNil(NodeEx node) {
@@ -2457,15 +2755,18 @@ private module Stage4Param implements MkStage::StageParam {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
- localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
+ PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
}
pragma[nomagic]
predicate flowOutOfCall(
- DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
) {
exists(FlowState state |
- flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
+ flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2493,7 +2794,7 @@ private module Stage4Param implements MkStage::StageParam {
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
-private module Stage4 = MkStage::Stage;
+private module Stage5 = MkStage::Stage;
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
@@ -2502,13 +2803,13 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
- NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
+ NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(AccessPathApprox apa0 |
- Stage4::parameterMayFlowThrough(_, c, _, _) and
- Stage4::revFlow(n, state, true, _, apa0, config) and
- Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
- n.getEnclosingCallable() = c
+ Stage5::parameterMayFlowThrough(p, _, _) and
+ Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
+ Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()),
+ TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2516,9 +2817,9 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
- exists(DataFlowCallable c |
- Stage4::parameterMayFlowThrough(_, c, apa, config) and
- nodeMayUseSummary0(n, c, state, apa, config)
+ exists(ParamNodeEx p |
+ Stage5::parameterMayFlowThrough(p, apa, config) and
+ nodeMayUseSummary0(n, p, state, apa, config)
)
}
@@ -2526,8 +2827,8 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
- Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, config)
+ Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
+ Stage5::revFlow(p, state, _, config)
)
}
@@ -2576,7 +2877,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
len = apa.len() and
result =
strictcount(AccessPathFront apf |
- Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
+ Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
config)
)
)
@@ -2585,7 +2886,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -2606,7 +2907,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
exists(TypedContent head |
apa.pop(head) = result and
- Stage4::consCand(head, result, config)
+ Stage5::consCand(head, result, config)
)
}
@@ -2688,7 +2989,7 @@ private newtype TPathNode =
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) {
// A PathNode is introduced by a source ...
- Stage4::revFlow(node, state, config) and
+ Stage5::revFlow(node, state, config) and
sourceNode(node, state, config) and
(
if hasSourceCallCtx(config)
@@ -2702,7 +3003,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
+ Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -2712,6 +3013,18 @@ private newtype TPathNode =
state = sink.getState() and
config = sink.getConfiguration()
)
+ } or
+ TPathNodeSourceGroup(string sourceGroup, Configuration config) {
+ exists(PathNodeImpl source |
+ sourceGroup = source.getSourceGroup() and
+ config = source.getConfiguration()
+ )
+ } or
+ TPathNodeSinkGroup(string sinkGroup, Configuration config) {
+ exists(PathNodeSink sink |
+ sinkGroup = sink.getSinkGroup() and
+ config = sink.getConfiguration()
+ )
}
/**
@@ -2832,7 +3145,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
override TypedContent getHead() { result = head1 }
override AccessPath getTail() {
- Stage4::consCand(head1, result.getApprox(), _) and
+ Stage5::consCand(head1, result.getApprox(), _) and
result.getHead() = head2 and
result.length() = len - 1
}
@@ -2863,7 +3176,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
override TypedContent getHead() { result = head }
override AccessPath getTail() {
- Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
+ Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
}
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
@@ -2920,6 +3233,22 @@ abstract private class PathNodeImpl extends TPathNode {
)
}
+ string getSourceGroup() {
+ this.isSource() and
+ this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result)
+ }
+
+ predicate isFlowSource() {
+ this.isSource() and not exists(this.getSourceGroup())
+ or
+ this instanceof PathNodeSourceGroup
+ }
+
+ predicate isFlowSink() {
+ this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or
+ this instanceof PathNodeSinkGroup
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2959,7 +3288,9 @@ abstract private class PathNodeImpl extends TPathNode {
/** Holds if `n` can reach a sink. */
private predicate directReach(PathNodeImpl n) {
- n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
+ n instanceof PathNodeSink or
+ n instanceof PathNodeSinkGroup or
+ directReach(n.getANonHiddenSuccessor())
}
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
@@ -3015,6 +3346,12 @@ class PathNode instanceof PathNodeImpl {
/** Holds if this node is a source. */
final predicate isSource() { super.isSource() }
+
+ /** Holds if this node is a grouping of source nodes. */
+ final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) }
+
+ /** Holds if this node is a grouping of sink nodes. */
+ final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) }
}
/**
@@ -3136,9 +3473,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNodeImpl getASuccessorImpl() { none() }
+ override PathNodeImpl getASuccessorImpl() {
+ result = TPathNodeSinkGroup(this.getSinkGroup(), config)
+ }
override predicate isSource() { sourceNode(node, state, config) }
+
+ string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
+}
+
+private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
+ string sourceGroup;
+ Configuration config;
+
+ PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
+
+ override NodeEx getNodeEx() { none() }
+
+ override FlowState getState() { none() }
+
+ override Configuration getConfiguration() { result = config }
+
+ override PathNodeImpl getASuccessorImpl() {
+ result.getSourceGroup() = sourceGroup and
+ result.getConfiguration() = config
+ }
+
+ override predicate isSource() { none() }
+
+ override string toString() { result = sourceGroup }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
+ }
+}
+
+private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
+ string sinkGroup;
+ Configuration config;
+
+ PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
+
+ override NodeEx getNodeEx() { none() }
+
+ override FlowState getState() { none() }
+
+ override Configuration getConfiguration() { result = config }
+
+ override PathNodeImpl getASuccessorImpl() { none() }
+
+ override predicate isSource() { none() }
+
+ override string toString() { result = sinkGroup }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
+ }
}
private predicate pathNode(
@@ -3174,7 +3568,8 @@ private predicate pathStep(
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
- localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
+ localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
+ localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -3216,7 +3611,7 @@ private predicate pathReadStep(
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
- Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
+ Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3226,7 +3621,7 @@ private predicate pathStoreStep(
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
+ Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3263,7 +3658,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, apa, config)
+ Stage5::revFlow(result, _, apa, config)
}
/**
@@ -3299,7 +3694,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, apa, config) and
+ Stage5::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
@@ -3354,17 +3749,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
- exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
+ exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
- pos = sc.getParameterPos() and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = pos
- or
- sc.getParamNode().allowParameterReturnInSelf()
- )
+ parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}
@@ -3495,6 +3884,15 @@ private module Subpaths {
* Will only have results if `configuration` has non-empty sources and
* sinks.
*/
+private predicate hasFlowPath(
+ PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration
+) {
+ flowsource.isFlowSource() and
+ flowsource.getConfiguration() = configuration and
+ (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
+ flowsink.isFlowSink()
+}
+
private predicate flowsTo(
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink,
Configuration configuration
@@ -3575,9 +3973,17 @@ predicate stageStats(
n = 45 and
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
or
- stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
+ stage = "5 Fwd" and
+ n = 50 and
+ Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
or
- stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
+ stage = "5 Rev" and
+ n = 55 and
+ Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
+ or
+ stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
+ or
+ stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
}
private module FlowExploration {
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
index 2dde1c2819d..1228d00b6ba 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
- predicate isSink(Node source, FlowState state) { none() }
+ predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/
FlowFeature getAFeature() { none() }
+ /** Holds if sources should be grouped in the result of `hasFlowPath`. */
+ predicate sourceGrouping(Node source, string sourceGroup) { none() }
+
+ /** Holds if sinks should be grouped in the result of `hasFlowPath`. */
+ predicate sinkGrouping(Node sink, string sinkGroup) { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
* The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`.
*/
- predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
+ predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
/**
* Holds if data may flow from some source to `sink` for this configuration.
@@ -313,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -602,8 +606,27 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
+/**
+ * Holds if flow from `p` to a return node of kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[p, kind]
+private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
+ exists(ParameterPosition pos | p.isParameterOf(_, pos) |
+ not kind.(ParamUpdateReturnKind).getPosition() = pos
+ or
+ allowParameterReturnInSelfCached(p.asNode())
+ )
+}
+
private module Stage1 implements StageSig {
- class Ap = Unit;
+ class Ap extends int {
+ // workaround for bad functionality-induced joins (happens when using `Unit`)
+ pragma[nomagic]
+ Ap() { this in [0 .. 1] and this < 1 }
+ }
private class Cc = boolean;
@@ -944,6 +967,12 @@ private module Stage1 implements StageSig {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
+ pragma[nomagic]
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
+ revFlow(node, config) and
+ exists(ap)
+ }
+
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -975,21 +1004,26 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnKindExt kind |
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
+ exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
- or
- p.allowParameterReturnInSelf()
- )
+ parameterFlowThroughAllowed(p, kind)
)
}
+ pragma[nomagic]
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ ) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind() and
+ exists(argAp) and
+ exists(ap)
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1046,12 +1080,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
- viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
- Stage1::revFlow(ret, config) and
- not outBarrier(ret, config) and
- not inBarrier(out, config)
+ exists(ReturnPosition pos |
+ viableReturnPosOutNodeCand1(call, pos, out, config) and
+ pos = ret.getReturnPosition() and
+ kind = pos.getKind() and
+ Stage1::revFlow(ret, config) and
+ not outBarrier(ret, config) and
+ not inBarrier(out, config)
+ )
}
pragma[nomagic]
@@ -1081,10 +1119,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
+pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
- flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
+ flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1093,10 +1132,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
+pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
- flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
+ flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1109,12 +1149,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
) {
- flowOutOfCallNodeCand1(call, ret, out, config) and
+ flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
- b = branch(ret, config) and
- j = join(out, config) and
+ b = branch(ret, pragma[only_bind_into](config)) and
+ j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1130,10 +1171,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- flowIntoCallNodeCand1(call, arg, p, config) and
+ flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
- b = branch(arg, config) and
- j = join(p, config) and
+ b = branch(arg, pragma[only_bind_into](config)) and
+ j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1145,12 +1186,18 @@ private signature module StageSig {
predicate revFlow(NodeEx node, Configuration config);
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
+
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
+
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ );
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1216,7 +1263,8 @@ private module MkStage {
);
predicate flowOutOfCall(
- DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
);
predicate flowIntoCall(
@@ -1234,21 +1282,43 @@ private module MkStage {
import Param
/* Begin: Stage logic. */
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
+ PrevStage::revFlowAp(node, apa, config)
+ }
+
+ pragma[nomagic]
+ private predicate flowIntoCallApa(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
+ Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate flowOutOfCallApa(
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ ApApprox apa, Configuration config
+ ) {
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
+ PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ ApApprox argApa, ApApprox apa, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- matchesCall(ccc, call)
+ exists(ReturnKindExt kind |
+ flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ )
}
/**
@@ -1256,99 +1326,134 @@ private module MkStage {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
- * argument in a call, and if so, `argAp` records the access path of that
- * argument.
+ * argument in a call, and if so, `summaryCtx` and `argAp` record the
+ * corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
- NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ ApApprox apa, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
+ PrevStage::revFlow(node, state, apa, config) and
filter(node, state, ap, config)
}
+ pragma[inline]
+ additional predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ Configuration config
+ ) {
+ fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
+ }
+
pragma[nomagic]
private predicate fwdFlow0(
- NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ ApApprox apa, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
- ap = getApNil(node)
+ summaryCtx = TParamNodeNone() and
+ ap = getApNil(node) and
+ apa = getApprox(ap)
or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
+ ap = ap0 and
+ apa = apa0
or
localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
+ ap0 instanceof ApNil and
+ apa = getApprox(ap)
)
or
exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone() and
- ap = getApNil(node)
+ ap = getApNil(node) and
+ apa = getApprox(ap)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
+ fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone() and
- ap = getApNil(node)
+ ap = getApNil(node) and
+ apa = getApprox(ap)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
+ ap = apCons(tc, ap0) and
+ apa = getApprox(ap)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config) and
+ apa = getApprox(ap)
)
or
// flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParamNodeSome(node.asNode()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParamNodeNone() and argAp = apNone()
)
or
// flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
+ flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
+ inner = ret.getEnclosingCallable() and
+ cc = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ // flow through a callable
+ exists(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, boolean allowsFieldFlow, ApApprox innerArgApa
+ |
+ fwdFlowThrough(call, cc, state, ccc, summaryCtx, argAp, ap, apa, ret, innerArgApa, config) and
+ flowThroughOutOfCall(call, ccc, ret, node, allowsFieldFlow, innerArgApa, apa, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
+ ParamNodeOption summaryCtx, ApOption argAp, Configuration config
) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ exists(DataFlowType contentType, ApApprox apa1 |
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
+ PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1360,60 +1465,85 @@ private module MkStage {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
}
+ private class ApNonNil instanceof Ap {
+ pragma[nomagic]
+ ApNonNil() { not this instanceof ApNil }
+
+ string toString() { result = "" }
+ }
+
pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ private predicate fwdFlowRead0(
+ NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
Configuration config
) {
- fwdFlow(node1, state, cc, argAp, ap, config) and
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
+ PrevStage::readStepCand(node1, _, _, config)
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
+ ParamNodeOption summaryCtx, ApOption argAp, Configuration config
+ ) {
+ fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
- Ap ap, Configuration config
+ DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
+ ParamNodeOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, argAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
+ flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowRetFromArg(
+ RetNodeEx ret, FlowState state, CcCall ccc, ParamNodeEx summaryCtx, Ap argAp, ApApprox argApa,
+ Ap ap, ApApprox apa, Configuration config
) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
+ exists(ReturnKindExt kind |
+ fwdFlow(pragma[only_bind_into](ret), state, ccc,
+ TParamNodeSome(pragma[only_bind_into](summaryCtx.asNode())),
+ pragma[only_bind_into](apSome(argAp)), ap, pragma[only_bind_into](apa),
+ pragma[only_bind_into](config)) and
+ kind = ret.getKind() and
+ parameterFlowThroughAllowed(summaryCtx, kind) and
+ argApa = getApprox(argAp) and
+ PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config))
)
}
- pragma[nomagic]
- private predicate fwdFlowOutFromArg(
- DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
+ pragma[inline]
+ private predicate fwdFlowThrough0(
+ DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx,
+ ApOption argAp, Ap ap, ApApprox apa, RetNodeEx ret, ParamNodeEx innerSummaryCtx,
+ Ap innerArgAp, ApApprox innerArgApa, Configuration config
) {
- exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
- fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
- flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ fwdFlowRetFromArg(ret, state, ccc, innerSummaryCtx, innerArgAp, innerArgApa, ap, apa, config) and
+ fwdFlowIsEntered(call, cc, ccc, summaryCtx, argAp, innerSummaryCtx, innerArgAp, config)
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowThrough(
+ DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx,
+ ApOption argAp, Ap ap, ApApprox apa, RetNodeEx ret, ApApprox innerArgApa, Configuration config
+ ) {
+ fwdFlowThrough0(call, cc, state, ccc, summaryCtx, argAp, ap, apa, ret, _, _, innerArgApa,
+ config)
}
/**
@@ -1422,11 +1552,14 @@ private module MkStage {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ DataFlowCall call, Cc cc, CcCall innerCc, ParamNodeOption summaryCtx, ApOption argAp,
+ ParamNodeEx p, Ap ap, Configuration config
) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ exists(ApApprox apa |
+ fwdFlowIn(call, pragma[only_bind_into](p), _, cc, innerCc, summaryCtx, argAp, ap,
+ pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, apa, config) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config))
)
}
@@ -1434,146 +1567,194 @@ private module MkStage {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
- fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
+ fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
- fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
+ fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
+ private predicate returnFlowsThrough0(
+ DataFlowCall call, FlowState state, CcCall ccc, Ap ap, ApApprox apa, RetNodeEx ret,
+ ParamNodeEx innerSummaryCtx, Ap innerArgAp, ApApprox innerArgApa, Configuration config
+ ) {
+ fwdFlowThrough0(call, _, state, ccc, _, _, ap, apa, ret, innerSummaryCtx, innerArgAp,
+ innerArgApa, config)
+ }
+
+ pragma[nomagic]
+ private predicate returnFlowsThrough(
+ RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
+ Ap ap, Configuration config
+ ) {
+ exists(DataFlowCall call, ApApprox apa, boolean allowsFieldFlow, ApApprox innerArgApa |
+ returnFlowsThrough0(call, state, ccc, ap, apa, ret, p, argAp, innerArgApa, config) and
+ flowThroughOutOfCall(call, ccc, ret, _, allowsFieldFlow, innerArgApa, apa, config) and
+ pos = ret.getReturnPosition() and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp, Ap ap,
+ 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))
+ exists(ApApprox argApa |
+ flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
+ allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
+ pragma[only_bind_into](config)) and
+ returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ if allowsFieldFlow = false then argAp instanceof ApNil else any()
+ )
}
pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ private predicate flowIntoCallAp(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
+ Configuration config
) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
+ exists(ApApprox apa |
+ flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
+ fwdFlow(arg, _, _, _, _, ap, apa, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowOutOfCallAp(
+ DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow,
+ Ap ap, Configuration config
+ ) {
+ exists(ApApprox apa |
+ flowOutOfCallApa(call, ret, _, out, allowsFieldFlow, apa, config) and
+ fwdFlow(ret, _, _, _, _, ap, apa, config) and
+ pos = ret.getReturnPosition()
+ )
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
- * The Boolean `toReturn` records whether the node must be returned from the
- * enclosing callable in order to reach a sink, and if so, `returnAp` records
- * the access path of the returned value.
+ * The parameter `returnCtx` records whether (and how) the node must be returned
+ * from the enclosing callable in order to reach a sink, and if so, `returnAp`
+ * records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
+ Configuration config
) {
- revFlow0(node, state, toReturn, returnAp, ap, config) and
- fwdFlow(node, state, _, _, ap, config)
+ revFlow0(node, state, returnCtx, returnAp, ap, config) and
+ fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
+ Configuration config
) {
- fwdFlow(node, state, _, _, ap, config) and
+ fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
+ (
+ if hasSinkCallCtx(config)
+ then returnCtx = TReturnCtxNoFlowThrough()
+ else returnCtx = TReturnCtxNone()
+ ) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
- revFlow(mid, state0, toReturn, returnAp, ap, config)
+ revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
- revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
+ revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
- fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
+ flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ returnCtx = TReturnCtxNone()
+ )
or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ // flow through a callable
+ exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
+ revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
+ flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
)
or
// flow out of a callable
- revFlowOut(_, node, state, _, _, ap, config) and
- toReturn = true and
- if returnNodeMayFlowThrough(node, state, ap, config)
- then returnAp = apSome(ap)
- else returnAp = apNone()
+ exists(ReturnPosition pos |
+ revFlowOut(_, node, pos, state, _, _, ap, config) and
+ if returnFlowsThrough(node, pos, state, _, _, _, ap, config)
+ then (
+ returnCtx = TReturnCtxMaybeFlowThrough(pos) and
+ returnAp = apSome(ap)
+ ) else (
+ returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
+ )
+ )
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
- boolean toReturn, ApOption returnAp, Configuration config
+ ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1593,36 +1774,33 @@ private module MkStage {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx,
+ ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
- revFlow(out, state, toReturn, returnAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ revFlow(out, state, returnCtx, returnAp, ap, config) and
+ flowOutOfCallAp(call, ret, pos, out, allowsFieldFlow, ap, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate revFlowInNotToReturn(
- ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
+ private predicate revFlowParamToReturn(
+ ParamNodeEx p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap, Configuration config
) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, false, returnAp, ap, config) and
- flowIntoCall(_, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp),
+ pragma[only_bind_into](ap), pragma[only_bind_into](config)) and
+ parameterFlowThroughAllowed(p, pos.getKind()) and
+ PrevStage::parameterMayFlowThrough(p, getApprox(ap), config)
}
pragma[nomagic]
- private predicate revFlowInToReturn(
- DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
+ private predicate revFlowThrough(
+ DataFlowCall call, ReturnCtx returnCtx, ParamNodeEx p, FlowState state, ReturnPosition pos,
+ ApOption returnAp, Ap ap, Ap innerReturnAp, Configuration config
) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ revFlowParamToReturn(p, state, pos, innerReturnAp, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, pos, innerReturnAp, config)
}
/**
@@ -1632,11 +1810,12 @@ private module MkStage {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap,
+ Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
- revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
- fwdFlow(ret, state, ccc, apSome(_), ap, config) and
+ revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and
+ returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1673,6 +1852,11 @@ private module MkStage {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+ pragma[nomagic]
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
+ revFlow(node, _, _, _, ap, config)
+ }
+
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) {
@@ -1707,40 +1891,49 @@ private module MkStage {
validAp(ap, config)
}
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ pragma[nomagic]
+ private predicate parameterFlowsThroughRev(
+ ParamNodeEx p, Ap ap, ReturnPosition pos, Ap returnAp, Configuration config
) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap, config) and
+ parameterFlowThroughAllowed(p, pos.getKind())
}
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
- parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
- revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
- pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
- fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
- kind = ret.getKind() and
- p.getPosition() = pos and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = pos
- or
- p.allowParameterReturnInSelf()
- )
+ pragma[nomagic]
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, ReturnPosition pos |
+ returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, pos, _, config)
+ )
+ }
+
+ pragma[nomagic]
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ ) {
+ exists(ParamNodeEx p, ReturnPosition pos |
+ returnFlowsThrough(ret, pos, _, _, p, argAp, ap, config) and
+ parameterFlowsThroughRev(p, argAp, pos, ap, config) and
+ kind = pos.getKind()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowThroughArg(
+ DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
+ Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
+ revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
+ flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
+ revFlow(arg, state, returnCtx, returnAp, ap, config) and
+ revFlowThroughArg(call, arg, state, returnCtx, returnAp, ap, config)
)
}
@@ -1748,13 +1941,13 @@ private module MkStage {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
- nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
+ states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
+ count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)
)
or
fwd = false and
@@ -1763,8 +1956,8 @@ private module MkStage {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
- count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
- revFlow(n, state, b, retAp, ap, config)
+ count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
+ revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1909,7 +2102,7 @@ private module Stage2Param implements MkStage::StageParam {
exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1945,9 +2138,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
- DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
) {
- flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
+ flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2015,8 +2209,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
- flowIntoCallNodeCand1(_, node, next, config) or
- flowOutOfCallNodeCand1(_, node, next, config) or
+ flowIntoCallNodeCand2(_, node, next, _, config) or
+ flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2103,16 +2297,16 @@ private module LocalFlowBigStep {
pragma[nomagic]
predicate localFlowBigStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
- AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
+ DataFlowType t, Configuration config, LocalCallContext callContext
) {
- localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
+ localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
localFlowExit(node2, state1, config) and
state1 = state2
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
state1 != state2 and
preservesValue = false and
- apf = TFrontNil(node2.getDataFlowType()) and
+ t = node2.getDataFlowType() and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
@@ -2126,11 +2320,87 @@ private import LocalFlowBigStep
private module Stage3Param implements MkStage::StageParam {
private module PrevStage = Stage2;
+ class Ap = ApproxAccessPathFront;
+
+ class ApNil = ApproxAccessPathFrontNil;
+
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+
+ ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
+ }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
+
+ pragma[noinline]
+ Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
+
+ class ApOption = ApproxAccessPathFrontOption;
+
+ ApOption apNone() { result = TApproxAccessPathFrontNone() }
+
+ ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
+
+ import BooleanCallContext
+
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
+ ) {
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
+ exists(lcc)
+ }
+
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
+
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
+
+ pragma[nomagic]
+ private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
+ exists(Content c |
+ PrevStage::revFlow(node, pragma[only_bind_into](config)) and
+ PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
+ expectsContentEx(node, c) and
+ c = ap.getAHead().getContent()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ exists(state) and
+ exists(config) and
+ (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
+ (
+ notExpectsContent(node)
+ or
+ expectsContentCand(node, ap, config)
+ )
+ }
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ // We need to typecheck stores here, since reverse flow through a getter
+ // might have a different type here compared to inside the getter.
+ compatibleTypes(ap.getType(), contentType)
+ }
+}
+
+private module Stage3 implements StageSig {
+ import MkStage::Stage
+}
+
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
+
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
@@ -2150,16 +2420,42 @@ private module Stage3Param implements MkStage::StageParam {
import BooleanCallContext
+ pragma[nomagic]
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
- localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
+ PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
+ exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ pragma[nomagic]
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ exists(FlowState state |
+ flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
+ pragma[only_bind_into](config))
+ )
+ }
- predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ pragma[nomagic]
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ exists(FlowState state |
+ flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
+ pragma[only_bind_into](config))
+ )
+ }
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2215,8 +2511,8 @@ private module Stage3Param implements MkStage::StageParam {
}
}
-private module Stage3 implements StageSig {
- import MkStage::Stage
+private module Stage4 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2227,8 +2523,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
- Stage3::revFlow(node, state, true, _, apf, config) and
- Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
+ Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
+ Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
+ config)
)
}
@@ -2238,10 +2535,10 @@ private predicate flowCandSummaryCtx(
*/
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
exists(int tails, int nodes, int apLimit, int tupleLimit |
- tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
+ tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2255,11 +2552,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
private newtype TAccessPathApprox =
TNil(DataFlowType t) or
TConsNil(TypedContent tc, DataFlowType t) {
- Stage3::consCand(tc, TFrontNil(t), _) and
+ Stage4::consCand(tc, TFrontNil(t), _) and
not expensiveLen2unfolding(tc, _)
} or
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
- Stage3::consCand(tc1, TFrontHead(tc2), _) and
+ Stage4::consCand(tc1, TFrontHead(tc2), _) and
len in [2 .. accessPathLimit()] and
not expensiveLen2unfolding(tc1, _)
} or
@@ -2389,7 +2686,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
override AccessPathApprox pop(TypedContent head) {
head = tc and
(
- exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
+ exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
result = TConsCons(tc2, _, len - 1)
or
len = 2 and
@@ -2400,7 +2697,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
or
exists(DataFlowType t |
len = 1 and
- Stage3::consCand(tc, TFrontNil(t), _) and
+ Stage4::consCand(tc, TFrontNil(t), _) and
result = TNil(t)
)
)
@@ -2425,13 +2722,14 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4Param implements MkStage::StageParam {
- private module PrevStage = Stage3;
+private module Stage5Param implements MkStage::StageParam {
+ private module PrevStage = Stage4;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
+ pragma[nomagic]
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
ApNil getApNil(NodeEx node) {
@@ -2457,15 +2755,18 @@ private module Stage4Param implements MkStage::StageParam {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
- localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
+ PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
}
pragma[nomagic]
predicate flowOutOfCall(
- DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
) {
exists(FlowState state |
- flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
+ flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2493,7 +2794,7 @@ private module Stage4Param implements MkStage::StageParam {
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
-private module Stage4 = MkStage::Stage;
+private module Stage5 = MkStage::Stage;
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
@@ -2502,13 +2803,13 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
- NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
+ NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(AccessPathApprox apa0 |
- Stage4::parameterMayFlowThrough(_, c, _, _) and
- Stage4::revFlow(n, state, true, _, apa0, config) and
- Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
- n.getEnclosingCallable() = c
+ Stage5::parameterMayFlowThrough(p, _, _) and
+ Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
+ Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()),
+ TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2516,9 +2817,9 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
- exists(DataFlowCallable c |
- Stage4::parameterMayFlowThrough(_, c, apa, config) and
- nodeMayUseSummary0(n, c, state, apa, config)
+ exists(ParamNodeEx p |
+ Stage5::parameterMayFlowThrough(p, apa, config) and
+ nodeMayUseSummary0(n, p, state, apa, config)
)
}
@@ -2526,8 +2827,8 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
- Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, config)
+ Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
+ Stage5::revFlow(p, state, _, config)
)
}
@@ -2576,7 +2877,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
len = apa.len() and
result =
strictcount(AccessPathFront apf |
- Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
+ Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
config)
)
)
@@ -2585,7 +2886,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -2606,7 +2907,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
exists(TypedContent head |
apa.pop(head) = result and
- Stage4::consCand(head, result, config)
+ Stage5::consCand(head, result, config)
)
}
@@ -2688,7 +2989,7 @@ private newtype TPathNode =
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) {
// A PathNode is introduced by a source ...
- Stage4::revFlow(node, state, config) and
+ Stage5::revFlow(node, state, config) and
sourceNode(node, state, config) and
(
if hasSourceCallCtx(config)
@@ -2702,7 +3003,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
+ Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -2712,6 +3013,18 @@ private newtype TPathNode =
state = sink.getState() and
config = sink.getConfiguration()
)
+ } or
+ TPathNodeSourceGroup(string sourceGroup, Configuration config) {
+ exists(PathNodeImpl source |
+ sourceGroup = source.getSourceGroup() and
+ config = source.getConfiguration()
+ )
+ } or
+ TPathNodeSinkGroup(string sinkGroup, Configuration config) {
+ exists(PathNodeSink sink |
+ sinkGroup = sink.getSinkGroup() and
+ config = sink.getConfiguration()
+ )
}
/**
@@ -2832,7 +3145,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
override TypedContent getHead() { result = head1 }
override AccessPath getTail() {
- Stage4::consCand(head1, result.getApprox(), _) and
+ Stage5::consCand(head1, result.getApprox(), _) and
result.getHead() = head2 and
result.length() = len - 1
}
@@ -2863,7 +3176,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
override TypedContent getHead() { result = head }
override AccessPath getTail() {
- Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
+ Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
}
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
@@ -2920,6 +3233,22 @@ abstract private class PathNodeImpl extends TPathNode {
)
}
+ string getSourceGroup() {
+ this.isSource() and
+ this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result)
+ }
+
+ predicate isFlowSource() {
+ this.isSource() and not exists(this.getSourceGroup())
+ or
+ this instanceof PathNodeSourceGroup
+ }
+
+ predicate isFlowSink() {
+ this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or
+ this instanceof PathNodeSinkGroup
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2959,7 +3288,9 @@ abstract private class PathNodeImpl extends TPathNode {
/** Holds if `n` can reach a sink. */
private predicate directReach(PathNodeImpl n) {
- n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
+ n instanceof PathNodeSink or
+ n instanceof PathNodeSinkGroup or
+ directReach(n.getANonHiddenSuccessor())
}
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
@@ -3015,6 +3346,12 @@ class PathNode instanceof PathNodeImpl {
/** Holds if this node is a source. */
final predicate isSource() { super.isSource() }
+
+ /** Holds if this node is a grouping of source nodes. */
+ final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) }
+
+ /** Holds if this node is a grouping of sink nodes. */
+ final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) }
}
/**
@@ -3136,9 +3473,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNodeImpl getASuccessorImpl() { none() }
+ override PathNodeImpl getASuccessorImpl() {
+ result = TPathNodeSinkGroup(this.getSinkGroup(), config)
+ }
override predicate isSource() { sourceNode(node, state, config) }
+
+ string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
+}
+
+private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
+ string sourceGroup;
+ Configuration config;
+
+ PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
+
+ override NodeEx getNodeEx() { none() }
+
+ override FlowState getState() { none() }
+
+ override Configuration getConfiguration() { result = config }
+
+ override PathNodeImpl getASuccessorImpl() {
+ result.getSourceGroup() = sourceGroup and
+ result.getConfiguration() = config
+ }
+
+ override predicate isSource() { none() }
+
+ override string toString() { result = sourceGroup }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
+ }
+}
+
+private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
+ string sinkGroup;
+ Configuration config;
+
+ PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
+
+ override NodeEx getNodeEx() { none() }
+
+ override FlowState getState() { none() }
+
+ override Configuration getConfiguration() { result = config }
+
+ override PathNodeImpl getASuccessorImpl() { none() }
+
+ override predicate isSource() { none() }
+
+ override string toString() { result = sinkGroup }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
+ }
}
private predicate pathNode(
@@ -3174,7 +3568,8 @@ private predicate pathStep(
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
- localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
+ localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
+ localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -3216,7 +3611,7 @@ private predicate pathReadStep(
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
- Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
+ Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3226,7 +3621,7 @@ private predicate pathStoreStep(
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
+ Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3263,7 +3658,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, apa, config)
+ Stage5::revFlow(result, _, apa, config)
}
/**
@@ -3299,7 +3694,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, apa, config) and
+ Stage5::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
@@ -3354,17 +3749,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
- exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
+ exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
- pos = sc.getParameterPos() and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = pos
- or
- sc.getParamNode().allowParameterReturnInSelf()
- )
+ parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}
@@ -3495,6 +3884,15 @@ private module Subpaths {
* Will only have results if `configuration` has non-empty sources and
* sinks.
*/
+private predicate hasFlowPath(
+ PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration
+) {
+ flowsource.isFlowSource() and
+ flowsource.getConfiguration() = configuration and
+ (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
+ flowsink.isFlowSink()
+}
+
private predicate flowsTo(
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink,
Configuration configuration
@@ -3575,9 +3973,17 @@ predicate stageStats(
n = 45 and
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
or
- stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
+ stage = "5 Fwd" and
+ n = 50 and
+ Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
or
- stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
+ stage = "5 Rev" and
+ n = 55 and
+ Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
+ or
+ stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
+ or
+ stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
}
private module FlowExploration {
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
index 2dde1c2819d..1228d00b6ba 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
- predicate isSink(Node source, FlowState state) { none() }
+ predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/
FlowFeature getAFeature() { none() }
+ /** Holds if sources should be grouped in the result of `hasFlowPath`. */
+ predicate sourceGrouping(Node source, string sourceGroup) { none() }
+
+ /** Holds if sinks should be grouped in the result of `hasFlowPath`. */
+ predicate sinkGrouping(Node sink, string sinkGroup) { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
* The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`.
*/
- predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
+ predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
/**
* Holds if data may flow from some source to `sink` for this configuration.
@@ -313,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -602,8 +606,27 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
+/**
+ * Holds if flow from `p` to a return node of kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[p, kind]
+private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
+ exists(ParameterPosition pos | p.isParameterOf(_, pos) |
+ not kind.(ParamUpdateReturnKind).getPosition() = pos
+ or
+ allowParameterReturnInSelfCached(p.asNode())
+ )
+}
+
private module Stage1 implements StageSig {
- class Ap = Unit;
+ class Ap extends int {
+ // workaround for bad functionality-induced joins (happens when using `Unit`)
+ pragma[nomagic]
+ Ap() { this in [0 .. 1] and this < 1 }
+ }
private class Cc = boolean;
@@ -944,6 +967,12 @@ private module Stage1 implements StageSig {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
+ pragma[nomagic]
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
+ revFlow(node, config) and
+ exists(ap)
+ }
+
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -975,21 +1004,26 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnKindExt kind |
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
+ exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
- or
- p.allowParameterReturnInSelf()
- )
+ parameterFlowThroughAllowed(p, kind)
)
}
+ pragma[nomagic]
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ ) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind() and
+ exists(argAp) and
+ exists(ap)
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1046,12 +1080,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
- viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
- Stage1::revFlow(ret, config) and
- not outBarrier(ret, config) and
- not inBarrier(out, config)
+ exists(ReturnPosition pos |
+ viableReturnPosOutNodeCand1(call, pos, out, config) and
+ pos = ret.getReturnPosition() and
+ kind = pos.getKind() and
+ Stage1::revFlow(ret, config) and
+ not outBarrier(ret, config) and
+ not inBarrier(out, config)
+ )
}
pragma[nomagic]
@@ -1081,10 +1119,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
+pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
- flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
+ flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1093,10 +1132,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
+pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
- flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
+ flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1109,12 +1149,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
) {
- flowOutOfCallNodeCand1(call, ret, out, config) and
+ flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
- b = branch(ret, config) and
- j = join(out, config) and
+ b = branch(ret, pragma[only_bind_into](config)) and
+ j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1130,10 +1171,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- flowIntoCallNodeCand1(call, arg, p, config) and
+ flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
- b = branch(arg, config) and
- j = join(p, config) and
+ b = branch(arg, pragma[only_bind_into](config)) and
+ j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1145,12 +1186,18 @@ private signature module StageSig {
predicate revFlow(NodeEx node, Configuration config);
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
+
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
+
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ );
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1216,7 +1263,8 @@ private module MkStage {
);
predicate flowOutOfCall(
- DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
);
predicate flowIntoCall(
@@ -1234,21 +1282,43 @@ private module MkStage {
import Param
/* Begin: Stage logic. */
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
+ PrevStage::revFlowAp(node, apa, config)
+ }
+
+ pragma[nomagic]
+ private predicate flowIntoCallApa(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
+ Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate flowOutOfCallApa(
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ ApApprox apa, Configuration config
+ ) {
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
+ PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ ApApprox argApa, ApApprox apa, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- matchesCall(ccc, call)
+ exists(ReturnKindExt kind |
+ flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ )
}
/**
@@ -1256,99 +1326,134 @@ private module MkStage {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
- * argument in a call, and if so, `argAp` records the access path of that
- * argument.
+ * argument in a call, and if so, `summaryCtx` and `argAp` record the
+ * corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
- NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ ApApprox apa, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
+ PrevStage::revFlow(node, state, apa, config) and
filter(node, state, ap, config)
}
+ pragma[inline]
+ additional predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ Configuration config
+ ) {
+ fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
+ }
+
pragma[nomagic]
private predicate fwdFlow0(
- NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ ApApprox apa, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
- ap = getApNil(node)
+ summaryCtx = TParamNodeNone() and
+ ap = getApNil(node) and
+ apa = getApprox(ap)
or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
+ ap = ap0 and
+ apa = apa0
or
localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
+ ap0 instanceof ApNil and
+ apa = getApprox(ap)
)
or
exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone() and
- ap = getApNil(node)
+ ap = getApNil(node) and
+ apa = getApprox(ap)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
+ fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone() and
- ap = getApNil(node)
+ ap = getApNil(node) and
+ apa = getApprox(ap)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
+ ap = apCons(tc, ap0) and
+ apa = getApprox(ap)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config) and
+ apa = getApprox(ap)
)
or
// flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParamNodeSome(node.asNode()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParamNodeNone() and argAp = apNone()
)
or
// flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
+ flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
+ inner = ret.getEnclosingCallable() and
+ cc = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ // flow through a callable
+ exists(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, boolean allowsFieldFlow, ApApprox innerArgApa
+ |
+ fwdFlowThrough(call, cc, state, ccc, summaryCtx, argAp, ap, apa, ret, innerArgApa, config) and
+ flowThroughOutOfCall(call, ccc, ret, node, allowsFieldFlow, innerArgApa, apa, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
+ ParamNodeOption summaryCtx, ApOption argAp, Configuration config
) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ exists(DataFlowType contentType, ApApprox apa1 |
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
+ PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1360,60 +1465,85 @@ private module MkStage {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
}
+ private class ApNonNil instanceof Ap {
+ pragma[nomagic]
+ ApNonNil() { not this instanceof ApNil }
+
+ string toString() { result = "" }
+ }
+
pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ private predicate fwdFlowRead0(
+ NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
Configuration config
) {
- fwdFlow(node1, state, cc, argAp, ap, config) and
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
+ PrevStage::readStepCand(node1, _, _, config)
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
+ ParamNodeOption summaryCtx, ApOption argAp, Configuration config
+ ) {
+ fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
- Ap ap, Configuration config
+ DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
+ ParamNodeOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, argAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
+ flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowRetFromArg(
+ RetNodeEx ret, FlowState state, CcCall ccc, ParamNodeEx summaryCtx, Ap argAp, ApApprox argApa,
+ Ap ap, ApApprox apa, Configuration config
) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
+ exists(ReturnKindExt kind |
+ fwdFlow(pragma[only_bind_into](ret), state, ccc,
+ TParamNodeSome(pragma[only_bind_into](summaryCtx.asNode())),
+ pragma[only_bind_into](apSome(argAp)), ap, pragma[only_bind_into](apa),
+ pragma[only_bind_into](config)) and
+ kind = ret.getKind() and
+ parameterFlowThroughAllowed(summaryCtx, kind) and
+ argApa = getApprox(argAp) and
+ PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config))
)
}
- pragma[nomagic]
- private predicate fwdFlowOutFromArg(
- DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
+ pragma[inline]
+ private predicate fwdFlowThrough0(
+ DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx,
+ ApOption argAp, Ap ap, ApApprox apa, RetNodeEx ret, ParamNodeEx innerSummaryCtx,
+ Ap innerArgAp, ApApprox innerArgApa, Configuration config
) {
- exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
- fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
- flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ fwdFlowRetFromArg(ret, state, ccc, innerSummaryCtx, innerArgAp, innerArgApa, ap, apa, config) and
+ fwdFlowIsEntered(call, cc, ccc, summaryCtx, argAp, innerSummaryCtx, innerArgAp, config)
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowThrough(
+ DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx,
+ ApOption argAp, Ap ap, ApApprox apa, RetNodeEx ret, ApApprox innerArgApa, Configuration config
+ ) {
+ fwdFlowThrough0(call, cc, state, ccc, summaryCtx, argAp, ap, apa, ret, _, _, innerArgApa,
+ config)
}
/**
@@ -1422,11 +1552,14 @@ private module MkStage {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ DataFlowCall call, Cc cc, CcCall innerCc, ParamNodeOption summaryCtx, ApOption argAp,
+ ParamNodeEx p, Ap ap, Configuration config
) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ exists(ApApprox apa |
+ fwdFlowIn(call, pragma[only_bind_into](p), _, cc, innerCc, summaryCtx, argAp, ap,
+ pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, apa, config) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config))
)
}
@@ -1434,146 +1567,194 @@ private module MkStage {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
- fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
+ fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
- fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
+ fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
+ private predicate returnFlowsThrough0(
+ DataFlowCall call, FlowState state, CcCall ccc, Ap ap, ApApprox apa, RetNodeEx ret,
+ ParamNodeEx innerSummaryCtx, Ap innerArgAp, ApApprox innerArgApa, Configuration config
+ ) {
+ fwdFlowThrough0(call, _, state, ccc, _, _, ap, apa, ret, innerSummaryCtx, innerArgAp,
+ innerArgApa, config)
+ }
+
+ pragma[nomagic]
+ private predicate returnFlowsThrough(
+ RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
+ Ap ap, Configuration config
+ ) {
+ exists(DataFlowCall call, ApApprox apa, boolean allowsFieldFlow, ApApprox innerArgApa |
+ returnFlowsThrough0(call, state, ccc, ap, apa, ret, p, argAp, innerArgApa, config) and
+ flowThroughOutOfCall(call, ccc, ret, _, allowsFieldFlow, innerArgApa, apa, config) and
+ pos = ret.getReturnPosition() and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp, Ap ap,
+ 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))
+ exists(ApApprox argApa |
+ flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
+ allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
+ pragma[only_bind_into](config)) and
+ returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ if allowsFieldFlow = false then argAp instanceof ApNil else any()
+ )
}
pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ private predicate flowIntoCallAp(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
+ Configuration config
) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
+ exists(ApApprox apa |
+ flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
+ fwdFlow(arg, _, _, _, _, ap, apa, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowOutOfCallAp(
+ DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow,
+ Ap ap, Configuration config
+ ) {
+ exists(ApApprox apa |
+ flowOutOfCallApa(call, ret, _, out, allowsFieldFlow, apa, config) and
+ fwdFlow(ret, _, _, _, _, ap, apa, config) and
+ pos = ret.getReturnPosition()
+ )
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
- * The Boolean `toReturn` records whether the node must be returned from the
- * enclosing callable in order to reach a sink, and if so, `returnAp` records
- * the access path of the returned value.
+ * The parameter `returnCtx` records whether (and how) the node must be returned
+ * from the enclosing callable in order to reach a sink, and if so, `returnAp`
+ * records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
+ Configuration config
) {
- revFlow0(node, state, toReturn, returnAp, ap, config) and
- fwdFlow(node, state, _, _, ap, config)
+ revFlow0(node, state, returnCtx, returnAp, ap, config) and
+ fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
+ Configuration config
) {
- fwdFlow(node, state, _, _, ap, config) and
+ fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
+ (
+ if hasSinkCallCtx(config)
+ then returnCtx = TReturnCtxNoFlowThrough()
+ else returnCtx = TReturnCtxNone()
+ ) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
- revFlow(mid, state0, toReturn, returnAp, ap, config)
+ revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
- revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
+ revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
- fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
+ flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ returnCtx = TReturnCtxNone()
+ )
or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ // flow through a callable
+ exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
+ revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
+ flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
)
or
// flow out of a callable
- revFlowOut(_, node, state, _, _, ap, config) and
- toReturn = true and
- if returnNodeMayFlowThrough(node, state, ap, config)
- then returnAp = apSome(ap)
- else returnAp = apNone()
+ exists(ReturnPosition pos |
+ revFlowOut(_, node, pos, state, _, _, ap, config) and
+ if returnFlowsThrough(node, pos, state, _, _, _, ap, config)
+ then (
+ returnCtx = TReturnCtxMaybeFlowThrough(pos) and
+ returnAp = apSome(ap)
+ ) else (
+ returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
+ )
+ )
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
- boolean toReturn, ApOption returnAp, Configuration config
+ ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1593,36 +1774,33 @@ private module MkStage {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx,
+ ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
- revFlow(out, state, toReturn, returnAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ revFlow(out, state, returnCtx, returnAp, ap, config) and
+ flowOutOfCallAp(call, ret, pos, out, allowsFieldFlow, ap, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate revFlowInNotToReturn(
- ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
+ private predicate revFlowParamToReturn(
+ ParamNodeEx p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap, Configuration config
) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, false, returnAp, ap, config) and
- flowIntoCall(_, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp),
+ pragma[only_bind_into](ap), pragma[only_bind_into](config)) and
+ parameterFlowThroughAllowed(p, pos.getKind()) and
+ PrevStage::parameterMayFlowThrough(p, getApprox(ap), config)
}
pragma[nomagic]
- private predicate revFlowInToReturn(
- DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
+ private predicate revFlowThrough(
+ DataFlowCall call, ReturnCtx returnCtx, ParamNodeEx p, FlowState state, ReturnPosition pos,
+ ApOption returnAp, Ap ap, Ap innerReturnAp, Configuration config
) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ revFlowParamToReturn(p, state, pos, innerReturnAp, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, pos, innerReturnAp, config)
}
/**
@@ -1632,11 +1810,12 @@ private module MkStage {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap,
+ Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
- revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
- fwdFlow(ret, state, ccc, apSome(_), ap, config) and
+ revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and
+ returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1673,6 +1852,11 @@ private module MkStage {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+ pragma[nomagic]
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
+ revFlow(node, _, _, _, ap, config)
+ }
+
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) {
@@ -1707,40 +1891,49 @@ private module MkStage {
validAp(ap, config)
}
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ pragma[nomagic]
+ private predicate parameterFlowsThroughRev(
+ ParamNodeEx p, Ap ap, ReturnPosition pos, Ap returnAp, Configuration config
) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap, config) and
+ parameterFlowThroughAllowed(p, pos.getKind())
}
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
- parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
- revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
- pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
- fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
- kind = ret.getKind() and
- p.getPosition() = pos and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = pos
- or
- p.allowParameterReturnInSelf()
- )
+ pragma[nomagic]
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, ReturnPosition pos |
+ returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, pos, _, config)
+ )
+ }
+
+ pragma[nomagic]
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ ) {
+ exists(ParamNodeEx p, ReturnPosition pos |
+ returnFlowsThrough(ret, pos, _, _, p, argAp, ap, config) and
+ parameterFlowsThroughRev(p, argAp, pos, ap, config) and
+ kind = pos.getKind()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowThroughArg(
+ DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
+ Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
+ revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
+ flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
+ revFlow(arg, state, returnCtx, returnAp, ap, config) and
+ revFlowThroughArg(call, arg, state, returnCtx, returnAp, ap, config)
)
}
@@ -1748,13 +1941,13 @@ private module MkStage {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
- nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
+ states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
+ count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)
)
or
fwd = false and
@@ -1763,8 +1956,8 @@ private module MkStage {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
- count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
- revFlow(n, state, b, retAp, ap, config)
+ count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
+ revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1909,7 +2102,7 @@ private module Stage2Param implements MkStage::StageParam {
exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1945,9 +2138,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
- DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
) {
- flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
+ flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2015,8 +2209,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
- flowIntoCallNodeCand1(_, node, next, config) or
- flowOutOfCallNodeCand1(_, node, next, config) or
+ flowIntoCallNodeCand2(_, node, next, _, config) or
+ flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2103,16 +2297,16 @@ private module LocalFlowBigStep {
pragma[nomagic]
predicate localFlowBigStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
- AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
+ DataFlowType t, Configuration config, LocalCallContext callContext
) {
- localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
+ localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
localFlowExit(node2, state1, config) and
state1 = state2
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
state1 != state2 and
preservesValue = false and
- apf = TFrontNil(node2.getDataFlowType()) and
+ t = node2.getDataFlowType() and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
@@ -2126,11 +2320,87 @@ private import LocalFlowBigStep
private module Stage3Param implements MkStage::StageParam {
private module PrevStage = Stage2;
+ class Ap = ApproxAccessPathFront;
+
+ class ApNil = ApproxAccessPathFrontNil;
+
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+
+ ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
+ }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
+
+ pragma[noinline]
+ Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
+
+ class ApOption = ApproxAccessPathFrontOption;
+
+ ApOption apNone() { result = TApproxAccessPathFrontNone() }
+
+ ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
+
+ import BooleanCallContext
+
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
+ ) {
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
+ exists(lcc)
+ }
+
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
+
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
+
+ pragma[nomagic]
+ private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
+ exists(Content c |
+ PrevStage::revFlow(node, pragma[only_bind_into](config)) and
+ PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
+ expectsContentEx(node, c) and
+ c = ap.getAHead().getContent()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ exists(state) and
+ exists(config) and
+ (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
+ (
+ notExpectsContent(node)
+ or
+ expectsContentCand(node, ap, config)
+ )
+ }
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ // We need to typecheck stores here, since reverse flow through a getter
+ // might have a different type here compared to inside the getter.
+ compatibleTypes(ap.getType(), contentType)
+ }
+}
+
+private module Stage3 implements StageSig {
+ import MkStage::Stage
+}
+
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
+
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
@@ -2150,16 +2420,42 @@ private module Stage3Param implements MkStage::StageParam {
import BooleanCallContext
+ pragma[nomagic]
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
- localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
+ PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
+ exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ pragma[nomagic]
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ exists(FlowState state |
+ flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
+ pragma[only_bind_into](config))
+ )
+ }
- predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ pragma[nomagic]
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ exists(FlowState state |
+ flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
+ pragma[only_bind_into](config))
+ )
+ }
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2215,8 +2511,8 @@ private module Stage3Param implements MkStage::StageParam {
}
}
-private module Stage3 implements StageSig {
- import MkStage::Stage
+private module Stage4 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2227,8 +2523,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
- Stage3::revFlow(node, state, true, _, apf, config) and
- Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
+ Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
+ Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
+ config)
)
}
@@ -2238,10 +2535,10 @@ private predicate flowCandSummaryCtx(
*/
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
exists(int tails, int nodes, int apLimit, int tupleLimit |
- tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
+ tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2255,11 +2552,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
private newtype TAccessPathApprox =
TNil(DataFlowType t) or
TConsNil(TypedContent tc, DataFlowType t) {
- Stage3::consCand(tc, TFrontNil(t), _) and
+ Stage4::consCand(tc, TFrontNil(t), _) and
not expensiveLen2unfolding(tc, _)
} or
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
- Stage3::consCand(tc1, TFrontHead(tc2), _) and
+ Stage4::consCand(tc1, TFrontHead(tc2), _) and
len in [2 .. accessPathLimit()] and
not expensiveLen2unfolding(tc1, _)
} or
@@ -2389,7 +2686,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
override AccessPathApprox pop(TypedContent head) {
head = tc and
(
- exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
+ exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
result = TConsCons(tc2, _, len - 1)
or
len = 2 and
@@ -2400,7 +2697,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
or
exists(DataFlowType t |
len = 1 and
- Stage3::consCand(tc, TFrontNil(t), _) and
+ Stage4::consCand(tc, TFrontNil(t), _) and
result = TNil(t)
)
)
@@ -2425,13 +2722,14 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4Param implements MkStage::StageParam {
- private module PrevStage = Stage3;
+private module Stage5Param implements MkStage::StageParam {
+ private module PrevStage = Stage4;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
+ pragma[nomagic]
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
ApNil getApNil(NodeEx node) {
@@ -2457,15 +2755,18 @@ private module Stage4Param implements MkStage::StageParam {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
- localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
+ PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
}
pragma[nomagic]
predicate flowOutOfCall(
- DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
) {
exists(FlowState state |
- flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
+ flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2493,7 +2794,7 @@ private module Stage4Param implements MkStage::StageParam {
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
-private module Stage4 = MkStage::Stage;
+private module Stage5 = MkStage::Stage;
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
@@ -2502,13 +2803,13 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
- NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
+ NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(AccessPathApprox apa0 |
- Stage4::parameterMayFlowThrough(_, c, _, _) and
- Stage4::revFlow(n, state, true, _, apa0, config) and
- Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
- n.getEnclosingCallable() = c
+ Stage5::parameterMayFlowThrough(p, _, _) and
+ Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
+ Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()),
+ TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2516,9 +2817,9 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
- exists(DataFlowCallable c |
- Stage4::parameterMayFlowThrough(_, c, apa, config) and
- nodeMayUseSummary0(n, c, state, apa, config)
+ exists(ParamNodeEx p |
+ Stage5::parameterMayFlowThrough(p, apa, config) and
+ nodeMayUseSummary0(n, p, state, apa, config)
)
}
@@ -2526,8 +2827,8 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
- Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, config)
+ Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
+ Stage5::revFlow(p, state, _, config)
)
}
@@ -2576,7 +2877,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
len = apa.len() and
result =
strictcount(AccessPathFront apf |
- Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
+ Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
config)
)
)
@@ -2585,7 +2886,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -2606,7 +2907,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
exists(TypedContent head |
apa.pop(head) = result and
- Stage4::consCand(head, result, config)
+ Stage5::consCand(head, result, config)
)
}
@@ -2688,7 +2989,7 @@ private newtype TPathNode =
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) {
// A PathNode is introduced by a source ...
- Stage4::revFlow(node, state, config) and
+ Stage5::revFlow(node, state, config) and
sourceNode(node, state, config) and
(
if hasSourceCallCtx(config)
@@ -2702,7 +3003,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
+ Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -2712,6 +3013,18 @@ private newtype TPathNode =
state = sink.getState() and
config = sink.getConfiguration()
)
+ } or
+ TPathNodeSourceGroup(string sourceGroup, Configuration config) {
+ exists(PathNodeImpl source |
+ sourceGroup = source.getSourceGroup() and
+ config = source.getConfiguration()
+ )
+ } or
+ TPathNodeSinkGroup(string sinkGroup, Configuration config) {
+ exists(PathNodeSink sink |
+ sinkGroup = sink.getSinkGroup() and
+ config = sink.getConfiguration()
+ )
}
/**
@@ -2832,7 +3145,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
override TypedContent getHead() { result = head1 }
override AccessPath getTail() {
- Stage4::consCand(head1, result.getApprox(), _) and
+ Stage5::consCand(head1, result.getApprox(), _) and
result.getHead() = head2 and
result.length() = len - 1
}
@@ -2863,7 +3176,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
override TypedContent getHead() { result = head }
override AccessPath getTail() {
- Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
+ Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
}
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
@@ -2920,6 +3233,22 @@ abstract private class PathNodeImpl extends TPathNode {
)
}
+ string getSourceGroup() {
+ this.isSource() and
+ this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result)
+ }
+
+ predicate isFlowSource() {
+ this.isSource() and not exists(this.getSourceGroup())
+ or
+ this instanceof PathNodeSourceGroup
+ }
+
+ predicate isFlowSink() {
+ this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or
+ this instanceof PathNodeSinkGroup
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2959,7 +3288,9 @@ abstract private class PathNodeImpl extends TPathNode {
/** Holds if `n` can reach a sink. */
private predicate directReach(PathNodeImpl n) {
- n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
+ n instanceof PathNodeSink or
+ n instanceof PathNodeSinkGroup or
+ directReach(n.getANonHiddenSuccessor())
}
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
@@ -3015,6 +3346,12 @@ class PathNode instanceof PathNodeImpl {
/** Holds if this node is a source. */
final predicate isSource() { super.isSource() }
+
+ /** Holds if this node is a grouping of source nodes. */
+ final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) }
+
+ /** Holds if this node is a grouping of sink nodes. */
+ final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) }
}
/**
@@ -3136,9 +3473,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNodeImpl getASuccessorImpl() { none() }
+ override PathNodeImpl getASuccessorImpl() {
+ result = TPathNodeSinkGroup(this.getSinkGroup(), config)
+ }
override predicate isSource() { sourceNode(node, state, config) }
+
+ string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
+}
+
+private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
+ string sourceGroup;
+ Configuration config;
+
+ PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
+
+ override NodeEx getNodeEx() { none() }
+
+ override FlowState getState() { none() }
+
+ override Configuration getConfiguration() { result = config }
+
+ override PathNodeImpl getASuccessorImpl() {
+ result.getSourceGroup() = sourceGroup and
+ result.getConfiguration() = config
+ }
+
+ override predicate isSource() { none() }
+
+ override string toString() { result = sourceGroup }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
+ }
+}
+
+private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
+ string sinkGroup;
+ Configuration config;
+
+ PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
+
+ override NodeEx getNodeEx() { none() }
+
+ override FlowState getState() { none() }
+
+ override Configuration getConfiguration() { result = config }
+
+ override PathNodeImpl getASuccessorImpl() { none() }
+
+ override predicate isSource() { none() }
+
+ override string toString() { result = sinkGroup }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
+ }
}
private predicate pathNode(
@@ -3174,7 +3568,8 @@ private predicate pathStep(
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
- localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
+ localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
+ localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -3216,7 +3611,7 @@ private predicate pathReadStep(
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
- Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
+ Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3226,7 +3621,7 @@ private predicate pathStoreStep(
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
+ Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3263,7 +3658,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, apa, config)
+ Stage5::revFlow(result, _, apa, config)
}
/**
@@ -3299,7 +3694,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, apa, config) and
+ Stage5::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
@@ -3354,17 +3749,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
- exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
+ exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
- pos = sc.getParameterPos() and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = pos
- or
- sc.getParamNode().allowParameterReturnInSelf()
- )
+ parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}
@@ -3495,6 +3884,15 @@ private module Subpaths {
* Will only have results if `configuration` has non-empty sources and
* sinks.
*/
+private predicate hasFlowPath(
+ PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration
+) {
+ flowsource.isFlowSource() and
+ flowsource.getConfiguration() = configuration and
+ (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
+ flowsink.isFlowSink()
+}
+
private predicate flowsTo(
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink,
Configuration configuration
@@ -3575,9 +3973,17 @@ predicate stageStats(
n = 45 and
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
or
- stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
+ stage = "5 Fwd" and
+ n = 50 and
+ Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
or
- stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
+ stage = "5 Rev" and
+ n = 55 and
+ Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
+ or
+ stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
+ or
+ stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
}
private module FlowExploration {
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
index 2dde1c2819d..1228d00b6ba 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
- predicate isSink(Node source, FlowState state) { none() }
+ predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/
FlowFeature getAFeature() { none() }
+ /** Holds if sources should be grouped in the result of `hasFlowPath`. */
+ predicate sourceGrouping(Node source, string sourceGroup) { none() }
+
+ /** Holds if sinks should be grouped in the result of `hasFlowPath`. */
+ predicate sinkGrouping(Node sink, string sinkGroup) { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
* The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`.
*/
- predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
+ predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
/**
* Holds if data may flow from some source to `sink` for this configuration.
@@ -313,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -602,8 +606,27 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
+/**
+ * Holds if flow from `p` to a return node of kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[p, kind]
+private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
+ exists(ParameterPosition pos | p.isParameterOf(_, pos) |
+ not kind.(ParamUpdateReturnKind).getPosition() = pos
+ or
+ allowParameterReturnInSelfCached(p.asNode())
+ )
+}
+
private module Stage1 implements StageSig {
- class Ap = Unit;
+ class Ap extends int {
+ // workaround for bad functionality-induced joins (happens when using `Unit`)
+ pragma[nomagic]
+ Ap() { this in [0 .. 1] and this < 1 }
+ }
private class Cc = boolean;
@@ -944,6 +967,12 @@ private module Stage1 implements StageSig {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
+ pragma[nomagic]
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
+ revFlow(node, config) and
+ exists(ap)
+ }
+
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -975,21 +1004,26 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnKindExt kind |
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
+ exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
- or
- p.allowParameterReturnInSelf()
- )
+ parameterFlowThroughAllowed(p, kind)
)
}
+ pragma[nomagic]
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ ) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind() and
+ exists(argAp) and
+ exists(ap)
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1046,12 +1080,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
- viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
- Stage1::revFlow(ret, config) and
- not outBarrier(ret, config) and
- not inBarrier(out, config)
+ exists(ReturnPosition pos |
+ viableReturnPosOutNodeCand1(call, pos, out, config) and
+ pos = ret.getReturnPosition() and
+ kind = pos.getKind() and
+ Stage1::revFlow(ret, config) and
+ not outBarrier(ret, config) and
+ not inBarrier(out, config)
+ )
}
pragma[nomagic]
@@ -1081,10 +1119,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
+pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
- flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
+ flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1093,10 +1132,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
+pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
- flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
+ flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1109,12 +1149,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
) {
- flowOutOfCallNodeCand1(call, ret, out, config) and
+ flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
- b = branch(ret, config) and
- j = join(out, config) and
+ b = branch(ret, pragma[only_bind_into](config)) and
+ j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1130,10 +1171,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- flowIntoCallNodeCand1(call, arg, p, config) and
+ flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
- b = branch(arg, config) and
- j = join(p, config) and
+ b = branch(arg, pragma[only_bind_into](config)) and
+ j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1145,12 +1186,18 @@ private signature module StageSig {
predicate revFlow(NodeEx node, Configuration config);
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
+
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
+
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ );
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1216,7 +1263,8 @@ private module MkStage {
);
predicate flowOutOfCall(
- DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
);
predicate flowIntoCall(
@@ -1234,21 +1282,43 @@ private module MkStage {
import Param
/* Begin: Stage logic. */
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
+ PrevStage::revFlowAp(node, apa, config)
+ }
+
+ pragma[nomagic]
+ private predicate flowIntoCallApa(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
+ Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate flowOutOfCallApa(
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ ApApprox apa, Configuration config
+ ) {
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
+ PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ ApApprox argApa, ApApprox apa, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- matchesCall(ccc, call)
+ exists(ReturnKindExt kind |
+ flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ )
}
/**
@@ -1256,99 +1326,134 @@ private module MkStage {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
- * argument in a call, and if so, `argAp` records the access path of that
- * argument.
+ * argument in a call, and if so, `summaryCtx` and `argAp` record the
+ * corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
- NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ ApApprox apa, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
+ PrevStage::revFlow(node, state, apa, config) and
filter(node, state, ap, config)
}
+ pragma[inline]
+ additional predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ Configuration config
+ ) {
+ fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
+ }
+
pragma[nomagic]
private predicate fwdFlow0(
- NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ ApApprox apa, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
- ap = getApNil(node)
+ summaryCtx = TParamNodeNone() and
+ ap = getApNil(node) and
+ apa = getApprox(ap)
or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
+ ap = ap0 and
+ apa = apa0
or
localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
+ ap0 instanceof ApNil and
+ apa = getApprox(ap)
)
or
exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone() and
- ap = getApNil(node)
+ ap = getApNil(node) and
+ apa = getApprox(ap)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
+ fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone() and
- ap = getApNil(node)
+ ap = getApNil(node) and
+ apa = getApprox(ap)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
+ ap = apCons(tc, ap0) and
+ apa = getApprox(ap)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config) and
+ apa = getApprox(ap)
)
or
// flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParamNodeSome(node.asNode()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParamNodeNone() and argAp = apNone()
)
or
// flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
+ flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
+ inner = ret.getEnclosingCallable() and
+ cc = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ // flow through a callable
+ exists(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, boolean allowsFieldFlow, ApApprox innerArgApa
+ |
+ fwdFlowThrough(call, cc, state, ccc, summaryCtx, argAp, ap, apa, ret, innerArgApa, config) and
+ flowThroughOutOfCall(call, ccc, ret, node, allowsFieldFlow, innerArgApa, apa, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
+ ParamNodeOption summaryCtx, ApOption argAp, Configuration config
) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ exists(DataFlowType contentType, ApApprox apa1 |
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
+ PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1360,60 +1465,85 @@ private module MkStage {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
}
+ private class ApNonNil instanceof Ap {
+ pragma[nomagic]
+ ApNonNil() { not this instanceof ApNil }
+
+ string toString() { result = "" }
+ }
+
pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ private predicate fwdFlowRead0(
+ NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
Configuration config
) {
- fwdFlow(node1, state, cc, argAp, ap, config) and
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
+ PrevStage::readStepCand(node1, _, _, config)
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
+ ParamNodeOption summaryCtx, ApOption argAp, Configuration config
+ ) {
+ fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
- DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
- Ap ap, Configuration config
+ DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, CcCall innercc,
+ ParamNodeOption summaryCtx, ApOption argAp, Ap ap, ApApprox apa, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, argAp, ap, config) and
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, apa, config) and
+ flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowRetFromArg(
+ RetNodeEx ret, FlowState state, CcCall ccc, ParamNodeEx summaryCtx, Ap argAp, ApApprox argApa,
+ Ap ap, ApApprox apa, Configuration config
) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
+ exists(ReturnKindExt kind |
+ fwdFlow(pragma[only_bind_into](ret), state, ccc,
+ TParamNodeSome(pragma[only_bind_into](summaryCtx.asNode())),
+ pragma[only_bind_into](apSome(argAp)), ap, pragma[only_bind_into](apa),
+ pragma[only_bind_into](config)) and
+ kind = ret.getKind() and
+ parameterFlowThroughAllowed(summaryCtx, kind) and
+ argApa = getApprox(argAp) and
+ PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config))
)
}
- pragma[nomagic]
- private predicate fwdFlowOutFromArg(
- DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
+ pragma[inline]
+ private predicate fwdFlowThrough0(
+ DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx,
+ ApOption argAp, Ap ap, ApApprox apa, RetNodeEx ret, ParamNodeEx innerSummaryCtx,
+ Ap innerArgAp, ApApprox innerArgApa, Configuration config
) {
- exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
- fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
- flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ fwdFlowRetFromArg(ret, state, ccc, innerSummaryCtx, innerArgAp, innerArgApa, ap, apa, config) and
+ fwdFlowIsEntered(call, cc, ccc, summaryCtx, argAp, innerSummaryCtx, innerArgAp, config)
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowThrough(
+ DataFlowCall call, Cc cc, FlowState state, CcCall ccc, ParamNodeOption summaryCtx,
+ ApOption argAp, Ap ap, ApApprox apa, RetNodeEx ret, ApApprox innerArgApa, Configuration config
+ ) {
+ fwdFlowThrough0(call, cc, state, ccc, summaryCtx, argAp, ap, apa, ret, _, _, innerArgApa,
+ config)
}
/**
@@ -1422,11 +1552,14 @@ private module MkStage {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ DataFlowCall call, Cc cc, CcCall innerCc, ParamNodeOption summaryCtx, ApOption argAp,
+ ParamNodeEx p, Ap ap, Configuration config
) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ exists(ApApprox apa |
+ fwdFlowIn(call, pragma[only_bind_into](p), _, cc, innerCc, summaryCtx, argAp, ap,
+ pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, apa, config) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config))
)
}
@@ -1434,146 +1567,194 @@ private module MkStage {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
- fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
+ fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
- fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
+ fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
+ private predicate returnFlowsThrough0(
+ DataFlowCall call, FlowState state, CcCall ccc, Ap ap, ApApprox apa, RetNodeEx ret,
+ ParamNodeEx innerSummaryCtx, Ap innerArgAp, ApApprox innerArgApa, Configuration config
+ ) {
+ fwdFlowThrough0(call, _, state, ccc, _, _, ap, apa, ret, innerSummaryCtx, innerArgAp,
+ innerArgApa, config)
+ }
+
+ pragma[nomagic]
+ private predicate returnFlowsThrough(
+ RetNodeEx ret, ReturnPosition pos, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
+ Ap ap, Configuration config
+ ) {
+ exists(DataFlowCall call, ApApprox apa, boolean allowsFieldFlow, ApApprox innerArgApa |
+ returnFlowsThrough0(call, state, ccc, ap, apa, ret, p, argAp, innerArgApa, config) and
+ flowThroughOutOfCall(call, ccc, ret, _, allowsFieldFlow, innerArgApa, apa, config) and
+ pos = ret.getReturnPosition() and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap argAp, Ap ap,
+ 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))
+ exists(ApApprox argApa |
+ flowIntoCallApa(call, pragma[only_bind_into](arg), pragma[only_bind_into](p),
+ allowsFieldFlow, argApa, pragma[only_bind_into](config)) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), argApa,
+ pragma[only_bind_into](config)) and
+ returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ if allowsFieldFlow = false then argAp instanceof ApNil else any()
+ )
}
pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ private predicate flowIntoCallAp(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Ap ap,
+ Configuration config
) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
+ exists(ApApprox apa |
+ flowIntoCallApa(call, arg, p, allowsFieldFlow, apa, config) and
+ fwdFlow(arg, _, _, _, _, ap, apa, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowOutOfCallAp(
+ DataFlowCall call, RetNodeEx ret, ReturnPosition pos, NodeEx out, boolean allowsFieldFlow,
+ Ap ap, Configuration config
+ ) {
+ exists(ApApprox apa |
+ flowOutOfCallApa(call, ret, _, out, allowsFieldFlow, apa, config) and
+ fwdFlow(ret, _, _, _, _, ap, apa, config) and
+ pos = ret.getReturnPosition()
+ )
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
- * The Boolean `toReturn` records whether the node must be returned from the
- * enclosing callable in order to reach a sink, and if so, `returnAp` records
- * the access path of the returned value.
+ * The parameter `returnCtx` records whether (and how) the node must be returned
+ * from the enclosing callable in order to reach a sink, and if so, `returnAp`
+ * records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
+ Configuration config
) {
- revFlow0(node, state, toReturn, returnAp, ap, config) and
- fwdFlow(node, state, _, _, ap, config)
+ revFlow0(node, state, returnCtx, returnAp, ap, config) and
+ fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
+ Configuration config
) {
- fwdFlow(node, state, _, _, ap, config) and
+ fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
+ (
+ if hasSinkCallCtx(config)
+ then returnCtx = TReturnCtxNoFlowThrough()
+ else returnCtx = TReturnCtxNone()
+ ) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
- revFlow(mid, state0, toReturn, returnAp, ap, config)
+ revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
- revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
+ revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
- fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
- toReturn = false and
+ returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
+ flowIntoCallAp(_, node, p, allowsFieldFlow, ap, config) and
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ returnCtx = TReturnCtxNone()
+ )
or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ // flow through a callable
+ exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
+ revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
+ flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
)
or
// flow out of a callable
- revFlowOut(_, node, state, _, _, ap, config) and
- toReturn = true and
- if returnNodeMayFlowThrough(node, state, ap, config)
- then returnAp = apSome(ap)
- else returnAp = apNone()
+ exists(ReturnPosition pos |
+ revFlowOut(_, node, pos, state, _, _, ap, config) and
+ if returnFlowsThrough(node, pos, state, _, _, _, ap, config)
+ then (
+ returnCtx = TReturnCtxMaybeFlowThrough(pos) and
+ returnAp = apSome(ap)
+ ) else (
+ returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
+ )
+ )
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
- boolean toReturn, ApOption returnAp, Configuration config
+ ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1593,36 +1774,33 @@ private module MkStage {
pragma[nomagic]
private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
- Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnPosition pos, FlowState state, ReturnCtx returnCtx,
+ ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
- revFlow(out, state, toReturn, returnAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ revFlow(out, state, returnCtx, returnAp, ap, config) and
+ flowOutOfCallAp(call, ret, pos, out, allowsFieldFlow, ap, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate revFlowInNotToReturn(
- ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
+ private predicate revFlowParamToReturn(
+ ParamNodeEx p, FlowState state, ReturnPosition pos, Ap returnAp, Ap ap, Configuration config
) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, false, returnAp, ap, config) and
- flowIntoCall(_, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ revFlow(pragma[only_bind_into](p), state, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp),
+ pragma[only_bind_into](ap), pragma[only_bind_into](config)) and
+ parameterFlowThroughAllowed(p, pos.getKind()) and
+ PrevStage::parameterMayFlowThrough(p, getApprox(ap), config)
}
pragma[nomagic]
- private predicate revFlowInToReturn(
- DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
+ private predicate revFlowThrough(
+ DataFlowCall call, ReturnCtx returnCtx, ParamNodeEx p, FlowState state, ReturnPosition pos,
+ ApOption returnAp, Ap ap, Ap innerReturnAp, Configuration config
) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
+ revFlowParamToReturn(p, state, pos, innerReturnAp, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, pos, innerReturnAp, config)
}
/**
@@ -1632,11 +1810,12 @@ private module MkStage {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnPosition pos, Ap ap,
+ Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
- revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
- fwdFlow(ret, state, ccc, apSome(_), ap, config) and
+ revFlowOut(call, ret, pos, state, returnCtx, returnAp, ap, config) and
+ returnFlowsThrough(ret, pos, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1673,6 +1852,11 @@ private module MkStage {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+ pragma[nomagic]
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
+ revFlow(node, _, _, _, ap, config)
+ }
+
// use an alias as a workaround for bad functionality-induced joins
pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) {
@@ -1707,40 +1891,49 @@ private module MkStage {
validAp(ap, config)
}
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ pragma[nomagic]
+ private predicate parameterFlowsThroughRev(
+ ParamNodeEx p, Ap ap, ReturnPosition pos, Ap returnAp, Configuration config
) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ revFlow(p, _, TReturnCtxMaybeFlowThrough(pos), apSome(returnAp), ap, config) and
+ parameterFlowThroughAllowed(p, pos.getKind())
}
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
- parameterFlow(p, ap, ap0, c, config) and
- c = ret.getEnclosingCallable() and
- revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
- pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
- fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
- kind = ret.getKind() and
- p.getPosition() = pos and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = pos
- or
- p.allowParameterReturnInSelf()
- )
+ pragma[nomagic]
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
+ exists(RetNodeEx ret, ReturnPosition pos |
+ returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, pos, _, config)
+ )
+ }
+
+ pragma[nomagic]
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ ) {
+ exists(ParamNodeEx p, ReturnPosition pos |
+ returnFlowsThrough(ret, pos, _, _, p, argAp, ap, config) and
+ parameterFlowsThroughRev(p, argAp, pos, ap, config) and
+ kind = pos.getKind()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowThroughArg(
+ DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
+ Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
+ revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
+ flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ exists(ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap |
+ revFlow(arg, state, returnCtx, returnAp, ap, config) and
+ revFlowThroughArg(call, arg, state, returnCtx, returnAp, ap, config)
)
}
@@ -1748,13 +1941,13 @@ private module MkStage {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
- nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
+ nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
- states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
+ states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
+ count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, summaryCtx, argAp, ap, config)
)
or
fwd = false and
@@ -1763,8 +1956,8 @@ private module MkStage {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
- count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
- revFlow(n, state, b, retAp, ap, config)
+ count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
+ revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1909,7 +2102,7 @@ private module Stage2Param implements MkStage::StageParam {
exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1945,9 +2138,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
- DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
) {
- flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
+ flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2015,8 +2209,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
- flowIntoCallNodeCand1(_, node, next, config) or
- flowOutOfCallNodeCand1(_, node, next, config) or
+ flowIntoCallNodeCand2(_, node, next, _, config) or
+ flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2103,16 +2297,16 @@ private module LocalFlowBigStep {
pragma[nomagic]
predicate localFlowBigStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
- AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
+ DataFlowType t, Configuration config, LocalCallContext callContext
) {
- localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
+ localFlowStepPlus(node1, state1, node2, preservesValue, t, config, callContext) and
localFlowExit(node2, state1, config) and
state1 = state2
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
state1 != state2 and
preservesValue = false and
- apf = TFrontNil(node2.getDataFlowType()) and
+ t = node2.getDataFlowType() and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
@@ -2126,11 +2320,87 @@ private import LocalFlowBigStep
private module Stage3Param implements MkStage::StageParam {
private module PrevStage = Stage2;
+ class Ap = ApproxAccessPathFront;
+
+ class ApNil = ApproxAccessPathFrontNil;
+
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+
+ ApNil getApNil(NodeEx node) {
+ PrevStage::revFlow(node, _) and result = TApproxFrontNil(node.getDataFlowType())
+ }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
+
+ pragma[noinline]
+ Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
+
+ class ApOption = ApproxAccessPathFrontOption;
+
+ ApOption apNone() { result = TApproxAccessPathFrontNone() }
+
+ ApOption apSome(Ap ap) { result = TApproxAccessPathFrontSome(ap) }
+
+ import BooleanCallContext
+
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApproxAccessPathFrontNil ap, Configuration config, LocalCc lcc
+ ) {
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
+ exists(lcc)
+ }
+
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
+
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
+
+ pragma[nomagic]
+ private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
+ exists(Content c |
+ PrevStage::revFlow(node, pragma[only_bind_into](config)) and
+ PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
+ expectsContentEx(node, c) and
+ c = ap.getAHead().getContent()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ exists(state) and
+ exists(config) and
+ (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
+ (
+ notExpectsContent(node)
+ or
+ expectsContentCand(node, ap, config)
+ )
+ }
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ // We need to typecheck stores here, since reverse flow through a getter
+ // might have a different type here compared to inside the getter.
+ compatibleTypes(ap.getType(), contentType)
+ }
+}
+
+private module Stage3 implements StageSig {
+ import MkStage::Stage
+}
+
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
+
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toApprox() }
ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
@@ -2150,16 +2420,42 @@ private module Stage3Param implements MkStage::StageParam {
import BooleanCallContext
+ pragma[nomagic]
predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
- localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, _) and
+ PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config)) and
+ exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ pragma[nomagic]
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ exists(FlowState state |
+ flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
+ pragma[only_bind_into](config))
+ )
+ }
- predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ pragma[nomagic]
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ exists(FlowState state |
+ flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
+ pragma[only_bind_into](config))
+ )
+ }
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2215,8 +2511,8 @@ private module Stage3Param implements MkStage::StageParam {
}
}
-private module Stage3 implements StageSig {
- import MkStage::Stage
+private module Stage4 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2227,8 +2523,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
- Stage3::revFlow(node, state, true, _, apf, config) and
- Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
+ Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
+ Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
+ config)
)
}
@@ -2238,10 +2535,10 @@ private predicate flowCandSummaryCtx(
*/
private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) {
exists(int tails, int nodes, int apLimit, int tupleLimit |
- tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
+ tails = strictcount(AccessPathFront apf | Stage4::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage4::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2255,11 +2552,11 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
private newtype TAccessPathApprox =
TNil(DataFlowType t) or
TConsNil(TypedContent tc, DataFlowType t) {
- Stage3::consCand(tc, TFrontNil(t), _) and
+ Stage4::consCand(tc, TFrontNil(t), _) and
not expensiveLen2unfolding(tc, _)
} or
TConsCons(TypedContent tc1, TypedContent tc2, int len) {
- Stage3::consCand(tc1, TFrontHead(tc2), _) and
+ Stage4::consCand(tc1, TFrontHead(tc2), _) and
len in [2 .. accessPathLimit()] and
not expensiveLen2unfolding(tc1, _)
} or
@@ -2389,7 +2686,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
override AccessPathApprox pop(TypedContent head) {
head = tc and
(
- exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) |
+ exists(TypedContent tc2 | Stage4::consCand(tc, TFrontHead(tc2), _) |
result = TConsCons(tc2, _, len - 1)
or
len = 2 and
@@ -2400,7 +2697,7 @@ private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 {
or
exists(DataFlowType t |
len = 1 and
- Stage3::consCand(tc, TFrontNil(t), _) and
+ Stage4::consCand(tc, TFrontNil(t), _) and
result = TNil(t)
)
)
@@ -2425,13 +2722,14 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4Param implements MkStage::StageParam {
- private module PrevStage = Stage3;
+private module Stage5Param implements MkStage::StageParam {
+ private module PrevStage = Stage4;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
+ pragma[nomagic]
PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
ApNil getApNil(NodeEx node) {
@@ -2457,15 +2755,18 @@ private module Stage4Param implements MkStage::StageParam {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
- localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc)
+ localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getType(), config, lcc) and
+ PrevStage::revFlow(node1, pragma[only_bind_into](state1), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node2, pragma[only_bind_into](state2), _, pragma[only_bind_into](config))
}
pragma[nomagic]
predicate flowOutOfCall(
- DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
+ Configuration config
) {
exists(FlowState state |
- flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
+ flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2493,7 +2794,7 @@ private module Stage4Param implements MkStage::StageParam {
predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
-private module Stage4 = MkStage::Stage;
+private module Stage5 = MkStage::Stage;
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
@@ -2502,13 +2803,13 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
- NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
+ NodeEx n, ParamNodeEx p, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(AccessPathApprox apa0 |
- Stage4::parameterMayFlowThrough(_, c, _, _) and
- Stage4::revFlow(n, state, true, _, apa0, config) and
- Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
- n.getEnclosingCallable() = c
+ Stage5::parameterMayFlowThrough(p, _, _) and
+ Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
+ Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()),
+ TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2516,9 +2817,9 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
- exists(DataFlowCallable c |
- Stage4::parameterMayFlowThrough(_, c, apa, config) and
- nodeMayUseSummary0(n, c, state, apa, config)
+ exists(ParamNodeEx p |
+ Stage5::parameterMayFlowThrough(p, apa, config) and
+ nodeMayUseSummary0(n, p, state, apa, config)
)
}
@@ -2526,8 +2827,8 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
- Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, config)
+ Stage5::parameterMayFlowThrough(p, ap.getApprox(), config) and
+ Stage5::revFlow(p, state, _, config)
)
}
@@ -2576,7 +2877,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
len = apa.len() and
result =
strictcount(AccessPathFront apf |
- Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
+ Stage5::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1),
config)
)
)
@@ -2585,7 +2886,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage5::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -2606,7 +2907,7 @@ private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configura
private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
exists(TypedContent head |
apa.pop(head) = result and
- Stage4::consCand(head, result, config)
+ Stage5::consCand(head, result, config)
)
}
@@ -2688,7 +2989,7 @@ private newtype TPathNode =
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) {
// A PathNode is introduced by a source ...
- Stage4::revFlow(node, state, config) and
+ Stage5::revFlow(node, state, config) and
sourceNode(node, state, config) and
(
if hasSourceCallCtx(config)
@@ -2702,7 +3003,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
+ Stage5::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -2712,6 +3013,18 @@ private newtype TPathNode =
state = sink.getState() and
config = sink.getConfiguration()
)
+ } or
+ TPathNodeSourceGroup(string sourceGroup, Configuration config) {
+ exists(PathNodeImpl source |
+ sourceGroup = source.getSourceGroup() and
+ config = source.getConfiguration()
+ )
+ } or
+ TPathNodeSinkGroup(string sinkGroup, Configuration config) {
+ exists(PathNodeSink sink |
+ sinkGroup = sink.getSinkGroup() and
+ config = sink.getConfiguration()
+ )
}
/**
@@ -2832,7 +3145,7 @@ private class AccessPathCons2 extends AccessPath, TAccessPathCons2 {
override TypedContent getHead() { result = head1 }
override AccessPath getTail() {
- Stage4::consCand(head1, result.getApprox(), _) and
+ Stage5::consCand(head1, result.getApprox(), _) and
result.getHead() = head2 and
result.length() = len - 1
}
@@ -2863,7 +3176,7 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
override TypedContent getHead() { result = head }
override AccessPath getTail() {
- Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1
+ Stage5::consCand(head, result.getApprox(), _) and result.length() = len - 1
}
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
@@ -2920,6 +3233,22 @@ abstract private class PathNodeImpl extends TPathNode {
)
}
+ string getSourceGroup() {
+ this.isSource() and
+ this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result)
+ }
+
+ predicate isFlowSource() {
+ this.isSource() and not exists(this.getSourceGroup())
+ or
+ this instanceof PathNodeSourceGroup
+ }
+
+ predicate isFlowSink() {
+ this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or
+ this instanceof PathNodeSinkGroup
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2959,7 +3288,9 @@ abstract private class PathNodeImpl extends TPathNode {
/** Holds if `n` can reach a sink. */
private predicate directReach(PathNodeImpl n) {
- n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
+ n instanceof PathNodeSink or
+ n instanceof PathNodeSinkGroup or
+ directReach(n.getANonHiddenSuccessor())
}
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
@@ -3015,6 +3346,12 @@ class PathNode instanceof PathNodeImpl {
/** Holds if this node is a source. */
final predicate isSource() { super.isSource() }
+
+ /** Holds if this node is a grouping of source nodes. */
+ final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) }
+
+ /** Holds if this node is a grouping of sink nodes. */
+ final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) }
}
/**
@@ -3136,9 +3473,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNodeImpl getASuccessorImpl() { none() }
+ override PathNodeImpl getASuccessorImpl() {
+ result = TPathNodeSinkGroup(this.getSinkGroup(), config)
+ }
override predicate isSource() { sourceNode(node, state, config) }
+
+ string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
+}
+
+private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
+ string sourceGroup;
+ Configuration config;
+
+ PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
+
+ override NodeEx getNodeEx() { none() }
+
+ override FlowState getState() { none() }
+
+ override Configuration getConfiguration() { result = config }
+
+ override PathNodeImpl getASuccessorImpl() {
+ result.getSourceGroup() = sourceGroup and
+ result.getConfiguration() = config
+ }
+
+ override predicate isSource() { none() }
+
+ override string toString() { result = sourceGroup }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
+ }
+}
+
+private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
+ string sinkGroup;
+ Configuration config;
+
+ PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
+
+ override NodeEx getNodeEx() { none() }
+
+ override FlowState getState() { none() }
+
+ override Configuration getConfiguration() { result = config }
+
+ override PathNodeImpl getASuccessorImpl() { none() }
+
+ override predicate isSource() { none() }
+
+ override string toString() { result = sinkGroup }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
+ }
}
private predicate pathNode(
@@ -3174,7 +3568,8 @@ private predicate pathStep(
AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC
|
pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and
- localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and
+ localFlowBigStep(midnode, state0, node, state, false, ap.(AccessPathNil).getType(), conf,
+ localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -3216,7 +3611,7 @@ private predicate pathReadStep(
) {
ap0 = mid.getAp() and
tc = ap0.getHead() and
- Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
+ Stage5::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3226,7 +3621,7 @@ private predicate pathStoreStep(
PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
+ Stage5::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3263,7 +3658,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, apa, config)
+ Stage5::revFlow(result, _, apa, config)
}
/**
@@ -3299,7 +3694,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, apa, config) and
+ Stage5::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
@@ -3354,17 +3749,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
- exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
+ exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
- pos = sc.getParameterPos() and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = pos
- or
- sc.getParamNode().allowParameterReturnInSelf()
- )
+ parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}
@@ -3495,6 +3884,15 @@ private module Subpaths {
* Will only have results if `configuration` has non-empty sources and
* sinks.
*/
+private predicate hasFlowPath(
+ PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration
+) {
+ flowsource.isFlowSource() and
+ flowsource.getConfiguration() = configuration and
+ (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
+ flowsink.isFlowSink()
+}
+
private predicate flowsTo(
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink,
Configuration configuration
@@ -3575,9 +3973,17 @@ predicate stageStats(
n = 45 and
Stage4::stats(false, nodes, fields, conscand, states, tuples, config)
or
- stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples)
+ stage = "5 Fwd" and
+ n = 50 and
+ Stage5::stats(true, nodes, fields, conscand, states, tuples, config)
or
- stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples)
+ stage = "5 Rev" and
+ n = 55 and
+ Stage5::stats(false, nodes, fields, conscand, states, tuples, config)
+ or
+ stage = "6 Fwd" and n = 60 and finalStats(true, nodes, fields, conscand, states, tuples)
+ or
+ stage = "6 Rev" and n = 65 and finalStats(false, nodes, fields, conscand, states, tuples)
}
private module FlowExploration {
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
index ae9c6f3f12e..9aa6a529a5b 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
@@ -915,18 +915,57 @@ private module Cached {
TDataFlowCallNone() or
TDataFlowCallSome(DataFlowCall call)
+ cached
+ newtype TParamNodeOption =
+ TParamNodeNone() or
+ TParamNodeSome(ParamNode p)
+
+ cached
+ newtype TReturnCtx =
+ TReturnCtxNone() or
+ TReturnCtxNoFlowThrough() or
+ TReturnCtxMaybeFlowThrough(ReturnPosition pos)
+
+ cached
+ newtype TTypedContentApprox =
+ MkTypedContentApprox(ContentApprox c, DataFlowType t) {
+ exists(Content cont |
+ c = getContentApprox(cont) and
+ store(_, cont, _, _, t)
+ )
+ }
+
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
+ cached
+ TypedContent getATypedContent(TypedContentApprox c) {
+ exists(ContentApprox cls, DataFlowType t, Content cont |
+ c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
+ result = MkTypedContent(cont, pragma[only_bind_into](t)) and
+ cls = getContentApprox(cont)
+ )
+ }
+
cached
newtype TAccessPathFront =
TFrontNil(DataFlowType t) or
TFrontHead(TypedContent tc)
+ cached
+ newtype TApproxAccessPathFront =
+ TApproxFrontNil(DataFlowType t) or
+ TApproxFrontHead(TypedContentApprox tc)
+
cached
newtype TAccessPathFrontOption =
TAccessPathFrontNone() or
TAccessPathFrontSome(AccessPathFront apf)
+
+ cached
+ newtype TApproxAccessPathFrontOption =
+ TApproxAccessPathFrontNone() or
+ TApproxAccessPathFrontSome(ApproxAccessPathFront apf)
}
/**
@@ -1304,6 +1343,113 @@ class DataFlowCallOption extends TDataFlowCallOption {
}
}
+/** An optional `ParamNode`. */
+class ParamNodeOption extends TParamNodeOption {
+ string toString() {
+ this = TParamNodeNone() and
+ result = "(none)"
+ or
+ exists(ParamNode p |
+ this = TParamNodeSome(p) and
+ result = p.toString()
+ )
+ }
+}
+
+/**
+ * A return context used to calculate flow summaries in reverse flow.
+ *
+ * The possible values are:
+ *
+ * - `TReturnCtxNone()`: no return flow.
+ * - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible.
+ * - `TReturnCtxMaybeFlowThrough(ReturnPosition pos)`: return flow, of kind `pos`, and
+ * flow through may be possible.
+ */
+class ReturnCtx extends TReturnCtx {
+ string toString() {
+ this = TReturnCtxNone() and
+ result = "(none)"
+ or
+ this = TReturnCtxNoFlowThrough() and
+ result = "(no flow through)"
+ or
+ exists(ReturnPosition pos |
+ this = TReturnCtxMaybeFlowThrough(pos) and
+ result = pos.toString()
+ )
+ }
+}
+
+/** An approximated `Content` tagged with the type of a containing object. */
+class TypedContentApprox extends MkTypedContentApprox {
+ private ContentApprox c;
+ private DataFlowType t;
+
+ TypedContentApprox() { this = MkTypedContentApprox(c, t) }
+
+ /** Gets a typed content approximated by this value. */
+ TypedContent getATypedContent() { result = getATypedContent(this) }
+
+ /** Gets the container type. */
+ DataFlowType getContainerType() { result = t }
+
+ /** Gets a textual representation of this approximated content. */
+ string toString() { result = c.toString() }
+}
+
+/**
+ * The front of an approximated access path. This is either a head or a nil.
+ */
+abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
+ abstract string toString();
+
+ abstract DataFlowType getType();
+
+ abstract boolean toBoolNonEmpty();
+
+ pragma[nomagic]
+ TypedContent getAHead() {
+ exists(TypedContentApprox cont |
+ this = TApproxFrontHead(cont) and
+ result = cont.getATypedContent()
+ )
+ }
+}
+
+class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
+ private DataFlowType t;
+
+ ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
+
+ override string toString() { result = ppReprType(t) }
+
+ override DataFlowType getType() { result = t }
+
+ override boolean toBoolNonEmpty() { result = false }
+}
+
+class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
+ private TypedContentApprox tc;
+
+ ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
+
+ override string toString() { result = tc.toString() }
+
+ override DataFlowType getType() { result = tc.getContainerType() }
+
+ override boolean toBoolNonEmpty() { result = true }
+}
+
+/** An optional approximated access path front. */
+class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
+ string toString() {
+ this = TApproxAccessPathFrontNone() and result = ""
+ or
+ this = TApproxAccessPathFrontSome(any(ApproxAccessPathFront apf | result = apf.toString()))
+ }
+}
+
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
@@ -1336,7 +1482,7 @@ abstract class AccessPathFront extends TAccessPathFront {
abstract DataFlowType getType();
- abstract boolean toBoolNonEmpty();
+ abstract ApproxAccessPathFront toApprox();
TypedContent getHead() { this = TFrontHead(result) }
}
@@ -1350,7 +1496,7 @@ class AccessPathFrontNil extends AccessPathFront, TFrontNil {
override DataFlowType getType() { result = t }
- override boolean toBoolNonEmpty() { result = false }
+ override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
}
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
@@ -1362,7 +1508,7 @@ class AccessPathFrontHead extends AccessPathFront, TFrontHead {
override DataFlowType getType() { result = tc.getContainerType() }
- override boolean toBoolNonEmpty() { result = true }
+ override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
}
/** An optional access path front. */
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll
index dde16ab5a2a..e85e0cd92ec 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll
@@ -136,6 +136,18 @@ module Consistency {
msg = "Local flow step does not preserve enclosing callable."
}
+ query predicate readStepIsLocal(Node n1, Node n2, string msg) {
+ readStep(n1, _, n2) and
+ nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
+ msg = "Read step does not preserve enclosing callable."
+ }
+
+ query predicate storeStepIsLocal(Node n1, Node n2, string msg) {
+ storeStep(n1, _, n2) and
+ nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
+ msg = "Store step does not preserve enclosing callable."
+ }
+
private DataFlowType typeRepr() { result = getNodeType(_) }
query predicate compatibleTypesReflexive(DataFlowType t, string msg) {
@@ -232,4 +244,25 @@ module Consistency {
not callable = viableCallable(call) and
not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable)
}
+
+ query predicate uniqueParameterNodeAtPosition(
+ DataFlowCallable c, ParameterPosition pos, Node p, string msg
+ ) {
+ isParameterNode(p, c, pos) and
+ not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
+ msg = "Parameters with overlapping positions."
+ }
+
+ query predicate uniqueParameterNodePosition(
+ DataFlowCallable c, ParameterPosition pos, Node p, string msg
+ ) {
+ isParameterNode(p, c, pos) and
+ not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
+ msg = "Parameter node with multiple positions."
+ }
+
+ query predicate uniqueContentApprox(Content c, string msg) {
+ not exists(unique(ContentApprox approx | approx = getContentApprox(c))) and
+ msg = "Non-unique content approximation."
+ }
}
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
index 2dde1c2819d..1228d00b6ba 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
- predicate isSink(Node source, FlowState state) { none() }
+ predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/
FlowFeature getAFeature() { none() }
+ /** Holds if sources should be grouped in the result of `hasFlowPath`. */
+ predicate sourceGrouping(Node source, string sourceGroup) { none() }
+
+ /** Holds if sinks should be grouped in the result of `hasFlowPath`. */
+ predicate sinkGrouping(Node sink, string sinkGroup) { none() }
+
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
* The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`.
*/
- predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
+ predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
/**
* Holds if data may flow from some source to `sink` for this configuration.
@@ -313,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -602,8 +606,27 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
+/**
+ * Holds if flow from `p` to a return node of kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[p, kind]
+private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
+ exists(ParameterPosition pos | p.isParameterOf(_, pos) |
+ not kind.(ParamUpdateReturnKind).getPosition() = pos
+ or
+ allowParameterReturnInSelfCached(p.asNode())
+ )
+}
+
private module Stage1 implements StageSig {
- class Ap = Unit;
+ class Ap extends int {
+ // workaround for bad functionality-induced joins (happens when using `Unit`)
+ pragma[nomagic]
+ Ap() { this in [0 .. 1] and this < 1 }
+ }
private class Cc = boolean;
@@ -944,6 +967,12 @@ private module Stage1 implements StageSig {
pragma[nomagic]
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
+ pragma[nomagic]
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config) {
+ revFlow(node, config) and
+ exists(ap)
+ }
+
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, _, pragma[only_bind_into](config)) and
@@ -975,21 +1004,26 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
- exists(ReturnKindExt kind |
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
+ exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
- // we don't expect a parameter to return stored in itself, unless explicitly allowed
- (
- not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
- or
- p.allowParameterReturnInSelf()
- )
+ parameterFlowThroughAllowed(p, kind)
)
}
+ pragma[nomagic]
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ ) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind() and
+ exists(argAp) and
+ exists(ap)
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1046,12 +1080,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
- viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
- Stage1::revFlow(ret, config) and
- not outBarrier(ret, config) and
- not inBarrier(out, config)
+ exists(ReturnPosition pos |
+ viableReturnPosOutNodeCand1(call, pos, out, config) and
+ pos = ret.getReturnPosition() and
+ kind = pos.getKind() and
+ Stage1::revFlow(ret, config) and
+ not outBarrier(ret, config) and
+ not inBarrier(out, config)
+ )
}
pragma[nomagic]
@@ -1081,10 +1119,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
+pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
- flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
+ flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1093,10 +1132,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
+pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
- flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
+ flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1109,12 +1149,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
- DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
) {
- flowOutOfCallNodeCand1(call, ret, out, config) and
+ flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
- b = branch(ret, config) and
- j = join(out, config) and
+ b = branch(ret, pragma[only_bind_into](config)) and
+ j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1130,10 +1171,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- flowIntoCallNodeCand1(call, arg, p, config) and
+ flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
- b = branch(arg, config) and
- j = join(p, config) and
+ b = branch(arg, pragma[only_bind_into](config)) and
+ j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1145,12 +1186,18 @@ private signature module StageSig {
predicate revFlow(NodeEx node, Configuration config);
+ predicate revFlowAp(NodeEx node, Ap ap, Configuration config);
+
bindingset[node, state, config]
predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
- predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+ predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
+
+ predicate returnMayFlowThrough(
+ RetNodeEx ret, Ap argAp, Ap ap, ReturnKindExt kind, Configuration config
+ );
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1216,7 +1263,8 @@ private module MkStage {
);
predicate flowOutOfCall(
- DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
);
predicate flowIntoCall(
@@ -1234,21 +1282,43 @@ private module MkStage {
import Param
/* Begin: Stage logic. */
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ private predicate revFlowApAlias(NodeEx node, ApApprox apa, Configuration config) {
+ PrevStage::revFlowAp(node, apa, config)
+ }
+
+ pragma[nomagic]
+ private predicate flowIntoCallApa(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, ApApprox apa,
+ Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ PrevStage::revFlowAp(p, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ revFlowApAlias(arg, pragma[only_bind_into](apa), pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate flowOutOfCallApa(
+ DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
+ ApApprox apa, Configuration config
+ ) {
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
+ PrevStage::revFlowAp(out, pragma[only_bind_into](apa), pragma[only_bind_into](config)) and
+ revFlowApAlias(ret, pragma[only_bind_into](apa), pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ ApApprox argApa, ApApprox apa, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- matchesCall(ccc, call)
+ exists(ReturnKindExt kind |
+ flowOutOfCallApa(call, ret, kind, out, allowsFieldFlow, apa, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::returnMayFlowThrough(ret, argApa, apa, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ )
}
/**
@@ -1256,99 +1326,134 @@ private module MkStage {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
- * argument in a call, and if so, `argAp` records the access path of that
- * argument.
+ * argument in a call, and if so, `summaryCtx` and `argAp` record the
+ * corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
- NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ ApApprox apa, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, apa, config) and
+ PrevStage::revFlow(node, state, apa, config) and
filter(node, state, ap, config)
}
+ pragma[inline]
+ additional predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ Configuration config
+ ) {
+ fwdFlow(node, state, cc, summaryCtx, argAp, ap, _, config)
+ }
+
pragma[nomagic]
private predicate fwdFlow0(
- NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, Ap ap,
+ ApApprox apa, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
- ap = getApNil(node)
+ summaryCtx = TParamNodeNone() and
+ ap = getApNil(node) and
+ apa = getApprox(ap)
or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ exists(NodeEx mid, FlowState state0, Ap ap0, ApApprox apa0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, apa0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
+ ap = ap0 and
+ apa = apa0
or
localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
+ ap0 instanceof ApNil and
+ apa = getApprox(ap)
)
or
exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, apa, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone() and
- ap = getApNil(node)
+ ap = getApNil(node) and
+ apa = getApprox(ap)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
- fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
+ fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
+ summaryCtx = TParamNodeNone() and
argAp = apNone() and
- ap = getApNil(node)
+ ap = getApNil(node) and
+ apa = getApprox(ap)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
+ ap = apCons(tc, ap0) and
+ apa = getApprox(ap)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config) and
+ apa = getApprox(ap)
)
or
// flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, apa, config) and
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParamNodeSome(node.asNode()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParamNodeNone() and argAp = apNone()
)
or
// flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, apa, config) and
+ flowOutOfCallApa(call, ret, _, node, allowsFieldFlow, apa, config) and
+ inner = ret.getEnclosingCallable() and
+ cc = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ // flow through a callable
+ exists(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, boolean allowsFieldFlow, ApApprox innerArgApa
+ |
+ fwdFlowThrough(call, cc, state, ccc, summaryCtx, argAp, ap, apa, ret, innerArgApa, config) and
+ flowThroughOutOfCall(call, ccc, ret, node, allowsFieldFlow, innerArgApa, apa, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
+ ParamNodeOption summaryCtx, ApOption argAp, Configuration config
) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ exists(DataFlowType contentType, ApApprox apa1 |
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, apa1, config) and
+ PrevStage::storeStepCand(node1, apa1, tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
}
@@ -1360,60 +1465,85 @@ private module MkStage