diff --git a/.github/ISSUE_TEMPLATE/ql---general.md b/.github/ISSUE_TEMPLATE/ql---general.md
index 1d5b7d244f65..f68574485c10 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
index 7f22c577d144..b80590a18324 100644
--- a/.github/ISSUE_TEMPLATE/ql--false-positive.md
+++ b/.github/ISSUE_TEMPLATE/ql--false-positive.md
@@ -1,5 +1,5 @@
---
-name: CodeQL False positive
+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
diff --git a/.github/actions/cache-query-compilation/action.yml b/.github/actions/cache-query-compilation/action.yml
new file mode 100644
index 000000000000..95f25cbfc686
--- /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 000000000000..67fc503cdc0b
--- /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/workflows/compile-queries.yml b/.github/workflows/compile-queries.yml
index 94378250c857..96d8e4cc30ba 100644
--- a/.github/workflows/compile-queries.yml
+++ b/.github/workflows/compile-queries.yml
@@ -14,58 +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: 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: |
- # Move all the existing cache into another folder, so we only preserve the cache for the current queries.
- mkdir -p ${COMBINED_CACHE_DIR}
- rm */ql/src/.cache/{lock,size}
- # copy the contents of the .cache folders into the combined cache folder.
- cp -r */ql/src/.cache/* ${COMBINED_CACHE_DIR}/
- # clean up the .cache folders
- rm -rf */ql/src/.cache/*
-
- # compile the queries
- codeql query compile -j0 */ql/src --keep-going --warnings=error --compilation-cache ${COMBINED_CACHE_DIR}
- env:
- COMBINED_CACHE_DIR: ${{ github.workspace }}/compilation-dir
\ 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 000000000000..b9bbf930adda
--- /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 000000000000..edf6eb63d491
--- /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 3cc61eafa5c9..eceabb0410a6 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
- 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
-
- test-win:
- name: Test Windows
- runs-on: windows-2019
- 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
+ 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 d3b5d49f2ba5..90cb56911267 100644
--- a/.github/workflows/js-ml-tests.yml
+++ b/.github/workflows/js-ml-tests.yml
@@ -23,9 +23,9 @@ defaults:
working-directory: javascript/ql/experimental/adaptivethreatmodeling
jobs:
- qlcompile:
- name: Check QL compilation
- runs-on: ubuntu-latest
+ qltest:
+ name: Test QL
+ runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v3
@@ -33,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/ql-for-ql-tests.yml b/.github/workflows/ql-for-ql-tests.yml
index b820d00a3e4a..ce7963e8f790 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 f7ec215a44a5..f561af9c13bc 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 125e2694fb09..370375cea939 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,16 +28,6 @@ defaults:
working-directory: ruby
jobs:
- 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:
@@ -58,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 797d950ee571..47a2d2bf6ae3 100644
--- a/.github/workflows/swift.yml
+++ b/.github/workflows/swift.yml
@@ -7,95 +7,76 @@ 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
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:
@@ -113,6 +94,7 @@ jobs:
name: swift-generated-cpp-files
path: generated-cpp-files/**
database-upgrade-scripts:
+ if : ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
diff --git a/.gitignore b/.gitignore
index 7b8532b00d2c..c81e23fc7f82 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 14845337b36b..5f35c2c183b5 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -44,7 +44,7 @@ repos:
- id: swift-codegen
name: Run Swift checked in code generation
- files: ^swift/(schema.py$|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 8b22c91bb77c..1050c79b825e 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 86f38eeee22a..42fb364418f4 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.yml @github/codeql-c
+/.github/workflows/swift.yml @github/codeql-swift
diff --git a/codeql-workspace.yml b/codeql-workspace.yml
index f93ed4ac5c88..c2258bd1363f 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 20d7dbc36063..0a5d037e226c 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"
@@ -505,6 +511,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",
diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs b/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs
index 76acba2eee48..def45890c9fd 100644
--- a/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs
+++ b/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs
@@ -257,11 +257,11 @@ CppAutobuilder CreateAutoBuilder(bool isWindows,
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 @@ public void TestCppAutobuilderSuccess()
{
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/CppAutobuilder.cs b/cpp/autobuilder/Semmle.Autobuild.Cpp/CppAutobuilder.cs
index 44c34656a2a8..1503dedb376f 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 3f4627c53d54..a7556197bcdd 100644
--- a/cpp/autobuilder/Semmle.Autobuild.Cpp/Program.cs
+++ b/cpp/autobuilder/Semmle.Autobuild.Cpp/Program.cs
@@ -11,14 +11,14 @@ static int Main()
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/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 000000000000..eb6bd755c2bc
--- /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 000000000000..0f87b8d8bcdd
--- /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 000000000000..bf2d5a07de6e
--- /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/definitions.qll b/cpp/ql/lib/definitions.qll
index cb229d66ef1d..e4a2aca98f65 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 3c41b1876dc1..ccde916a3c2e 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
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ 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())
+ )
+}
+
+/**
+ * Holds if flow from a parameter at position `pos` inside `c` to a return node of
+ * kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[c, pos, kind]
+private predicate parameterFlowThroughAllowed(
+ DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
+) {
+ exists(ParamNodeEx p |
+ p.isParameterOf(c, pos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
+}
+
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ 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, ReturnKindExt kind, Configuration config) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind()
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,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]
@@ -1087,10 +1122,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)
)
}
@@ -1099,10 +1135,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)
)
}
@@ -1115,12 +1152,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
@@ -1136,10 +1174,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
@@ -1156,7 +1194,9 @@ private signature module StageSig {
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, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,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(
@@ -1247,14 +1288,14 @@ private module MkStage {
pragma[nomagic]
private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
+ NodeEx out, boolean allowsFieldFlow, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ flowOutOfCall(call, ret, kind, 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)
+ PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call) and
+ c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
+ summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage {
)
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, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParameterPositionNone() 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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParameterPositionNone() 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, config) and
+ flowOutOfCall(call, ret, _, node, allowsFieldFlow, 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, ParameterPosition summaryCtx0, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
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,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ 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)
)
@@ -1374,21 +1435,21 @@ private module MkStage {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
- fwdFlow(node1, state, cc, argAp, ap, config) and
+ fwdFlow(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, Cc innercc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, argAp, ap, config) and
+ fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage {
}
pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutFromArg(
+ DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
+ Configuration config
) {
exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
+ DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
- 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutFromArg(
- DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, 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()
+ fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
+ TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
+ config) and
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
+ ParameterPosition pos, Ap ap, Configuration config
) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ exists(ParamNodeEx param |
+ fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
+ pos = param.getPosition()
)
}
@@ -1440,146 +1493,171 @@ 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,
+ private predicate returnFlowsThrough0(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
+ ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
+ ) {
+ exists(boolean allowsFieldFlow |
+ fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
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))
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ private predicate returnFlowsThrough(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, 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(DataFlowCallable c, ParameterPosition ppos |
+ returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
+ p.isParameterOf(c, ppos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
}
pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
+ exists(Ap argAp |
+ flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
+ pragma[only_bind_into](config)) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
+ returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
+ pragma[only_bind_into](config))
+ )
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
- * 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
+ flowIntoCall(_, node, p, allowsFieldFlow, 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, ReturnKindExt returnKind0, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, 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(ReturnKindExt kind |
+ revFlowOut(_, node, kind, state, _, _, ap, config) and
+ if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
+ then (
+ returnCtx = TReturnCtxMaybeFlowThrough(kind) 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
}
@@ -1599,35 +1677,27 @@ 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, ReturnKindExt kind, 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
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- pragma[nomagic]
- private predicate revFlowInNotToReturn(
- ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, false, returnAp, ap, config) and
- flowIntoCall(_, arg, p, allowsFieldFlow, config) and
+ revFlow(out, state, returnCtx, returnAp, ap, config) and
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
+ Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
+ revFlow(pragma[only_bind_into](p), state,
+ TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,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, ReturnKindExt kind, 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, kind, state, returnCtx, returnAp, ap, config) and
+ returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ 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, ReturnKindExt kind, Configuration config
) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
+ parameterFlowThroughAllowed(p, kind)
}
- 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, ReturnKindExt kind |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
+ )
+ }
+
+ pragma[nomagic]
+ predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
+ exists(ParamNodeEx p, Ap ap |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
+ ReturnCtx returnCtx, 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)
+ revFlow(arg, state, returnCtx, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,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. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam {
exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,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))
}
@@ -2021,8 +2091,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)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,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)
+ Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
+ Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
+ config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam {
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))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
- NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
+ NodeEx n, DataFlowCallable c, ParameterPosition pos, 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
+ c = n.getEnclosingCallable() and
+ Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
+ Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
+ TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ 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(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
+ Stage4::parameterMayFlowThrough(p, apa, config) and
+ nodeMayUseSummary0(n, c, pos, state, apa, config) and
+ p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
- Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
+ Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,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)
)
}
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 3c41b1876dc1..ccde916a3c2e 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
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ 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())
+ )
+}
+
+/**
+ * Holds if flow from a parameter at position `pos` inside `c` to a return node of
+ * kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[c, pos, kind]
+private predicate parameterFlowThroughAllowed(
+ DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
+) {
+ exists(ParamNodeEx p |
+ p.isParameterOf(c, pos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
+}
+
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ 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, ReturnKindExt kind, Configuration config) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind()
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,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]
@@ -1087,10 +1122,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)
)
}
@@ -1099,10 +1135,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)
)
}
@@ -1115,12 +1152,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
@@ -1136,10 +1174,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
@@ -1156,7 +1194,9 @@ private signature module StageSig {
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, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,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(
@@ -1247,14 +1288,14 @@ private module MkStage {
pragma[nomagic]
private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
+ NodeEx out, boolean allowsFieldFlow, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ flowOutOfCall(call, ret, kind, 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)
+ PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call) and
+ c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
+ summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage {
)
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, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParameterPositionNone() 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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParameterPositionNone() 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, config) and
+ flowOutOfCall(call, ret, _, node, allowsFieldFlow, 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, ParameterPosition summaryCtx0, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
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,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ 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)
)
@@ -1374,21 +1435,21 @@ private module MkStage {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
- fwdFlow(node1, state, cc, argAp, ap, config) and
+ fwdFlow(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, Cc innercc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, argAp, ap, config) and
+ fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage {
}
pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutFromArg(
+ DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
+ Configuration config
) {
exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
+ DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
- 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutFromArg(
- DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, 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()
+ fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
+ TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
+ config) and
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
+ ParameterPosition pos, Ap ap, Configuration config
) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ exists(ParamNodeEx param |
+ fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
+ pos = param.getPosition()
)
}
@@ -1440,146 +1493,171 @@ 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,
+ private predicate returnFlowsThrough0(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
+ ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
+ ) {
+ exists(boolean allowsFieldFlow |
+ fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
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))
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ private predicate returnFlowsThrough(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, 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(DataFlowCallable c, ParameterPosition ppos |
+ returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
+ p.isParameterOf(c, ppos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
}
pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
+ exists(Ap argAp |
+ flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
+ pragma[only_bind_into](config)) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
+ returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
+ pragma[only_bind_into](config))
+ )
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
- * 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
+ flowIntoCall(_, node, p, allowsFieldFlow, 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, ReturnKindExt returnKind0, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, 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(ReturnKindExt kind |
+ revFlowOut(_, node, kind, state, _, _, ap, config) and
+ if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
+ then (
+ returnCtx = TReturnCtxMaybeFlowThrough(kind) 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
}
@@ -1599,35 +1677,27 @@ 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, ReturnKindExt kind, 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
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- pragma[nomagic]
- private predicate revFlowInNotToReturn(
- ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, false, returnAp, ap, config) and
- flowIntoCall(_, arg, p, allowsFieldFlow, config) and
+ revFlow(out, state, returnCtx, returnAp, ap, config) and
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
+ Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
+ revFlow(pragma[only_bind_into](p), state,
+ TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,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, ReturnKindExt kind, 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, kind, state, returnCtx, returnAp, ap, config) and
+ returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ 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, ReturnKindExt kind, Configuration config
) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
+ parameterFlowThroughAllowed(p, kind)
}
- 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, ReturnKindExt kind |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
+ )
+ }
+
+ pragma[nomagic]
+ predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
+ exists(ParamNodeEx p, Ap ap |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
+ ReturnCtx returnCtx, 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)
+ revFlow(arg, state, returnCtx, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,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. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam {
exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,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))
}
@@ -2021,8 +2091,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)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,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)
+ Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
+ Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
+ config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam {
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))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
- NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
+ NodeEx n, DataFlowCallable c, ParameterPosition pos, 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
+ c = n.getEnclosingCallable() and
+ Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
+ Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
+ TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ 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(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
+ Stage4::parameterMayFlowThrough(p, apa, config) and
+ nodeMayUseSummary0(n, c, pos, state, apa, config) and
+ p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
- Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
+ Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,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)
)
}
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 3c41b1876dc1..ccde916a3c2e 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
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ 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())
+ )
+}
+
+/**
+ * Holds if flow from a parameter at position `pos` inside `c` to a return node of
+ * kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[c, pos, kind]
+private predicate parameterFlowThroughAllowed(
+ DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
+) {
+ exists(ParamNodeEx p |
+ p.isParameterOf(c, pos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
+}
+
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ 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, ReturnKindExt kind, Configuration config) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind()
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,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]
@@ -1087,10 +1122,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)
)
}
@@ -1099,10 +1135,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)
)
}
@@ -1115,12 +1152,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
@@ -1136,10 +1174,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
@@ -1156,7 +1194,9 @@ private signature module StageSig {
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, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,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(
@@ -1247,14 +1288,14 @@ private module MkStage {
pragma[nomagic]
private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
+ NodeEx out, boolean allowsFieldFlow, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ flowOutOfCall(call, ret, kind, 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)
+ PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call) and
+ c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
+ summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage {
)
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, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParameterPositionNone() 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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParameterPositionNone() 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, config) and
+ flowOutOfCall(call, ret, _, node, allowsFieldFlow, 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, ParameterPosition summaryCtx0, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
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,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ 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)
)
@@ -1374,21 +1435,21 @@ private module MkStage {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
- fwdFlow(node1, state, cc, argAp, ap, config) and
+ fwdFlow(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, Cc innercc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, argAp, ap, config) and
+ fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage {
}
pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutFromArg(
+ DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
+ Configuration config
) {
exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
+ DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
- 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutFromArg(
- DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, 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()
+ fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
+ TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
+ config) and
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
+ ParameterPosition pos, Ap ap, Configuration config
) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ exists(ParamNodeEx param |
+ fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
+ pos = param.getPosition()
)
}
@@ -1440,146 +1493,171 @@ 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,
+ private predicate returnFlowsThrough0(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
+ ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
+ ) {
+ exists(boolean allowsFieldFlow |
+ fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
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))
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ private predicate returnFlowsThrough(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, 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(DataFlowCallable c, ParameterPosition ppos |
+ returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
+ p.isParameterOf(c, ppos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
}
pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
+ exists(Ap argAp |
+ flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
+ pragma[only_bind_into](config)) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
+ returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
+ pragma[only_bind_into](config))
+ )
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
- * 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
+ flowIntoCall(_, node, p, allowsFieldFlow, 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, ReturnKindExt returnKind0, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, 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(ReturnKindExt kind |
+ revFlowOut(_, node, kind, state, _, _, ap, config) and
+ if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
+ then (
+ returnCtx = TReturnCtxMaybeFlowThrough(kind) 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
}
@@ -1599,35 +1677,27 @@ 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, ReturnKindExt kind, 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
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- pragma[nomagic]
- private predicate revFlowInNotToReturn(
- ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, false, returnAp, ap, config) and
- flowIntoCall(_, arg, p, allowsFieldFlow, config) and
+ revFlow(out, state, returnCtx, returnAp, ap, config) and
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
+ Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
+ revFlow(pragma[only_bind_into](p), state,
+ TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,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, ReturnKindExt kind, 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, kind, state, returnCtx, returnAp, ap, config) and
+ returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ 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, ReturnKindExt kind, Configuration config
) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
+ parameterFlowThroughAllowed(p, kind)
}
- 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, ReturnKindExt kind |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
+ )
+ }
+
+ pragma[nomagic]
+ predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
+ exists(ParamNodeEx p, Ap ap |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
+ ReturnCtx returnCtx, 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)
+ revFlow(arg, state, returnCtx, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,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. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam {
exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,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))
}
@@ -2021,8 +2091,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)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,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)
+ Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
+ Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
+ config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam {
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))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
- NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
+ NodeEx n, DataFlowCallable c, ParameterPosition pos, 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
+ c = n.getEnclosingCallable() and
+ Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
+ Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
+ TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ 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(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
+ Stage4::parameterMayFlowThrough(p, apa, config) and
+ nodeMayUseSummary0(n, c, pos, state, apa, config) and
+ p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
- Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
+ Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,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)
)
}
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 3c41b1876dc1..ccde916a3c2e 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
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ 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())
+ )
+}
+
+/**
+ * Holds if flow from a parameter at position `pos` inside `c` to a return node of
+ * kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[c, pos, kind]
+private predicate parameterFlowThroughAllowed(
+ DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
+) {
+ exists(ParamNodeEx p |
+ p.isParameterOf(c, pos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
+}
+
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ 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, ReturnKindExt kind, Configuration config) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind()
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,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]
@@ -1087,10 +1122,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)
)
}
@@ -1099,10 +1135,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)
)
}
@@ -1115,12 +1152,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
@@ -1136,10 +1174,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
@@ -1156,7 +1194,9 @@ private signature module StageSig {
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, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,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(
@@ -1247,14 +1288,14 @@ private module MkStage {
pragma[nomagic]
private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
+ NodeEx out, boolean allowsFieldFlow, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ flowOutOfCall(call, ret, kind, 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)
+ PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call) and
+ c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
+ summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage {
)
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, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParameterPositionNone() 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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParameterPositionNone() 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, config) and
+ flowOutOfCall(call, ret, _, node, allowsFieldFlow, 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, ParameterPosition summaryCtx0, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
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,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ 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)
)
@@ -1374,21 +1435,21 @@ private module MkStage {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
- fwdFlow(node1, state, cc, argAp, ap, config) and
+ fwdFlow(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, Cc innercc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, argAp, ap, config) and
+ fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage {
}
pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutFromArg(
+ DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
+ Configuration config
) {
exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
+ DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
- 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutFromArg(
- DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, 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()
+ fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
+ TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
+ config) and
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
+ ParameterPosition pos, Ap ap, Configuration config
) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ exists(ParamNodeEx param |
+ fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
+ pos = param.getPosition()
)
}
@@ -1440,146 +1493,171 @@ 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,
+ private predicate returnFlowsThrough0(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
+ ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
+ ) {
+ exists(boolean allowsFieldFlow |
+ fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
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))
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ private predicate returnFlowsThrough(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, 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(DataFlowCallable c, ParameterPosition ppos |
+ returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
+ p.isParameterOf(c, ppos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
}
pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
+ exists(Ap argAp |
+ flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
+ pragma[only_bind_into](config)) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
+ returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
+ pragma[only_bind_into](config))
+ )
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
- * 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
+ flowIntoCall(_, node, p, allowsFieldFlow, 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, ReturnKindExt returnKind0, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, 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(ReturnKindExt kind |
+ revFlowOut(_, node, kind, state, _, _, ap, config) and
+ if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
+ then (
+ returnCtx = TReturnCtxMaybeFlowThrough(kind) 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
}
@@ -1599,35 +1677,27 @@ 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, ReturnKindExt kind, 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
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- pragma[nomagic]
- private predicate revFlowInNotToReturn(
- ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, false, returnAp, ap, config) and
- flowIntoCall(_, arg, p, allowsFieldFlow, config) and
+ revFlow(out, state, returnCtx, returnAp, ap, config) and
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
+ Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
+ revFlow(pragma[only_bind_into](p), state,
+ TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,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, ReturnKindExt kind, 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, kind, state, returnCtx, returnAp, ap, config) and
+ returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ 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, ReturnKindExt kind, Configuration config
) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
+ parameterFlowThroughAllowed(p, kind)
}
- 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, ReturnKindExt kind |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
+ )
+ }
+
+ pragma[nomagic]
+ predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
+ exists(ParamNodeEx p, Ap ap |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
+ ReturnCtx returnCtx, 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)
+ revFlow(arg, state, returnCtx, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,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. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam {
exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,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))
}
@@ -2021,8 +2091,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)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,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)
+ Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
+ Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
+ config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam {
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))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
- NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
+ NodeEx n, DataFlowCallable c, ParameterPosition pos, 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
+ c = n.getEnclosingCallable() and
+ Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
+ Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
+ TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ 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(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
+ Stage4::parameterMayFlowThrough(p, apa, config) and
+ nodeMayUseSummary0(n, c, pos, state, apa, config) and
+ p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
- Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
+ Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,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)
)
}
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 ae9c6f3f12ef..f981834a6d4d 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,6 +915,17 @@ private module Cached {
TDataFlowCallNone() or
TDataFlowCallSome(DataFlowCall call)
+ cached
+ newtype TParameterPositionOption =
+ TParameterPositionNone() or
+ TParameterPositionSome(ParameterPosition pos)
+
+ cached
+ newtype TReturnCtx =
+ TReturnCtxNone() or
+ TReturnCtxNoFlowThrough() or
+ TReturnCtxMaybeFlowThrough(ReturnKindExt kind)
+
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
@@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption {
}
}
+/** An optional `ParameterPosition`. */
+class ParameterPositionOption extends TParameterPositionOption {
+ string toString() {
+ this = TParameterPositionNone() and
+ result = "(none)"
+ or
+ exists(ParameterPosition pos |
+ this = TParameterPositionSome(pos) and
+ result = pos.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(ReturnKindExt kind)`: return flow, of kind `kind`, 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(ReturnKindExt kind |
+ this = TReturnCtxMaybeFlowThrough(kind) and
+ result = kind.toString()
+ )
+ }
+}
+
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
diff --git a/cpp/ql/lib/qlpack.yml b/cpp/ql/lib/qlpack.yml
index e8c0a17068ea..4e0b86be561d 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
+version: 0.4.5-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 e58467fac20c..b2e4e0a41a58 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/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
index 3c41b1876dc1..ccde916a3c2e 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
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ 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())
+ )
+}
+
+/**
+ * Holds if flow from a parameter at position `pos` inside `c` to a return node of
+ * kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[c, pos, kind]
+private predicate parameterFlowThroughAllowed(
+ DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
+) {
+ exists(ParamNodeEx p |
+ p.isParameterOf(c, pos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
+}
+
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ 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, ReturnKindExt kind, Configuration config) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind()
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,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]
@@ -1087,10 +1122,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)
)
}
@@ -1099,10 +1135,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)
)
}
@@ -1115,12 +1152,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
@@ -1136,10 +1174,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
@@ -1156,7 +1194,9 @@ private signature module StageSig {
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, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,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(
@@ -1247,14 +1288,14 @@ private module MkStage {
pragma[nomagic]
private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
+ NodeEx out, boolean allowsFieldFlow, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ flowOutOfCall(call, ret, kind, 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)
+ PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call) and
+ c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
+ summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage {
)
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, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParameterPositionNone() 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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParameterPositionNone() 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, config) and
+ flowOutOfCall(call, ret, _, node, allowsFieldFlow, 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, ParameterPosition summaryCtx0, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
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,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ 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)
)
@@ -1374,21 +1435,21 @@ private module MkStage {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
- fwdFlow(node1, state, cc, argAp, ap, config) and
+ fwdFlow(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, Cc innercc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, argAp, ap, config) and
+ fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage {
}
pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutFromArg(
+ DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
+ Configuration config
) {
exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
+ DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
- 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutFromArg(
- DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, 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()
+ fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
+ TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
+ config) and
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
+ ParameterPosition pos, Ap ap, Configuration config
) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ exists(ParamNodeEx param |
+ fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
+ pos = param.getPosition()
)
}
@@ -1440,146 +1493,171 @@ 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,
+ private predicate returnFlowsThrough0(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
+ ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
+ ) {
+ exists(boolean allowsFieldFlow |
+ fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
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))
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ private predicate returnFlowsThrough(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, 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(DataFlowCallable c, ParameterPosition ppos |
+ returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
+ p.isParameterOf(c, ppos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
}
pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
+ exists(Ap argAp |
+ flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
+ pragma[only_bind_into](config)) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
+ returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
+ pragma[only_bind_into](config))
+ )
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
- * 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
+ flowIntoCall(_, node, p, allowsFieldFlow, 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, ReturnKindExt returnKind0, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, 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(ReturnKindExt kind |
+ revFlowOut(_, node, kind, state, _, _, ap, config) and
+ if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
+ then (
+ returnCtx = TReturnCtxMaybeFlowThrough(kind) 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
}
@@ -1599,35 +1677,27 @@ 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, ReturnKindExt kind, 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
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- pragma[nomagic]
- private predicate revFlowInNotToReturn(
- ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, false, returnAp, ap, config) and
- flowIntoCall(_, arg, p, allowsFieldFlow, config) and
+ revFlow(out, state, returnCtx, returnAp, ap, config) and
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
+ Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
+ revFlow(pragma[only_bind_into](p), state,
+ TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,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, ReturnKindExt kind, 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, kind, state, returnCtx, returnAp, ap, config) and
+ returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ 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, ReturnKindExt kind, Configuration config
) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
+ parameterFlowThroughAllowed(p, kind)
}
- 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, ReturnKindExt kind |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
+ )
+ }
+
+ pragma[nomagic]
+ predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
+ exists(ParamNodeEx p, Ap ap |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
+ ReturnCtx returnCtx, 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)
+ revFlow(arg, state, returnCtx, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,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. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam {
exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,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))
}
@@ -2021,8 +2091,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)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,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)
+ Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
+ Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
+ config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam {
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))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
- NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
+ NodeEx n, DataFlowCallable c, ParameterPosition pos, 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
+ c = n.getEnclosingCallable() and
+ Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
+ Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
+ TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ 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(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
+ Stage4::parameterMayFlowThrough(p, apa, config) and
+ nodeMayUseSummary0(n, c, pos, state, apa, config) and
+ p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
- Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
+ Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,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)
)
}
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 3c41b1876dc1..ccde916a3c2e 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
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ 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())
+ )
+}
+
+/**
+ * Holds if flow from a parameter at position `pos` inside `c` to a return node of
+ * kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[c, pos, kind]
+private predicate parameterFlowThroughAllowed(
+ DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
+) {
+ exists(ParamNodeEx p |
+ p.isParameterOf(c, pos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
+}
+
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ 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, ReturnKindExt kind, Configuration config) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind()
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,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]
@@ -1087,10 +1122,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)
)
}
@@ -1099,10 +1135,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)
)
}
@@ -1115,12 +1152,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
@@ -1136,10 +1174,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
@@ -1156,7 +1194,9 @@ private signature module StageSig {
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, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,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(
@@ -1247,14 +1288,14 @@ private module MkStage {
pragma[nomagic]
private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
+ NodeEx out, boolean allowsFieldFlow, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ flowOutOfCall(call, ret, kind, 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)
+ PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call) and
+ c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
+ summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage {
)
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, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParameterPositionNone() 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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParameterPositionNone() 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, config) and
+ flowOutOfCall(call, ret, _, node, allowsFieldFlow, 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, ParameterPosition summaryCtx0, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
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,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ 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)
)
@@ -1374,21 +1435,21 @@ private module MkStage {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
- fwdFlow(node1, state, cc, argAp, ap, config) and
+ fwdFlow(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, Cc innercc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, argAp, ap, config) and
+ fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage {
}
pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutFromArg(
+ DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
+ Configuration config
) {
exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
+ DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
- 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutFromArg(
- DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, 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()
+ fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
+ TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
+ config) and
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
+ ParameterPosition pos, Ap ap, Configuration config
) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ exists(ParamNodeEx param |
+ fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
+ pos = param.getPosition()
)
}
@@ -1440,146 +1493,171 @@ 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,
+ private predicate returnFlowsThrough0(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
+ ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
+ ) {
+ exists(boolean allowsFieldFlow |
+ fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
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))
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ private predicate returnFlowsThrough(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, 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(DataFlowCallable c, ParameterPosition ppos |
+ returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
+ p.isParameterOf(c, ppos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
}
pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
+ exists(Ap argAp |
+ flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
+ pragma[only_bind_into](config)) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
+ returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
+ pragma[only_bind_into](config))
+ )
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
- * 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
+ flowIntoCall(_, node, p, allowsFieldFlow, 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, ReturnKindExt returnKind0, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, 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(ReturnKindExt kind |
+ revFlowOut(_, node, kind, state, _, _, ap, config) and
+ if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
+ then (
+ returnCtx = TReturnCtxMaybeFlowThrough(kind) 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
}
@@ -1599,35 +1677,27 @@ 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, ReturnKindExt kind, 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
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- pragma[nomagic]
- private predicate revFlowInNotToReturn(
- ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, false, returnAp, ap, config) and
- flowIntoCall(_, arg, p, allowsFieldFlow, config) and
+ revFlow(out, state, returnCtx, returnAp, ap, config) and
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
+ Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
+ revFlow(pragma[only_bind_into](p), state,
+ TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,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, ReturnKindExt kind, 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, kind, state, returnCtx, returnAp, ap, config) and
+ returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ 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, ReturnKindExt kind, Configuration config
) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
+ parameterFlowThroughAllowed(p, kind)
}
- 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, ReturnKindExt kind |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
+ )
+ }
+
+ pragma[nomagic]
+ predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
+ exists(ParamNodeEx p, Ap ap |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
+ ReturnCtx returnCtx, 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)
+ revFlow(arg, state, returnCtx, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,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. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam {
exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,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))
}
@@ -2021,8 +2091,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)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,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)
+ Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
+ Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
+ config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam {
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))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
- NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
+ NodeEx n, DataFlowCallable c, ParameterPosition pos, 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
+ c = n.getEnclosingCallable() and
+ Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
+ Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
+ TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ 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(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
+ Stage4::parameterMayFlowThrough(p, apa, config) and
+ nodeMayUseSummary0(n, c, pos, state, apa, config) and
+ p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
- Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
+ Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,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)
)
}
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 3c41b1876dc1..ccde916a3c2e 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
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ 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())
+ )
+}
+
+/**
+ * Holds if flow from a parameter at position `pos` inside `c` to a return node of
+ * kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[c, pos, kind]
+private predicate parameterFlowThroughAllowed(
+ DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
+) {
+ exists(ParamNodeEx p |
+ p.isParameterOf(c, pos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
+}
+
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ 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, ReturnKindExt kind, Configuration config) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind()
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,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]
@@ -1087,10 +1122,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)
)
}
@@ -1099,10 +1135,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)
)
}
@@ -1115,12 +1152,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
@@ -1136,10 +1174,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
@@ -1156,7 +1194,9 @@ private signature module StageSig {
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, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,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(
@@ -1247,14 +1288,14 @@ private module MkStage {
pragma[nomagic]
private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
+ NodeEx out, boolean allowsFieldFlow, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ flowOutOfCall(call, ret, kind, 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)
+ PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call) and
+ c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
+ summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage {
)
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, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParameterPositionNone() 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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParameterPositionNone() 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, config) and
+ flowOutOfCall(call, ret, _, node, allowsFieldFlow, 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, ParameterPosition summaryCtx0, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
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,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ 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)
)
@@ -1374,21 +1435,21 @@ private module MkStage {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
- fwdFlow(node1, state, cc, argAp, ap, config) and
+ fwdFlow(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, Cc innercc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, argAp, ap, config) and
+ fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage {
}
pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutFromArg(
+ DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
+ Configuration config
) {
exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
+ DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
- 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutFromArg(
- DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, 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()
+ fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
+ TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
+ config) and
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
+ ParameterPosition pos, Ap ap, Configuration config
) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ exists(ParamNodeEx param |
+ fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
+ pos = param.getPosition()
)
}
@@ -1440,146 +1493,171 @@ 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,
+ private predicate returnFlowsThrough0(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
+ ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
+ ) {
+ exists(boolean allowsFieldFlow |
+ fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
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))
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ private predicate returnFlowsThrough(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, 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(DataFlowCallable c, ParameterPosition ppos |
+ returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
+ p.isParameterOf(c, ppos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
}
pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
+ exists(Ap argAp |
+ flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
+ pragma[only_bind_into](config)) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
+ returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
+ pragma[only_bind_into](config))
+ )
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
- * 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
+ flowIntoCall(_, node, p, allowsFieldFlow, 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, ReturnKindExt returnKind0, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, 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(ReturnKindExt kind |
+ revFlowOut(_, node, kind, state, _, _, ap, config) and
+ if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
+ then (
+ returnCtx = TReturnCtxMaybeFlowThrough(kind) 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
}
@@ -1599,35 +1677,27 @@ 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, ReturnKindExt kind, 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
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- pragma[nomagic]
- private predicate revFlowInNotToReturn(
- ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, false, returnAp, ap, config) and
- flowIntoCall(_, arg, p, allowsFieldFlow, config) and
+ revFlow(out, state, returnCtx, returnAp, ap, config) and
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
+ Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
+ revFlow(pragma[only_bind_into](p), state,
+ TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,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, ReturnKindExt kind, 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, kind, state, returnCtx, returnAp, ap, config) and
+ returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ 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, ReturnKindExt kind, Configuration config
) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
+ parameterFlowThroughAllowed(p, kind)
}
- 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, ReturnKindExt kind |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
+ )
+ }
+
+ pragma[nomagic]
+ predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
+ exists(ParamNodeEx p, Ap ap |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
+ ReturnCtx returnCtx, 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)
+ revFlow(arg, state, returnCtx, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,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. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam {
exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,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))
}
@@ -2021,8 +2091,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)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,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)
+ Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
+ Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
+ config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam {
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))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
- NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
+ NodeEx n, DataFlowCallable c, ParameterPosition pos, 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
+ c = n.getEnclosingCallable() and
+ Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
+ Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
+ TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ 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(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
+ Stage4::parameterMayFlowThrough(p, apa, config) and
+ nodeMayUseSummary0(n, c, pos, state, apa, config) and
+ p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
- Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
+ Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,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)
)
}
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 3c41b1876dc1..ccde916a3c2e 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
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ 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())
+ )
+}
+
+/**
+ * Holds if flow from a parameter at position `pos` inside `c` to a return node of
+ * kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[c, pos, kind]
+private predicate parameterFlowThroughAllowed(
+ DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
+) {
+ exists(ParamNodeEx p |
+ p.isParameterOf(c, pos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
+}
+
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ 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, ReturnKindExt kind, Configuration config) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind()
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,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]
@@ -1087,10 +1122,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)
)
}
@@ -1099,10 +1135,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)
)
}
@@ -1115,12 +1152,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
@@ -1136,10 +1174,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
@@ -1156,7 +1194,9 @@ private signature module StageSig {
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, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,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(
@@ -1247,14 +1288,14 @@ private module MkStage {
pragma[nomagic]
private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
+ NodeEx out, boolean allowsFieldFlow, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ flowOutOfCall(call, ret, kind, 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)
+ PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call) and
+ c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
+ summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage {
)
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, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParameterPositionNone() 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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParameterPositionNone() 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, config) and
+ flowOutOfCall(call, ret, _, node, allowsFieldFlow, 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, ParameterPosition summaryCtx0, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
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,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ 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)
)
@@ -1374,21 +1435,21 @@ private module MkStage {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
- fwdFlow(node1, state, cc, argAp, ap, config) and
+ fwdFlow(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, Cc innercc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, argAp, ap, config) and
+ fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage {
}
pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutFromArg(
+ DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
+ Configuration config
) {
exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
+ DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
- 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutFromArg(
- DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, 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()
+ fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
+ TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
+ config) and
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
+ ParameterPosition pos, Ap ap, Configuration config
) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ exists(ParamNodeEx param |
+ fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
+ pos = param.getPosition()
)
}
@@ -1440,146 +1493,171 @@ 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,
+ private predicate returnFlowsThrough0(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
+ ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
+ ) {
+ exists(boolean allowsFieldFlow |
+ fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
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))
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ private predicate returnFlowsThrough(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, 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(DataFlowCallable c, ParameterPosition ppos |
+ returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
+ p.isParameterOf(c, ppos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
}
pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
+ exists(Ap argAp |
+ flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
+ pragma[only_bind_into](config)) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
+ returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
+ pragma[only_bind_into](config))
+ )
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
- * 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
+ flowIntoCall(_, node, p, allowsFieldFlow, 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, ReturnKindExt returnKind0, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, 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(ReturnKindExt kind |
+ revFlowOut(_, node, kind, state, _, _, ap, config) and
+ if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
+ then (
+ returnCtx = TReturnCtxMaybeFlowThrough(kind) 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
}
@@ -1599,35 +1677,27 @@ 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, ReturnKindExt kind, 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
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- pragma[nomagic]
- private predicate revFlowInNotToReturn(
- ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, false, returnAp, ap, config) and
- flowIntoCall(_, arg, p, allowsFieldFlow, config) and
+ revFlow(out, state, returnCtx, returnAp, ap, config) and
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
+ Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
+ revFlow(pragma[only_bind_into](p), state,
+ TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,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, ReturnKindExt kind, 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, kind, state, returnCtx, returnAp, ap, config) and
+ returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ 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, ReturnKindExt kind, Configuration config
) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
+ parameterFlowThroughAllowed(p, kind)
}
- 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, ReturnKindExt kind |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
+ )
+ }
+
+ pragma[nomagic]
+ predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
+ exists(ParamNodeEx p, Ap ap |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
+ ReturnCtx returnCtx, 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)
+ revFlow(arg, state, returnCtx, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,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. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam {
exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,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))
}
@@ -2021,8 +2091,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)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,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)
+ Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
+ Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
+ config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam {
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))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
- NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
+ NodeEx n, DataFlowCallable c, ParameterPosition pos, 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
+ c = n.getEnclosingCallable() and
+ Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
+ Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
+ TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ 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(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
+ Stage4::parameterMayFlowThrough(p, apa, config) and
+ nodeMayUseSummary0(n, c, pos, state, apa, config) and
+ p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
- Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
+ Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,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)
)
}
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 ae9c6f3f12ef..f981834a6d4d 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
@@ -915,6 +915,17 @@ private module Cached {
TDataFlowCallNone() or
TDataFlowCallSome(DataFlowCall call)
+ cached
+ newtype TParameterPositionOption =
+ TParameterPositionNone() or
+ TParameterPositionSome(ParameterPosition pos)
+
+ cached
+ newtype TReturnCtx =
+ TReturnCtxNone() or
+ TReturnCtxNoFlowThrough() or
+ TReturnCtxMaybeFlowThrough(ReturnKindExt kind)
+
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
@@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption {
}
}
+/** An optional `ParameterPosition`. */
+class ParameterPositionOption extends TParameterPositionOption {
+ string toString() {
+ this = TParameterPositionNone() and
+ result = "(none)"
+ or
+ exists(ParameterPosition pos |
+ this = TParameterPositionSome(pos) and
+ result = pos.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(ReturnKindExt kind)`: return flow, of kind `kind`, 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(ReturnKindExt kind |
+ this = TReturnCtxMaybeFlowThrough(kind) and
+ result = kind.toString()
+ )
+ }
+}
+
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
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 3c41b1876dc1..ccde916a3c2e 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
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ 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())
+ )
+}
+
+/**
+ * Holds if flow from a parameter at position `pos` inside `c` to a return node of
+ * kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[c, pos, kind]
+private predicate parameterFlowThroughAllowed(
+ DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
+) {
+ exists(ParamNodeEx p |
+ p.isParameterOf(c, pos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
+}
+
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ 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, ReturnKindExt kind, Configuration config) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind()
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,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]
@@ -1087,10 +1122,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)
)
}
@@ -1099,10 +1135,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)
)
}
@@ -1115,12 +1152,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
@@ -1136,10 +1174,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
@@ -1156,7 +1194,9 @@ private signature module StageSig {
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, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,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(
@@ -1247,14 +1288,14 @@ private module MkStage {
pragma[nomagic]
private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
+ NodeEx out, boolean allowsFieldFlow, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ flowOutOfCall(call, ret, kind, 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)
+ PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call) and
+ c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
+ summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage {
)
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, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParameterPositionNone() 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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParameterPositionNone() 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, config) and
+ flowOutOfCall(call, ret, _, node, allowsFieldFlow, 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, ParameterPosition summaryCtx0, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
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,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ 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)
)
@@ -1374,21 +1435,21 @@ private module MkStage {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
- fwdFlow(node1, state, cc, argAp, ap, config) and
+ fwdFlow(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, Cc innercc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, argAp, ap, config) and
+ fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage {
}
pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutFromArg(
+ DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
+ Configuration config
) {
exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
+ DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
- 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutFromArg(
- DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, 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()
+ fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
+ TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
+ config) and
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
+ ParameterPosition pos, Ap ap, Configuration config
) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ exists(ParamNodeEx param |
+ fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
+ pos = param.getPosition()
)
}
@@ -1440,146 +1493,171 @@ 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,
+ private predicate returnFlowsThrough0(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
+ ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
+ ) {
+ exists(boolean allowsFieldFlow |
+ fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
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))
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ private predicate returnFlowsThrough(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, 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(DataFlowCallable c, ParameterPosition ppos |
+ returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
+ p.isParameterOf(c, ppos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
}
pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
+ exists(Ap argAp |
+ flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
+ pragma[only_bind_into](config)) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
+ returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
+ pragma[only_bind_into](config))
+ )
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
- * 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
+ flowIntoCall(_, node, p, allowsFieldFlow, 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, ReturnKindExt returnKind0, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, 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(ReturnKindExt kind |
+ revFlowOut(_, node, kind, state, _, _, ap, config) and
+ if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
+ then (
+ returnCtx = TReturnCtxMaybeFlowThrough(kind) 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
}
@@ -1599,35 +1677,27 @@ 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, ReturnKindExt kind, 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
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- pragma[nomagic]
- private predicate revFlowInNotToReturn(
- ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, false, returnAp, ap, config) and
- flowIntoCall(_, arg, p, allowsFieldFlow, config) and
+ revFlow(out, state, returnCtx, returnAp, ap, config) and
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
+ Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
+ revFlow(pragma[only_bind_into](p), state,
+ TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,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, ReturnKindExt kind, 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, kind, state, returnCtx, returnAp, ap, config) and
+ returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ 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, ReturnKindExt kind, Configuration config
) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
+ parameterFlowThroughAllowed(p, kind)
}
- 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, ReturnKindExt kind |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
+ )
+ }
+
+ pragma[nomagic]
+ predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
+ exists(ParamNodeEx p, Ap ap |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
+ ReturnCtx returnCtx, 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)
+ revFlow(arg, state, returnCtx, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,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. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam {
exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,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))
}
@@ -2021,8 +2091,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)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,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)
+ Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
+ Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
+ config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam {
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))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
- NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
+ NodeEx n, DataFlowCallable c, ParameterPosition pos, 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
+ c = n.getEnclosingCallable() and
+ Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
+ Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
+ TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ 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(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
+ Stage4::parameterMayFlowThrough(p, apa, config) and
+ nodeMayUseSummary0(n, c, pos, state, apa, config) and
+ p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
- Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
+ Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,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)
)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll
index 08ee06acdda3..904701144ca5 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll
@@ -5,7 +5,6 @@
*/
private import cpp
-import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.IR
/**
@@ -25,18 +24,18 @@ abstract class MustFlowConfiguration extends string {
/**
* Holds if `source` is a relevant data flow source.
*/
- abstract predicate isSource(DataFlow::Node source);
+ abstract predicate isSource(Instruction source);
/**
* Holds if `sink` is a relevant data flow sink.
*/
- abstract predicate isSink(DataFlow::Node sink);
+ abstract predicate isSink(Operand sink);
/**
* Holds if the additional flow step from `node1` to `node2` must be taken
* into account in the analysis.
*/
- predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
+ predicate isAdditionalFlowStep(Operand node1, Instruction node2) { none() }
/** Holds if this configuration allows flow from arguments to parameters. */
predicate allowInterproceduralFlow() { any() }
@@ -48,17 +47,17 @@ abstract class MustFlowConfiguration extends string {
* included in the module `PathGraph`.
*/
final predicate hasFlowPath(MustFlowPathNode source, MustFlowPathSink sink) {
- this.isSource(source.getNode()) and
+ this.isSource(source.getInstruction()) and
source.getASuccessor+() = sink
}
}
/** Holds if `node` flows from a source. */
pragma[nomagic]
-private predicate flowsFromSource(DataFlow::Node node, MustFlowConfiguration config) {
+private predicate flowsFromSource(Instruction node, MustFlowConfiguration config) {
config.isSource(node)
or
- exists(DataFlow::Node mid |
+ exists(Instruction mid |
step(mid, node, config) and
flowsFromSource(mid, pragma[only_bind_into](config))
)
@@ -66,12 +65,12 @@ private predicate flowsFromSource(DataFlow::Node node, MustFlowConfiguration con
/** Holds if `node` flows to a sink. */
pragma[nomagic]
-private predicate flowsToSink(DataFlow::Node node, MustFlowConfiguration config) {
+private predicate flowsToSink(Instruction node, MustFlowConfiguration config) {
flowsFromSource(node, pragma[only_bind_into](config)) and
(
- config.isSink(node)
+ config.isSink(node.getAUse())
or
- exists(DataFlow::Node mid |
+ exists(Instruction mid |
step(node, mid, config) and
flowsToSink(mid, pragma[only_bind_into](config))
)
@@ -198,12 +197,13 @@ private module Cached {
}
cached
- predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
- instructionToOperandStep(nodeFrom.asInstruction(), nodeTo.asOperand())
- or
- flowThroughCallable(nodeFrom.asInstruction(), nodeTo.asInstruction())
+ predicate step(Instruction nodeFrom, Instruction nodeTo) {
+ exists(Operand mid |
+ instructionToOperandStep(nodeFrom, mid) and
+ operandToInstructionStep(mid, nodeTo)
+ )
or
- operandToInstructionStep(nodeFrom.asOperand(), nodeTo.asInstruction())
+ flowThroughCallable(nodeFrom, nodeTo)
}
}
@@ -213,12 +213,12 @@ private module Cached {
* way around.
*/
pragma[inline]
-private Declaration getEnclosingCallable(DataFlow::Node n) {
- pragma[only_bind_into](result) = pragma[only_bind_out](n).getEnclosingCallable()
+private IRFunction getEnclosingCallable(Instruction n) {
+ pragma[only_bind_into](result) = pragma[only_bind_out](n).getEnclosingIRFunction()
}
/** Holds if `nodeFrom` flows to `nodeTo`. */
-private predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, MustFlowConfiguration config) {
+private predicate step(Instruction nodeFrom, Instruction nodeTo, MustFlowConfiguration config) {
exists(config) and
Cached::step(pragma[only_bind_into](nodeFrom), pragma[only_bind_into](nodeTo)) and
(
@@ -227,37 +227,37 @@ private predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, MustFlowC
getEnclosingCallable(nodeFrom) = getEnclosingCallable(nodeTo)
)
or
- config.isAdditionalFlowStep(nodeFrom, nodeTo)
+ config.isAdditionalFlowStep(nodeFrom.getAUse(), nodeTo)
}
private newtype TLocalPathNode =
- MkLocalPathNode(DataFlow::Node n, MustFlowConfiguration config) {
+ MkLocalPathNode(Instruction n, MustFlowConfiguration config) {
flowsToSink(n, config) and
(
config.isSource(n)
or
- exists(MustFlowPathNode mid | step(mid.getNode(), n, config))
+ exists(MustFlowPathNode mid | step(mid.getInstruction(), n, config))
)
}
/** A `Node` that is in a path from a source to a sink. */
class MustFlowPathNode extends TLocalPathNode {
- DataFlow::Node n;
+ Instruction n;
MustFlowPathNode() { this = MkLocalPathNode(n, _) }
/** Gets the underlying node. */
- DataFlow::Node getNode() { result = n }
+ Instruction getInstruction() { result = n }
/** Gets a textual representation of this node. */
- string toString() { result = n.toString() }
+ string toString() { result = n.getAst().toString() }
/** Gets the location of this element. */
Location getLocation() { result = n.getLocation() }
/** Gets a successor node, if any. */
MustFlowPathNode getASuccessor() {
- step(this.getNode(), result.getNode(), this.getConfiguration())
+ step(this.getInstruction(), result.getInstruction(), this.getConfiguration())
}
/** Gets the associated configuration. */
@@ -265,7 +265,7 @@ class MustFlowPathNode extends TLocalPathNode {
}
private class MustFlowPathSink extends MustFlowPathNode {
- MustFlowPathSink() { this.getConfiguration().isSink(this.getNode()) }
+ MustFlowPathSink() { this.getConfiguration().isSink(this.getInstruction().getAUse()) }
}
/**
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
index 3c41b1876dc1..ccde916a3c2e 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
+++ b/cpp/ql/lib/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
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ 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())
+ )
+}
+
+/**
+ * Holds if flow from a parameter at position `pos` inside `c` to a return node of
+ * kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[c, pos, kind]
+private predicate parameterFlowThroughAllowed(
+ DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
+) {
+ exists(ParamNodeEx p |
+ p.isParameterOf(c, pos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
+}
+
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ 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, ReturnKindExt kind, Configuration config) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind()
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,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]
@@ -1087,10 +1122,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)
)
}
@@ -1099,10 +1135,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)
)
}
@@ -1115,12 +1152,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
@@ -1136,10 +1174,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
@@ -1156,7 +1194,9 @@ private signature module StageSig {
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, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,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(
@@ -1247,14 +1288,14 @@ private module MkStage {
pragma[nomagic]
private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
+ NodeEx out, boolean allowsFieldFlow, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ flowOutOfCall(call, ret, kind, 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)
+ PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call) and
+ c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
+ summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage {
)
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, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParameterPositionNone() 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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParameterPositionNone() 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, config) and
+ flowOutOfCall(call, ret, _, node, allowsFieldFlow, 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, ParameterPosition summaryCtx0, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
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,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ 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)
)
@@ -1374,21 +1435,21 @@ private module MkStage {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
- fwdFlow(node1, state, cc, argAp, ap, config) and
+ fwdFlow(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, Cc innercc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, argAp, ap, config) and
+ fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage {
}
pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutFromArg(
+ DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
+ Configuration config
) {
exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
+ DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
- 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutFromArg(
- DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, 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()
+ fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
+ TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
+ config) and
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
+ ParameterPosition pos, Ap ap, Configuration config
) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ exists(ParamNodeEx param |
+ fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
+ pos = param.getPosition()
)
}
@@ -1440,146 +1493,171 @@ 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,
+ private predicate returnFlowsThrough0(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
+ ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
+ ) {
+ exists(boolean allowsFieldFlow |
+ fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
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))
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ private predicate returnFlowsThrough(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, 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(DataFlowCallable c, ParameterPosition ppos |
+ returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
+ p.isParameterOf(c, ppos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
}
pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
+ exists(Ap argAp |
+ flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
+ pragma[only_bind_into](config)) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
+ returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
+ pragma[only_bind_into](config))
+ )
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
- * 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
+ flowIntoCall(_, node, p, allowsFieldFlow, 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, ReturnKindExt returnKind0, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, 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(ReturnKindExt kind |
+ revFlowOut(_, node, kind, state, _, _, ap, config) and
+ if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
+ then (
+ returnCtx = TReturnCtxMaybeFlowThrough(kind) 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
}
@@ -1599,35 +1677,27 @@ 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, ReturnKindExt kind, 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
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- pragma[nomagic]
- private predicate revFlowInNotToReturn(
- ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, false, returnAp, ap, config) and
- flowIntoCall(_, arg, p, allowsFieldFlow, config) and
+ revFlow(out, state, returnCtx, returnAp, ap, config) and
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
+ Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
+ revFlow(pragma[only_bind_into](p), state,
+ TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,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, ReturnKindExt kind, 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, kind, state, returnCtx, returnAp, ap, config) and
+ returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ 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, ReturnKindExt kind, Configuration config
) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
+ parameterFlowThroughAllowed(p, kind)
}
- 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, ReturnKindExt kind |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
+ )
+ }
+
+ pragma[nomagic]
+ predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
+ exists(ParamNodeEx p, Ap ap |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
+ ReturnCtx returnCtx, 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)
+ revFlow(arg, state, returnCtx, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,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. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam {
exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,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))
}
@@ -2021,8 +2091,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)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,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)
+ Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
+ Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
+ config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam {
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))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
- NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
+ NodeEx n, DataFlowCallable c, ParameterPosition pos, 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
+ c = n.getEnclosingCallable() and
+ Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
+ Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
+ TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ 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(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
+ Stage4::parameterMayFlowThrough(p, apa, config) and
+ nodeMayUseSummary0(n, c, pos, state, apa, config) and
+ p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
- Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
+ Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,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)
)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
index 3c41b1876dc1..ccde916a3c2e 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
+++ b/cpp/ql/lib/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
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ 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())
+ )
+}
+
+/**
+ * Holds if flow from a parameter at position `pos` inside `c` to a return node of
+ * kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[c, pos, kind]
+private predicate parameterFlowThroughAllowed(
+ DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
+) {
+ exists(ParamNodeEx p |
+ p.isParameterOf(c, pos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
+}
+
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ 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, ReturnKindExt kind, Configuration config) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind()
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,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]
@@ -1087,10 +1122,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)
)
}
@@ -1099,10 +1135,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)
)
}
@@ -1115,12 +1152,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
@@ -1136,10 +1174,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
@@ -1156,7 +1194,9 @@ private signature module StageSig {
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, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,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(
@@ -1247,14 +1288,14 @@ private module MkStage {
pragma[nomagic]
private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
+ NodeEx out, boolean allowsFieldFlow, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ flowOutOfCall(call, ret, kind, 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)
+ PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call) and
+ c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
+ summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage {
)
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, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParameterPositionNone() 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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParameterPositionNone() 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, config) and
+ flowOutOfCall(call, ret, _, node, allowsFieldFlow, 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, ParameterPosition summaryCtx0, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
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,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ 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)
)
@@ -1374,21 +1435,21 @@ private module MkStage {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
- fwdFlow(node1, state, cc, argAp, ap, config) and
+ fwdFlow(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, Cc innercc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, argAp, ap, config) and
+ fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage {
}
pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutFromArg(
+ DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
+ Configuration config
) {
exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
+ DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
- 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutFromArg(
- DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, 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()
+ fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
+ TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
+ config) and
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
+ ParameterPosition pos, Ap ap, Configuration config
) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ exists(ParamNodeEx param |
+ fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
+ pos = param.getPosition()
)
}
@@ -1440,146 +1493,171 @@ 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,
+ private predicate returnFlowsThrough0(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
+ ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
+ ) {
+ exists(boolean allowsFieldFlow |
+ fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
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))
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ private predicate returnFlowsThrough(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, 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(DataFlowCallable c, ParameterPosition ppos |
+ returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
+ p.isParameterOf(c, ppos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
}
pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
+ exists(Ap argAp |
+ flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
+ pragma[only_bind_into](config)) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
+ returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
+ pragma[only_bind_into](config))
+ )
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
- * 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
+ flowIntoCall(_, node, p, allowsFieldFlow, 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, ReturnKindExt returnKind0, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, 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(ReturnKindExt kind |
+ revFlowOut(_, node, kind, state, _, _, ap, config) and
+ if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
+ then (
+ returnCtx = TReturnCtxMaybeFlowThrough(kind) 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
}
@@ -1599,35 +1677,27 @@ 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, ReturnKindExt kind, 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
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- pragma[nomagic]
- private predicate revFlowInNotToReturn(
- ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, false, returnAp, ap, config) and
- flowIntoCall(_, arg, p, allowsFieldFlow, config) and
+ revFlow(out, state, returnCtx, returnAp, ap, config) and
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
+ Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
+ revFlow(pragma[only_bind_into](p), state,
+ TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,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, ReturnKindExt kind, 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, kind, state, returnCtx, returnAp, ap, config) and
+ returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ 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, ReturnKindExt kind, Configuration config
) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
+ parameterFlowThroughAllowed(p, kind)
}
- 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, ReturnKindExt kind |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
+ )
+ }
+
+ pragma[nomagic]
+ predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
+ exists(ParamNodeEx p, Ap ap |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
+ ReturnCtx returnCtx, 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)
+ revFlow(arg, state, returnCtx, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,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. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam {
exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,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))
}
@@ -2021,8 +2091,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)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,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)
+ Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
+ Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
+ config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam {
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))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
- NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
+ NodeEx n, DataFlowCallable c, ParameterPosition pos, 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
+ c = n.getEnclosingCallable() and
+ Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
+ Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
+ TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ 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(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
+ Stage4::parameterMayFlowThrough(p, apa, config) and
+ nodeMayUseSummary0(n, c, pos, state, apa, config) and
+ p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
- Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
+ Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,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)
)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
index 3c41b1876dc1..ccde916a3c2e 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
+++ b/cpp/ql/lib/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
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ 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())
+ )
+}
+
+/**
+ * Holds if flow from a parameter at position `pos` inside `c` to a return node of
+ * kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[c, pos, kind]
+private predicate parameterFlowThroughAllowed(
+ DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
+) {
+ exists(ParamNodeEx p |
+ p.isParameterOf(c, pos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
+}
+
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ 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, ReturnKindExt kind, Configuration config) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind()
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,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]
@@ -1087,10 +1122,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)
)
}
@@ -1099,10 +1135,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)
)
}
@@ -1115,12 +1152,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
@@ -1136,10 +1174,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
@@ -1156,7 +1194,9 @@ private signature module StageSig {
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, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,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(
@@ -1247,14 +1288,14 @@ private module MkStage {
pragma[nomagic]
private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
+ NodeEx out, boolean allowsFieldFlow, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ flowOutOfCall(call, ret, kind, 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)
+ PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call) and
+ c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
+ summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage {
)
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, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParameterPositionNone() 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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParameterPositionNone() 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, config) and
+ flowOutOfCall(call, ret, _, node, allowsFieldFlow, 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, ParameterPosition summaryCtx0, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
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,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ 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)
)
@@ -1374,21 +1435,21 @@ private module MkStage {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
- fwdFlow(node1, state, cc, argAp, ap, config) and
+ fwdFlow(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, Cc innercc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, argAp, ap, config) and
+ fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage {
}
pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutFromArg(
+ DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
+ Configuration config
) {
exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
+ DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
- 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutFromArg(
- DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, 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()
+ fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
+ TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
+ config) and
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
+ ParameterPosition pos, Ap ap, Configuration config
) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ exists(ParamNodeEx param |
+ fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
+ pos = param.getPosition()
)
}
@@ -1440,146 +1493,171 @@ 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,
+ private predicate returnFlowsThrough0(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
+ ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
+ ) {
+ exists(boolean allowsFieldFlow |
+ fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
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))
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ private predicate returnFlowsThrough(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, 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(DataFlowCallable c, ParameterPosition ppos |
+ returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
+ p.isParameterOf(c, ppos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
}
pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
+ exists(Ap argAp |
+ flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
+ pragma[only_bind_into](config)) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
+ returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
+ pragma[only_bind_into](config))
+ )
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
- * 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
+ flowIntoCall(_, node, p, allowsFieldFlow, 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, ReturnKindExt returnKind0, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, 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(ReturnKindExt kind |
+ revFlowOut(_, node, kind, state, _, _, ap, config) and
+ if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
+ then (
+ returnCtx = TReturnCtxMaybeFlowThrough(kind) 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
}
@@ -1599,35 +1677,27 @@ 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, ReturnKindExt kind, 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
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- pragma[nomagic]
- private predicate revFlowInNotToReturn(
- ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, false, returnAp, ap, config) and
- flowIntoCall(_, arg, p, allowsFieldFlow, config) and
+ revFlow(out, state, returnCtx, returnAp, ap, config) and
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
+ Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
+ revFlow(pragma[only_bind_into](p), state,
+ TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,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, ReturnKindExt kind, 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, kind, state, returnCtx, returnAp, ap, config) and
+ returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ 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, ReturnKindExt kind, Configuration config
) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
+ parameterFlowThroughAllowed(p, kind)
}
- 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, ReturnKindExt kind |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
+ )
+ }
+
+ pragma[nomagic]
+ predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
+ exists(ParamNodeEx p, Ap ap |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
+ ReturnCtx returnCtx, 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)
+ revFlow(arg, state, returnCtx, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,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. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam {
exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,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))
}
@@ -2021,8 +2091,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)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,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)
+ Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
+ Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
+ config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam {
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))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
- NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
+ NodeEx n, DataFlowCallable c, ParameterPosition pos, 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
+ c = n.getEnclosingCallable() and
+ Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
+ Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
+ TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ 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(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
+ Stage4::parameterMayFlowThrough(p, apa, config) and
+ nodeMayUseSummary0(n, c, pos, state, apa, config) and
+ p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
- Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
+ Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,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)
)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
index 3c41b1876dc1..ccde916a3c2e 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
+++ b/cpp/ql/lib/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
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
-
- predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ 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())
+ )
+}
+
+/**
+ * Holds if flow from a parameter at position `pos` inside `c` to a return node of
+ * kind `kind` is allowed.
+ *
+ * We don't expect a parameter to return stored in itself, unless
+ * explicitly allowed
+ */
+bindingset[c, pos, kind]
+private predicate parameterFlowThroughAllowed(
+ DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
+) {
+ exists(ParamNodeEx p |
+ p.isParameterOf(c, pos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
+}
+
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ 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, ReturnKindExt kind, Configuration config) {
+ throughFlowNodeCand(ret, config) and
+ kind = ret.getKind()
+ }
+
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,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]
@@ -1087,10 +1122,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)
)
}
@@ -1099,10 +1135,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)
)
}
@@ -1115,12 +1152,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
@@ -1136,10 +1174,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
@@ -1156,7 +1194,9 @@ private signature module StageSig {
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, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,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(
@@ -1247,14 +1288,14 @@ private module MkStage {
pragma[nomagic]
private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
+ DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
+ NodeEx out, boolean allowsFieldFlow, Configuration config
) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ flowOutOfCall(call, ret, kind, 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)
+ PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
+ matchesCall(ccc, call) and
+ c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
+ fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, 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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
+ summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage {
)
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, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
+ summaryCtx = TParameterPositionNone() 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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
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 = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
+ if PrevStage::parameterMayFlowThrough(node, apa, config)
+ then (
+ summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
+ argAp = apSome(ap)
+ ) else (
+ summaryCtx = TParameterPositionNone() 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, config) and
+ flowOutOfCall(call, ret, _, node, allowsFieldFlow, 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, ParameterPosition summaryCtx0, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
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,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
+ fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ 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)
)
@@ -1374,21 +1435,21 @@ private module MkStage {
pragma[nomagic]
private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
- fwdFlow(node1, state, cc, argAp, ap, config) and
+ fwdFlow(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, Cc innercc,
+ ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, argAp, ap, config) and
+ fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage {
}
pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ private predicate fwdFlowOutFromArg(
+ DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
+ Configuration config
) {
exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
+ DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
- 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutFromArg(
- DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, 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()
+ fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
+ TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
+ config) and
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
+ ParameterPosition pos, Ap ap, Configuration config
) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ exists(ParamNodeEx param |
+ fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
+ pos = param.getPosition()
)
}
@@ -1440,146 +1493,171 @@ 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,
+ private predicate returnFlowsThrough0(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
+ ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
+ ) {
+ exists(boolean allowsFieldFlow |
+ fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
+ flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
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))
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ private predicate returnFlowsThrough(
+ RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, 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(DataFlowCallable c, ParameterPosition ppos |
+ returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
+ p.isParameterOf(c, ppos) and
+ parameterFlowThroughAllowed(p, kind)
+ )
}
pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
+ exists(Ap argAp |
+ flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
+ pragma[only_bind_into](config)) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
+ returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
+ pragma[only_bind_into](config))
+ )
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
- * 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
+ flowIntoCall(_, node, p, allowsFieldFlow, 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, ReturnKindExt returnKind0, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, 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(ReturnKindExt kind |
+ revFlowOut(_, node, kind, state, _, _, ap, config) and
+ if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
+ then (
+ returnCtx = TReturnCtxMaybeFlowThrough(kind) 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
}
@@ -1599,35 +1677,27 @@ 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, ReturnKindExt kind, 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
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- pragma[nomagic]
- private predicate revFlowInNotToReturn(
- ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, false, returnAp, ap, config) and
- flowIntoCall(_, arg, p, allowsFieldFlow, config) and
+ revFlow(out, state, returnCtx, returnAp, ap, config) and
+ flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
- DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
+ DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
+ Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
+ revFlow(pragma[only_bind_into](p), state,
+ TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
+ (if allowsFieldFlow = false then ap instanceof ApNil else any()) and
+ parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,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, ReturnKindExt kind, 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, kind, state, returnCtx, returnAp, ap, config) and
+ returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ 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, ReturnKindExt kind, Configuration config
) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
+ revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
+ parameterFlowThroughAllowed(p, kind)
}
- 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, ReturnKindExt kind |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
+ )
+ }
+
+ pragma[nomagic]
+ predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
+ exists(ParamNodeEx p, Ap ap |
+ returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
+ parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
+ ReturnCtx returnCtx, 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)
+ revFlow(arg, state, returnCtx, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
+ revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,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, ParameterPositionOption summaryCtx, ApOption argAp,
+ Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,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. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage::StageParam {
exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,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))
}
@@ -2021,8 +2091,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)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,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)
+ Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
+ Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
+ config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage::StageParam {
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))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
- NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
+ NodeEx n, DataFlowCallable c, ParameterPosition pos, 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
+ c = n.getEnclosingCallable() and
+ Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
+ Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
+ TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ 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(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
+ Stage4::parameterMayFlowThrough(p, apa, config) and
+ nodeMayUseSummary0(n, c, pos, state, apa, config) and
+ p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
- Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
+ Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,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)
)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
index ae9c6f3f12ef..f981834a6d4d 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
@@ -915,6 +915,17 @@ private module Cached {
TDataFlowCallNone() or
TDataFlowCallSome(DataFlowCall call)
+ cached
+ newtype TParameterPositionOption =
+ TParameterPositionNone() or
+ TParameterPositionSome(ParameterPosition pos)
+
+ cached
+ newtype TReturnCtx =
+ TReturnCtxNone() or
+ TReturnCtxNoFlowThrough() or
+ TReturnCtxMaybeFlowThrough(ReturnKindExt kind)
+
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
@@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption {
}
}
+/** An optional `ParameterPosition`. */
+class ParameterPositionOption extends TParameterPositionOption {
+ string toString() {
+ this = TParameterPositionNone() and
+ result = "(none)"
+ or
+ exists(ParameterPosition pos |
+ this = TParameterPositionSome(pos) and
+ result = pos.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(ReturnKindExt kind)`: return flow, of kind `kind`, 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(ReturnKindExt kind |
+ this = TReturnCtxMaybeFlowThrough(kind) and
+ result = kind.toString()
+ )
+ }
+}
+
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/Iterator.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/Iterator.qll
index 2fa9803d0533..599b2c47e224 100644
--- a/cpp/ql/lib/semmle/code/cpp/models/implementations/Iterator.qll
+++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/Iterator.qll
@@ -31,7 +31,17 @@ private class IteratorTraits extends Class {
* `std::iterator_traits` instantiation for it.
*/
private class IteratorByTraits extends Iterator {
- IteratorByTraits() { exists(IteratorTraits it | it.getIteratorType() = this) }
+ IteratorTraits trait;
+
+ IteratorByTraits() { trait.getIteratorType() = this }
+
+ override Type getValueType() {
+ exists(TypedefType t |
+ trait.getAMember() = t and
+ t.getName() = "value_type" and
+ result = t.getUnderlyingType()
+ )
+ }
}
/**
@@ -42,20 +52,27 @@ private class IteratorByTraits extends Iterator {
*/
private class IteratorByPointer extends Iterator instanceof PointerType {
IteratorByPointer() { not this instanceof IteratorByTraits }
+
+ override Type getValueType() { result = super.getBaseType() }
}
/**
* A type which has the typedefs expected for an iterator.
*/
private class IteratorByTypedefs extends Iterator, Class {
+ TypedefType valueType;
+
IteratorByTypedefs() {
this.getAMember().(TypedefType).hasName("difference_type") and
- this.getAMember().(TypedefType).hasName("value_type") and
+ valueType = this.getAMember() and
+ valueType.hasName("value_type") and
this.getAMember().(TypedefType).hasName("pointer") and
this.getAMember().(TypedefType).hasName("reference") and
this.getAMember().(TypedefType).hasName("iterator_category") and
not this.hasQualifiedName(["std", "bsl"], "iterator_traits")
}
+
+ override Type getValueType() { result = valueType.getUnderlyingType() }
}
/**
@@ -63,6 +80,8 @@ private class IteratorByTypedefs extends Iterator, Class {
*/
private class StdIterator extends Iterator, Class {
StdIterator() { this.hasQualifiedName(["std", "bsl"], "iterator") }
+
+ override Type getValueType() { result = this.getTemplateArgument(1).(Type).getUnderlyingType() }
}
/**
@@ -166,12 +185,15 @@ private class IteratorSubOperator extends Operator, TaintFunction {
/**
* A non-member `operator+=` or `operator-=` function for an iterator type.
*/
-private class IteratorAssignArithmeticOperator extends Operator, DataFlowFunction, TaintFunction {
+class IteratorAssignArithmeticOperator extends Operator {
IteratorAssignArithmeticOperator() {
this.hasName(["operator+=", "operator-="]) and
exists(getIteratorArgumentInput(this, 0))
}
+}
+private class IteratorAssignArithmeticOperatorModel extends IteratorAssignArithmeticOperator,
+ DataFlowFunction, TaintFunction {
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input.isParameter(0) and
output.isReturnValue()
@@ -210,11 +232,14 @@ class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunc
/**
* An `operator++` or `operator--` member function for an iterator type.
*/
-private class IteratorCrementMemberOperator extends MemberFunction, DataFlowFunction, TaintFunction {
+class IteratorCrementMemberOperator extends MemberFunction {
IteratorCrementMemberOperator() {
this.getClassAndName(["operator++", "operator--"]) instanceof Iterator
}
+}
+private class IteratorCrementMemberOperatorModel extends IteratorCrementMemberOperator,
+ DataFlowFunction, TaintFunction {
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierAddress() and
output.isReturnValue()
diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdContainer.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdContainer.qll
index 8c531891bcda..9d74f4ac051d 100644
--- a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdContainer.qll
+++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdContainer.qll
@@ -5,38 +5,53 @@
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.Iterator
+/**
+ * A sequence container template class (for example, `std::vector`) from the
+ * standard library.
+ */
+abstract class StdSequenceContainer extends Class {
+ Type getElementType() { result = this.getTemplateArgument(0) }
+}
+
/**
* The `std::array` template class.
*/
-private class Array extends Class {
+private class Array extends StdSequenceContainer {
Array() { this.hasQualifiedName(["std", "bsl"], "array") }
}
+/**
+ * The `std::string` template class.
+ */
+private class String extends StdSequenceContainer {
+ String() { this.hasQualifiedName(["std", "bsl"], "basic_string") }
+}
+
/**
* The `std::deque` template class.
*/
-private class Deque extends Class {
+private class Deque extends StdSequenceContainer {
Deque() { this.hasQualifiedName(["std", "bsl"], "deque") }
}
/**
* The `std::forward_list` template class.
*/
-private class ForwardList extends Class {
+private class ForwardList extends StdSequenceContainer {
ForwardList() { this.hasQualifiedName(["std", "bsl"], "forward_list") }
}
/**
* The `std::list` template class.
*/
-private class List extends Class {
+private class List extends StdSequenceContainer {
List() { this.hasQualifiedName(["std", "bsl"], "list") }
}
/**
* The `std::vector` template class.
*/
-private class Vector extends Class {
+private class Vector extends StdSequenceContainer {
Vector() { this.hasQualifiedName(["std", "bsl"], "vector") }
}
diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll
index 9ac92597b1a5..cf3ecb5e8924 100644
--- a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll
+++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll
@@ -15,15 +15,6 @@ private class StdBasicString extends ClassTemplateInstantiation {
StdBasicString() { this.hasQualifiedName(["std", "bsl"], "basic_string") }
}
-/**
- * The `std::basic_string::iterator` declaration.
- */
-private class StdBasicStringIterator extends Iterator, Type {
- StdBasicStringIterator() {
- this.getEnclosingElement() instanceof StdBasicString and this.hasName("iterator")
- }
-}
-
/**
* A `std::string` function for which taint should be propagated.
*/
diff --git a/cpp/ql/lib/semmle/code/cpp/models/interfaces/Iterator.qll b/cpp/ql/lib/semmle/code/cpp/models/interfaces/Iterator.qll
index 9a260a332557..d5d2b6ffe817 100644
--- a/cpp/ql/lib/semmle/code/cpp/models/interfaces/Iterator.qll
+++ b/cpp/ql/lib/semmle/code/cpp/models/interfaces/Iterator.qll
@@ -29,5 +29,17 @@ abstract class GetIteratorFunction extends Function {
/**
* A type which can be used as an iterator.
+ *
+ * Note: Do _not_ `extend` when inheriting from this class in queries. Always use `instanceof`:
+ * ```
+ * class MyIterator instanceof Iterator { ... }
+ * ```
*/
-abstract class Iterator extends Type { }
+abstract class Iterator extends Type {
+ /**
+ * Gets the value type of this iterator, if any.
+ *
+ * For example, the value type of a `std::vector::iterator` is `int`.
+ */
+ Type getValueType() { none() }
+}
diff --git a/cpp/ql/lib/semmle/code/cpp/security/TaintTracking.qll b/cpp/ql/lib/semmle/code/cpp/security/TaintTracking.qll
index 5b8f221f73a3..49ef4137aa1d 100644
--- a/cpp/ql/lib/semmle/code/cpp/security/TaintTracking.qll
+++ b/cpp/ql/lib/semmle/code/cpp/security/TaintTracking.qll
@@ -1,4 +1,4 @@
-/*
+/**
* Support for tracking tainted data through the program. This is an alias for
* `semmle.code.cpp.ir.dataflow.DefaultTaintTracking` provided for backwards
* compatibility.
diff --git a/cpp/ql/lib/semmle/code/cpp/valuenumbering/GlobalValueNumberingImpl.qll b/cpp/ql/lib/semmle/code/cpp/valuenumbering/GlobalValueNumberingImpl.qll
index 7dd55dbfde31..10e3d3ba1c21 100644
--- a/cpp/ql/lib/semmle/code/cpp/valuenumbering/GlobalValueNumberingImpl.qll
+++ b/cpp/ql/lib/semmle/code/cpp/valuenumbering/GlobalValueNumberingImpl.qll
@@ -1,4 +1,8 @@
/**
+ * DEPRECATED: This library has been replaced with a newer version which
+ * provides better performance and precision. Use
+ * `semmle.code.cpp.valuenumbering.GlobalValueNumbering` instead.
+ *
* Provides an implementation of Global Value Numbering.
* See https://en.wikipedia.org/wiki/Global_value_numbering
*
@@ -221,7 +225,7 @@ private newtype GvnBase =
* expression with this `GVN` and using its `toString` and `getLocation`
* methods.
*/
-class GVN extends GvnBase {
+deprecated class GVN extends GvnBase {
GVN() { this instanceof GvnBase }
/** Gets an expression that has this GVN. */
@@ -503,7 +507,7 @@ private predicate mk_Deref(GVN p, ControlFlowNode dominator, PointerDereferenceE
/** Gets the global value number of expression `e`. */
cached
-GVN globalValueNumber(Expr e) {
+deprecated GVN globalValueNumber(Expr e) {
exists(int val, Type t |
mk_IntConst(val, t, e) and
result = GVN_IntConst(val, t)
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql
index 26c8ae4c2581..af4bd8c61a36 100644
--- a/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql
+++ b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql
@@ -26,11 +26,11 @@ predicate intentionallyReturnsStackPointer(Function f) {
class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
ReturnStackAllocatedMemoryConfig() { this = "ReturnStackAllocatedMemoryConfig" }
- override predicate isSource(DataFlow::Node source) {
+ override predicate isSource(Instruction source) {
// Holds if `source` is a node that represents the use of a stack variable
exists(VariableAddressInstruction var, Function func |
- var = source.asInstruction() and
- func = var.getEnclosingFunction() and
+ var = source and
+ func = source.getEnclosingFunction() and
var.getAstVariable() instanceof StackVariable and
// Pointer-to-member types aren't properly handled in the dbscheme.
not var.getResultType() instanceof PointerToMemberType and
@@ -40,7 +40,7 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
)
}
- override predicate isSink(DataFlow::Node sink) {
+ override predicate isSink(Operand sink) {
// Holds if `sink` is a node that represents the `StoreInstruction` that is subsequently used in
// a `ReturnValueInstruction`.
// We use the `StoreInstruction` instead of the instruction that defines the
@@ -48,7 +48,7 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
exists(StoreInstruction store |
store.getDestinationAddress().(VariableAddressInstruction).getIRVariable() instanceof
IRReturnVariable and
- sink.asOperand() = store.getSourceValueOperand()
+ sink = store.getSourceValueOperand()
)
}
@@ -77,10 +77,10 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
* }
* ```
*/
- override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
- node2.asInstruction().(FieldAddressInstruction).getObjectAddressOperand() = node1.asOperand()
+ override predicate isAdditionalFlowStep(Operand node1, Instruction node2) {
+ node2.(FieldAddressInstruction).getObjectAddressOperand() = node1
or
- node2.asInstruction().(PointerOffsetInstruction).getLeftOperand() = node1.asOperand()
+ node2.(PointerOffsetInstruction).getLeftOperand() = node1
}
}
@@ -89,6 +89,6 @@ from
ReturnStackAllocatedMemoryConfig conf
where
conf.hasFlowPath(pragma[only_bind_into](source), pragma[only_bind_into](sink)) and
- source.getNode().asInstruction() = var
-select sink.getNode(), source, sink, "May return stack-allocated memory from $@.", var.getAst(),
- var.getAst().toString()
+ source.getInstruction() = var
+select sink.getInstruction(), source, sink, "May return stack-allocated memory from $@.",
+ var.getAst(), var.getAst().toString()
diff --git a/cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql b/cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql
index db1816f7a720..bb62cfc17553 100644
--- a/cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql
+++ b/cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql
@@ -22,37 +22,40 @@ import PathGraph
class UnsafeUseOfThisConfig extends MustFlowConfiguration {
UnsafeUseOfThisConfig() { this = "UnsafeUseOfThisConfig" }
- override predicate isSource(DataFlow::Node source) { isSource(source, _, _) }
+ override predicate isSource(Instruction source) { isSource(source, _, _) }
- override predicate isSink(DataFlow::Node sink) { isSink(sink, _) }
+ override predicate isSink(Operand sink) { isSink(sink, _) }
}
-/** Holds if `instr` is a `this` pointer used by the call instruction `call`. */
-predicate isSink(DataFlow::Node sink, CallInstruction call) {
+/** Holds if `sink` is a `this` pointer used by the call instruction `call`. */
+predicate isSink(Operand sink, CallInstruction call) {
exists(PureVirtualFunction func |
call.getStaticCallTarget() = func and
- call.getThisArgument() = sink.asInstruction() and
+ call.getThisArgumentOperand() = sink and
// Weed out implicit calls to destructors of a base class
not func instanceof Destructor
)
}
-/** Holds if `init` initializes the `this` pointer in class `c`. */
-predicate isSource(DataFlow::Node source, string msg, Class c) {
- exists(InitializeParameterInstruction init | init = source.asInstruction() |
- (
- exists(Constructor func |
- not func instanceof CopyConstructor and
- not func instanceof MoveConstructor and
- func = init.getEnclosingFunction() and
- msg = "construction"
- )
- or
- init.getEnclosingFunction() instanceof Destructor and msg = "destruction"
- ) and
- init.getIRVariable() instanceof IRThisVariable and
- init.getEnclosingFunction().getDeclaringType() = c
- )
+/**
+ * Holds if `source` initializes the `this` pointer in class `c`.
+ *
+ * The string `msg` describes whether the enclosing function is a
+ * constructor or destructor.
+ */
+predicate isSource(InitializeParameterInstruction source, string msg, Class c) {
+ (
+ exists(Constructor func |
+ not func instanceof CopyConstructor and
+ not func instanceof MoveConstructor and
+ func = source.getEnclosingFunction() and
+ msg = "construction"
+ )
+ or
+ source.getEnclosingFunction() instanceof Destructor and msg = "destruction"
+ ) and
+ source.getIRVariable() instanceof IRThisVariable and
+ source.getEnclosingFunction().getDeclaringType() = c
}
/**
@@ -68,8 +71,8 @@ predicate flows(
) {
exists(UnsafeUseOfThisConfig conf |
conf.hasFlowPath(source, sink) and
- isSource(source.getNode(), msg, sourceClass) and
- isSink(sink.getNode(), call)
+ isSource(source.getInstruction(), msg, sourceClass) and
+ isSink(sink.getInstruction().getAUse(), call)
)
}
diff --git a/cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql b/cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql
index 47ed2b704d52..4dacffb6a559 100644
--- a/cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql
+++ b/cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql
@@ -12,8 +12,8 @@
*/
/*
- * Note: this query is not assigned a precision yet because we don't want it on
- * LGTM until its performance is well understood.
+ * Note: this query is not assigned a precision yet because we don't want it
+ * to be included in query suites until its performance is well understood.
*/
import cpp
diff --git a/cpp/ql/src/Metrics/Dependencies/ExternalDependencies.qll b/cpp/ql/src/Metrics/Dependencies/ExternalDependencies.qll
index 352cf86ddd5b..967a9987a4f6 100644
--- a/cpp/ql/src/Metrics/Dependencies/ExternalDependencies.qll
+++ b/cpp/ql/src/Metrics/Dependencies/ExternalDependencies.qll
@@ -52,7 +52,7 @@ class Library extends LibraryT {
// The versions reported for C/C++ dependencies are just the versions that
// happen to be installed on the system where the build takes place.
// Reporting those versions is likely to cause misunderstandings, both for
- // people reading them and for the vulnerability checker of lgtm.
+ // people reading them and for vulnerability checkers.
result = "unknown"
}
diff --git a/cpp/ql/src/jsf/4.09 Style/Naming.qll b/cpp/ql/src/jsf/4.09 Style/Naming.qll
index 264a6bac219f..5df3724a0679 100644
--- a/cpp/ql/src/jsf/4.09 Style/Naming.qll
+++ b/cpp/ql/src/jsf/4.09 Style/Naming.qll
@@ -1,4 +1,4 @@
-/*
+/**
* Common functions for implementing naming conventions
*
* Naming rules are the following:
diff --git a/cpp/ql/src/qlpack.yml b/cpp/ql/src/qlpack.yml
index 4ee5b28070c4..75602332e296 100644
--- a/cpp/ql/src/qlpack.yml
+++ b/cpp/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/cpp-queries
-version: 0.4.4
+version: 0.4.5-dev
groups:
- cpp
- queries
diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ast_gvn.expected b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ast_gvn.expected
index d5d46ee0b72d..69c21b5e0b1d 100644
--- a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ast_gvn.expected
+++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ast_gvn.expected
@@ -1,3 +1,4 @@
+WARNING: Type GVN has been deprecated and may be removed in future (ast_gvn.ql:4,6-9)
| test.cpp:5:3:5:3 | x | 5:c3-c3 6:c3-c3 |
| test.cpp:5:7:5:8 | p0 | 5:c7-c8 6:c7-c8 |
| test.cpp:5:7:5:13 | ... + ... | 5:c7-c13 6:c7-c13 7:c7-c7 |
diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ast_uniqueness.expected b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ast_uniqueness.expected
index e69de29bb2d1..d94d58ad5eac 100644
--- a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ast_uniqueness.expected
+++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ast_uniqueness.expected
@@ -0,0 +1,3 @@
+WARNING: Predicate globalValueNumber has been deprecated and may be removed in future (ast_uniqueness.ql:7,13-30)
+WARNING: Predicate globalValueNumber has been deprecated and may be removed in future (ast_uniqueness.ql:8,30-47)
+WARNING: Type GVN has been deprecated and may be removed in future (ast_uniqueness.ql:8,18-21)
diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/diff_ir_expr.expected b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/diff_ir_expr.expected
index b838a13d5af3..810c83f197f9 100644
--- a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/diff_ir_expr.expected
+++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/diff_ir_expr.expected
@@ -1,3 +1,4 @@
+WARNING: Predicate globalValueNumber has been deprecated and may be removed in future (diff_ir_expr.ql:8,29-51)
| test.cpp:5:3:5:13 | ... = ... | test.cpp:5:3:5:13 | ... = ... | AST only |
| test.cpp:6:3:6:13 | ... = ... | test.cpp:6:3:6:13 | ... = ... | AST only |
| test.cpp:7:3:7:7 | ... = ... | test.cpp:7:3:7:7 | ... = ... | AST only |
diff --git a/cpp/ql/test/query-tests/Critical/UnsafeUseOfThis/UnsafeUseOfThis.expected b/cpp/ql/test/query-tests/Critical/UnsafeUseOfThis/UnsafeUseOfThis.expected
index 067ba7ab4fc6..7f1ee1356ab1 100644
--- a/cpp/ql/test/query-tests/Critical/UnsafeUseOfThis/UnsafeUseOfThis.expected
+++ b/cpp/ql/test/query-tests/Critical/UnsafeUseOfThis/UnsafeUseOfThis.expected
@@ -1,103 +1,61 @@
edges
-| test.cpp:7:3:7:3 | this | test.cpp:8:12:8:15 | Load |
-| test.cpp:8:12:8:15 | Load | test.cpp:8:12:8:15 | this |
+| test.cpp:7:3:7:3 | B | test.cpp:8:12:8:15 | this |
| test.cpp:8:12:8:15 | this | test.cpp:34:16:34:16 | x |
-| test.cpp:11:8:11:8 | b | test.cpp:12:5:12:5 | Load |
-| test.cpp:12:5:12:5 | (reference dereference) | test.cpp:12:5:12:5 | Unary |
-| test.cpp:12:5:12:5 | Load | test.cpp:12:5:12:5 | b |
-| test.cpp:12:5:12:5 | Unary | test.cpp:12:5:12:5 | (A)... |
-| test.cpp:12:5:12:5 | Unary | test.cpp:12:5:12:5 | (reference dereference) |
-| test.cpp:12:5:12:5 | b | test.cpp:12:5:12:5 | Unary |
-| test.cpp:15:3:15:4 | this | test.cpp:16:5:16:5 | Load |
-| test.cpp:16:5:16:5 | Load | test.cpp:16:5:16:5 | this |
-| test.cpp:16:5:16:5 | Unary | file://:0:0:0:0 | (A *)... |
-| test.cpp:16:5:16:5 | this | test.cpp:16:5:16:5 | Unary |
-| test.cpp:21:3:21:3 | Unary | test.cpp:21:13:21:13 | ConvertToNonVirtualBase |
-| test.cpp:21:3:21:3 | this | test.cpp:21:3:21:3 | Unary |
-| test.cpp:21:3:21:3 | this | test.cpp:22:12:22:15 | Load |
-| test.cpp:21:3:21:3 | this | test.cpp:25:7:25:10 | Load |
-| test.cpp:21:13:21:13 | ConvertToNonVirtualBase | test.cpp:7:3:7:3 | this |
+| test.cpp:11:8:11:8 | b | test.cpp:12:5:12:5 | b |
+| test.cpp:12:5:12:5 | (reference dereference) | test.cpp:12:5:12:5 | (A)... |
+| test.cpp:12:5:12:5 | b | test.cpp:12:5:12:5 | (reference dereference) |
+| test.cpp:15:3:15:4 | ~B | test.cpp:16:5:16:5 | this |
+| test.cpp:16:5:16:5 | this | file://:0:0:0:0 | (A *)... |
+| test.cpp:21:3:21:3 | C | test.cpp:21:13:21:13 | call to B |
+| test.cpp:21:3:21:3 | C | test.cpp:22:12:22:15 | this |
+| test.cpp:21:3:21:3 | C | test.cpp:25:7:25:10 | this |
+| test.cpp:21:13:21:13 | call to B | test.cpp:7:3:7:3 | B |
| test.cpp:22:12:22:15 | (B *)... | test.cpp:34:16:34:16 | x |
-| test.cpp:22:12:22:15 | Load | test.cpp:22:12:22:15 | this |
-| test.cpp:22:12:22:15 | Unary | test.cpp:22:12:22:15 | (B *)... |
-| test.cpp:22:12:22:15 | this | test.cpp:22:12:22:15 | Unary |
-| test.cpp:25:7:25:10 | (B *)... | test.cpp:25:7:25:10 | Unary |
-| test.cpp:25:7:25:10 | Load | test.cpp:25:7:25:10 | this |
-| test.cpp:25:7:25:10 | Unary | test.cpp:25:7:25:10 | (A *)... |
-| test.cpp:25:7:25:10 | Unary | test.cpp:25:7:25:10 | (B *)... |
-| test.cpp:25:7:25:10 | this | test.cpp:25:7:25:10 | Unary |
-| test.cpp:31:3:31:3 | this | test.cpp:31:12:31:15 | Load |
-| test.cpp:31:11:31:15 | (B)... | test.cpp:31:11:31:15 | Unary |
+| test.cpp:22:12:22:15 | this | test.cpp:22:12:22:15 | (B *)... |
+| test.cpp:25:7:25:10 | (B *)... | test.cpp:25:7:25:10 | (A *)... |
+| test.cpp:25:7:25:10 | this | test.cpp:25:7:25:10 | (B *)... |
+| test.cpp:31:3:31:3 | D | test.cpp:31:12:31:15 | this |
+| test.cpp:31:11:31:15 | (B)... | test.cpp:31:11:31:15 | (reference to) |
| test.cpp:31:11:31:15 | (reference to) | test.cpp:11:8:11:8 | b |
-| test.cpp:31:11:31:15 | * ... | test.cpp:31:11:31:15 | Unary |
-| test.cpp:31:11:31:15 | Unary | test.cpp:31:11:31:15 | (B)... |
-| test.cpp:31:11:31:15 | Unary | test.cpp:31:11:31:15 | (reference to) |
-| test.cpp:31:12:31:15 | Load | test.cpp:31:12:31:15 | this |
-| test.cpp:31:12:31:15 | Unary | test.cpp:31:11:31:15 | * ... |
-| test.cpp:31:12:31:15 | this | test.cpp:31:12:31:15 | Unary |
-| test.cpp:34:16:34:16 | x | test.cpp:35:3:35:3 | Load |
-| test.cpp:35:3:35:3 | Load | test.cpp:35:3:35:3 | x |
-| test.cpp:35:3:35:3 | Unary | test.cpp:35:3:35:3 | (A *)... |
-| test.cpp:35:3:35:3 | x | test.cpp:35:3:35:3 | Unary |
-| test.cpp:47:3:47:3 | this | test.cpp:48:10:48:13 | Load |
-| test.cpp:48:10:48:13 | (E *)... | test.cpp:48:10:48:13 | Unary |
-| test.cpp:48:10:48:13 | Load | test.cpp:48:10:48:13 | this |
-| test.cpp:48:10:48:13 | Unary | test.cpp:48:6:48:13 | (A *)... |
-| test.cpp:48:10:48:13 | Unary | test.cpp:48:10:48:13 | (E *)... |
-| test.cpp:48:10:48:13 | this | test.cpp:48:10:48:13 | Unary |
+| test.cpp:31:11:31:15 | * ... | test.cpp:31:11:31:15 | (B)... |
+| test.cpp:31:12:31:15 | this | test.cpp:31:11:31:15 | * ... |
+| test.cpp:34:16:34:16 | x | test.cpp:35:3:35:3 | x |
+| test.cpp:35:3:35:3 | x | test.cpp:35:3:35:3 | (A *)... |
+| test.cpp:47:3:47:3 | F | test.cpp:48:10:48:13 | this |
+| test.cpp:48:10:48:13 | (E *)... | test.cpp:48:6:48:13 | (A *)... |
+| test.cpp:48:10:48:13 | this | test.cpp:48:10:48:13 | (E *)... |
nodes
| file://:0:0:0:0 | (A *)... | semmle.label | (A *)... |
-| test.cpp:7:3:7:3 | this | semmle.label | this |
-| test.cpp:8:12:8:15 | Load | semmle.label | Load |
+| test.cpp:7:3:7:3 | B | semmle.label | B |
| test.cpp:8:12:8:15 | this | semmle.label | this |
| test.cpp:11:8:11:8 | b | semmle.label | b |
| test.cpp:12:5:12:5 | (A)... | semmle.label | (A)... |
| test.cpp:12:5:12:5 | (reference dereference) | semmle.label | (reference dereference) |
-| test.cpp:12:5:12:5 | Load | semmle.label | Load |
-| test.cpp:12:5:12:5 | Unary | semmle.label | Unary |
-| test.cpp:12:5:12:5 | Unary | semmle.label | Unary |
| test.cpp:12:5:12:5 | b | semmle.label | b |
-| test.cpp:15:3:15:4 | this | semmle.label | this |
-| test.cpp:16:5:16:5 | Load | semmle.label | Load |
-| test.cpp:16:5:16:5 | Unary | semmle.label | Unary |
+| test.cpp:15:3:15:4 | ~B | semmle.label | ~B |
| test.cpp:16:5:16:5 | this | semmle.label | this |
-| test.cpp:21:3:21:3 | Unary | semmle.label | Unary |
-| test.cpp:21:3:21:3 | this | semmle.label | this |
-| test.cpp:21:13:21:13 | ConvertToNonVirtualBase | semmle.label | ConvertToNonVirtualBase |
+| test.cpp:21:3:21:3 | C | semmle.label | C |
+| test.cpp:21:13:21:13 | call to B | semmle.label | call to B |
| test.cpp:22:12:22:15 | (B *)... | semmle.label | (B *)... |
-| test.cpp:22:12:22:15 | Load | semmle.label | Load |
-| test.cpp:22:12:22:15 | Unary | semmle.label | Unary |
| test.cpp:22:12:22:15 | this | semmle.label | this |
| test.cpp:25:7:25:10 | (A *)... | semmle.label | (A *)... |
| test.cpp:25:7:25:10 | (B *)... | semmle.label | (B *)... |
-| test.cpp:25:7:25:10 | Load | semmle.label | Load |
-| test.cpp:25:7:25:10 | Unary | semmle.label | Unary |
-| test.cpp:25:7:25:10 | Unary | semmle.label | Unary |
| test.cpp:25:7:25:10 | this | semmle.label | this |
-| test.cpp:31:3:31:3 | this | semmle.label | this |
+| test.cpp:31:3:31:3 | D | semmle.label | D |
| test.cpp:31:11:31:15 | (B)... | semmle.label | (B)... |
| test.cpp:31:11:31:15 | (reference to) | semmle.label | (reference to) |
| test.cpp:31:11:31:15 | * ... | semmle.label | * ... |
-| test.cpp:31:11:31:15 | Unary | semmle.label | Unary |
-| test.cpp:31:11:31:15 | Unary | semmle.label | Unary |
-| test.cpp:31:12:31:15 | Load | semmle.label | Load |
-| test.cpp:31:12:31:15 | Unary | semmle.label | Unary |
| test.cpp:31:12:31:15 | this | semmle.label | this |
| test.cpp:34:16:34:16 | x | semmle.label | x |
| test.cpp:35:3:35:3 | (A *)... | semmle.label | (A *)... |
-| test.cpp:35:3:35:3 | Load | semmle.label | Load |
-| test.cpp:35:3:35:3 | Unary | semmle.label | Unary |
| test.cpp:35:3:35:3 | x | semmle.label | x |
-| test.cpp:47:3:47:3 | this | semmle.label | this |
+| test.cpp:47:3:47:3 | F | semmle.label | F |
| test.cpp:48:6:48:13 | (A *)... | semmle.label | (A *)... |
| test.cpp:48:10:48:13 | (E *)... | semmle.label | (E *)... |
-| test.cpp:48:10:48:13 | Load | semmle.label | Load |
-| test.cpp:48:10:48:13 | Unary | semmle.label | Unary |
-| test.cpp:48:10:48:13 | Unary | semmle.label | Unary |
| test.cpp:48:10:48:13 | this | semmle.label | this |
#select
-| test.cpp:12:7:12:7 | call to f | test.cpp:31:3:31:3 | this | test.cpp:12:5:12:5 | (A)... | Call to pure virtual function during construction. |
-| test.cpp:16:5:16:5 | call to f | test.cpp:15:3:15:4 | this | file://:0:0:0:0 | (A *)... | Call to pure virtual function during destruction. |
-| test.cpp:25:13:25:13 | call to f | test.cpp:21:3:21:3 | this | test.cpp:25:7:25:10 | (A *)... | Call to pure virtual function during construction. |
-| test.cpp:35:6:35:6 | call to f | test.cpp:7:3:7:3 | this | test.cpp:35:3:35:3 | (A *)... | Call to pure virtual function during construction. |
-| test.cpp:35:6:35:6 | call to f | test.cpp:21:3:21:3 | this | test.cpp:35:3:35:3 | (A *)... | Call to pure virtual function during construction. |
+| test.cpp:12:7:12:7 | call to f | test.cpp:31:3:31:3 | D | test.cpp:12:5:12:5 | (A)... | Call to pure virtual function during construction. |
+| test.cpp:16:5:16:5 | call to f | test.cpp:15:3:15:4 | ~B | file://:0:0:0:0 | (A *)... | Call to pure virtual function during destruction. |
+| test.cpp:25:13:25:13 | call to f | test.cpp:21:3:21:3 | C | test.cpp:25:7:25:10 | (A *)... | Call to pure virtual function during construction. |
+| test.cpp:35:6:35:6 | call to f | test.cpp:7:3:7:3 | B | test.cpp:35:3:35:3 | (A *)... | Call to pure virtual function during construction. |
+| test.cpp:35:6:35:6 | call to f | test.cpp:21:3:21:3 | C | test.cpp:35:3:35:3 | (A *)... | Call to pure virtual function during construction. |
diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/ReturnStackAllocatedMemory.expected b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/ReturnStackAllocatedMemory.expected
index 8f9d91fc1ad5..b7b598a13c5c 100644
--- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/ReturnStackAllocatedMemory.expected
+++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/ReturnStackAllocatedMemory.expected
@@ -1,231 +1,117 @@
edges
-| test.cpp:17:9:17:11 | & ... | test.cpp:17:9:17:11 | StoreValue |
-| test.cpp:17:10:17:11 | Unary | test.cpp:17:9:17:11 | & ... |
-| test.cpp:17:10:17:11 | mc | test.cpp:17:10:17:11 | Unary |
-| test.cpp:23:17:23:19 | & ... | test.cpp:23:17:23:19 | StoreValue |
-| test.cpp:23:17:23:19 | Store | test.cpp:25:9:25:11 | Load |
-| test.cpp:23:17:23:19 | StoreValue | test.cpp:23:17:23:19 | Store |
-| test.cpp:23:18:23:19 | Unary | test.cpp:23:17:23:19 | & ... |
-| test.cpp:23:18:23:19 | mc | test.cpp:23:18:23:19 | Unary |
-| test.cpp:25:9:25:11 | Load | test.cpp:25:9:25:11 | ptr |
-| test.cpp:25:9:25:11 | ptr | test.cpp:25:9:25:11 | StoreValue |
-| test.cpp:39:17:39:18 | (reference to) | test.cpp:39:17:39:18 | StoreValue |
-| test.cpp:39:17:39:18 | Store | test.cpp:41:10:41:12 | Load |
-| test.cpp:39:17:39:18 | StoreValue | test.cpp:39:17:39:18 | Store |
-| test.cpp:39:17:39:18 | Unary | test.cpp:39:17:39:18 | (reference to) |
-| test.cpp:39:17:39:18 | mc | test.cpp:39:17:39:18 | Unary |
-| test.cpp:41:9:41:12 | & ... | test.cpp:41:9:41:12 | StoreValue |
-| test.cpp:41:10:41:12 | (reference dereference) | test.cpp:41:10:41:12 | Unary |
-| test.cpp:41:10:41:12 | Load | test.cpp:41:10:41:12 | ref |
-| test.cpp:41:10:41:12 | Unary | test.cpp:41:9:41:12 | & ... |
-| test.cpp:41:10:41:12 | Unary | test.cpp:41:10:41:12 | (reference dereference) |
-| test.cpp:41:10:41:12 | ref | test.cpp:41:10:41:12 | Unary |
-| test.cpp:47:9:47:10 | (reference to) | test.cpp:47:9:47:10 | StoreValue |
-| test.cpp:47:9:47:10 | Unary | test.cpp:47:9:47:10 | (reference to) |
-| test.cpp:47:9:47:10 | mc | test.cpp:47:9:47:10 | Unary |
-| test.cpp:54:9:54:15 | & ... | test.cpp:54:9:54:15 | StoreValue |
-| test.cpp:54:11:54:12 | Unary | test.cpp:54:14:54:14 | a |
-| test.cpp:54:11:54:12 | mc | test.cpp:54:11:54:12 | Unary |
-| test.cpp:54:14:54:14 | Unary | test.cpp:54:9:54:15 | & ... |
-| test.cpp:54:14:54:14 | a | test.cpp:54:14:54:14 | Unary |
-| test.cpp:89:3:89:11 | Store | test.cpp:92:9:92:11 | Load |
-| test.cpp:89:9:89:11 | & ... | test.cpp:89:9:89:11 | StoreValue |
-| test.cpp:89:9:89:11 | StoreValue | test.cpp:89:3:89:11 | Store |
-| test.cpp:89:10:89:11 | Unary | test.cpp:89:9:89:11 | & ... |
-| test.cpp:89:10:89:11 | mc | test.cpp:89:10:89:11 | Unary |
-| test.cpp:92:9:92:11 | Load | test.cpp:92:9:92:11 | ptr |
-| test.cpp:92:9:92:11 | ptr | test.cpp:92:9:92:11 | StoreValue |
-| test.cpp:112:9:112:11 | Unary | test.cpp:112:9:112:11 | array to pointer conversion |
-| test.cpp:112:9:112:11 | arr | test.cpp:112:9:112:11 | Unary |
-| test.cpp:112:9:112:11 | array to pointer conversion | test.cpp:112:9:112:11 | StoreValue |
-| test.cpp:119:9:119:18 | & ... | test.cpp:119:9:119:18 | StoreValue |
-| test.cpp:119:11:119:13 | Left | test.cpp:119:11:119:17 | access to array |
-| test.cpp:119:11:119:13 | Unary | test.cpp:119:11:119:13 | array to pointer conversion |
-| test.cpp:119:11:119:13 | arr | test.cpp:119:11:119:13 | Unary |
-| test.cpp:119:11:119:13 | array to pointer conversion | test.cpp:119:11:119:13 | Left |
-| test.cpp:119:11:119:17 | Unary | test.cpp:119:9:119:18 | & ... |
-| test.cpp:119:11:119:17 | access to array | test.cpp:119:11:119:17 | Unary |
-| test.cpp:134:2:134:14 | Store | test.cpp:135:2:135:4 | Load |
-| test.cpp:134:8:134:10 | Left | test.cpp:134:8:134:14 | ... + ... |
-| test.cpp:134:8:134:10 | Unary | test.cpp:134:8:134:10 | array to pointer conversion |
-| test.cpp:134:8:134:10 | arr | test.cpp:134:8:134:10 | Unary |
-| test.cpp:134:8:134:10 | array to pointer conversion | test.cpp:134:8:134:10 | Left |
-| test.cpp:134:8:134:14 | ... + ... | test.cpp:134:8:134:14 | StoreValue |
-| test.cpp:134:8:134:14 | StoreValue | test.cpp:134:2:134:14 | Store |
-| test.cpp:135:2:135:4 | Left | test.cpp:135:2:135:6 | PointerAdd |
-| test.cpp:135:2:135:4 | Load | test.cpp:135:2:135:4 | ptr |
-| test.cpp:135:2:135:4 | ptr | test.cpp:135:2:135:4 | Left |
-| test.cpp:135:2:135:6 | PointerAdd | test.cpp:135:2:135:6 | StoreValue |
-| test.cpp:135:2:135:6 | Store | test.cpp:137:9:137:11 | Load |
-| test.cpp:135:2:135:6 | StoreValue | test.cpp:135:2:135:6 | Store |
-| test.cpp:137:9:137:11 | Load | test.cpp:137:9:137:11 | ptr |
-| test.cpp:137:9:137:11 | ptr | test.cpp:137:9:137:11 | StoreValue |
-| test.cpp:170:26:170:41 | (void *)... | test.cpp:170:26:170:41 | StoreValue |
-| test.cpp:170:26:170:41 | Store | test.cpp:171:10:171:23 | Load |
-| test.cpp:170:26:170:41 | StoreValue | test.cpp:170:26:170:41 | Store |
-| test.cpp:170:34:170:41 | & ... | test.cpp:170:34:170:41 | Unary |
-| test.cpp:170:34:170:41 | Unary | test.cpp:170:26:170:41 | (void *)... |
-| test.cpp:170:35:170:41 | Unary | test.cpp:170:34:170:41 | & ... |
-| test.cpp:170:35:170:41 | myLocal | test.cpp:170:35:170:41 | Unary |
-| test.cpp:171:10:171:23 | Load | test.cpp:171:10:171:23 | pointerToLocal |
-| test.cpp:171:10:171:23 | pointerToLocal | test.cpp:171:10:171:23 | StoreValue |
-| test.cpp:176:25:176:34 | Store | test.cpp:177:10:177:23 | Load |
-| test.cpp:176:25:176:34 | StoreValue | test.cpp:176:25:176:34 | Store |
-| test.cpp:176:25:176:34 | Unary | test.cpp:176:25:176:34 | array to pointer conversion |
-| test.cpp:176:25:176:34 | array to pointer conversion | test.cpp:176:25:176:34 | StoreValue |
-| test.cpp:176:25:176:34 | localArray | test.cpp:176:25:176:34 | Unary |
-| test.cpp:177:10:177:23 | (void *)... | test.cpp:177:10:177:23 | StoreValue |
-| test.cpp:177:10:177:23 | Load | test.cpp:177:10:177:23 | pointerToLocal |
-| test.cpp:177:10:177:23 | Unary | test.cpp:177:10:177:23 | (void *)... |
-| test.cpp:177:10:177:23 | pointerToLocal | test.cpp:177:10:177:23 | Unary |
-| test.cpp:182:21:182:27 | (reference to) | test.cpp:182:21:182:27 | StoreValue |
-| test.cpp:182:21:182:27 | Store | test.cpp:183:10:183:19 | Load |
-| test.cpp:182:21:182:27 | StoreValue | test.cpp:182:21:182:27 | Store |
-| test.cpp:182:21:182:27 | Unary | test.cpp:182:21:182:27 | (reference to) |
-| test.cpp:182:21:182:27 | myLocal | test.cpp:182:21:182:27 | Unary |
-| test.cpp:183:10:183:19 | (reference dereference) | test.cpp:183:10:183:19 | Unary |
-| test.cpp:183:10:183:19 | (reference to) | test.cpp:183:10:183:19 | StoreValue |
-| test.cpp:183:10:183:19 | Load | test.cpp:183:10:183:19 | refToLocal |
-| test.cpp:183:10:183:19 | Unary | test.cpp:183:10:183:19 | (reference dereference) |
-| test.cpp:183:10:183:19 | Unary | test.cpp:183:10:183:19 | (reference to) |
-| test.cpp:183:10:183:19 | refToLocal | test.cpp:183:10:183:19 | Unary |
-| test.cpp:189:16:189:16 | (reference to) | test.cpp:189:16:189:16 | StoreValue |
-| test.cpp:189:16:189:16 | Store | test.cpp:190:10:190:13 | Load |
-| test.cpp:189:16:189:16 | StoreValue | test.cpp:189:16:189:16 | Store |
-| test.cpp:189:16:189:16 | Unary | test.cpp:189:16:189:16 | (reference to) |
-| test.cpp:189:16:189:16 | p | test.cpp:189:16:189:16 | Unary |
-| test.cpp:190:10:190:13 | (reference dereference) | test.cpp:190:10:190:13 | Unary |
-| test.cpp:190:10:190:13 | (reference to) | test.cpp:190:10:190:13 | StoreValue |
-| test.cpp:190:10:190:13 | Load | test.cpp:190:10:190:13 | pRef |
-| test.cpp:190:10:190:13 | Unary | test.cpp:190:10:190:13 | (reference dereference) |
-| test.cpp:190:10:190:13 | Unary | test.cpp:190:10:190:13 | (reference to) |
-| test.cpp:190:10:190:13 | pRef | test.cpp:190:10:190:13 | Unary |
+| test.cpp:17:10:17:11 | mc | test.cpp:17:9:17:11 | & ... |
+| test.cpp:23:17:23:19 | & ... | test.cpp:23:17:23:19 | & ... |
+| test.cpp:23:17:23:19 | & ... | test.cpp:25:9:25:11 | ptr |
+| test.cpp:23:18:23:19 | mc | test.cpp:23:17:23:19 | & ... |
+| test.cpp:39:17:39:18 | (reference to) | test.cpp:39:17:39:18 | (reference to) |
+| test.cpp:39:17:39:18 | (reference to) | test.cpp:41:10:41:12 | ref |
+| test.cpp:39:17:39:18 | mc | test.cpp:39:17:39:18 | (reference to) |
+| test.cpp:41:10:41:12 | (reference dereference) | test.cpp:41:9:41:12 | & ... |
+| test.cpp:41:10:41:12 | ref | test.cpp:41:10:41:12 | (reference dereference) |
+| test.cpp:47:9:47:10 | mc | test.cpp:47:9:47:10 | (reference to) |
+| test.cpp:54:11:54:12 | mc | test.cpp:54:14:54:14 | a |
+| test.cpp:54:14:54:14 | a | test.cpp:54:9:54:15 | & ... |
+| test.cpp:89:3:89:11 | ... = ... | test.cpp:92:9:92:11 | ptr |
+| test.cpp:89:9:89:11 | & ... | test.cpp:89:3:89:11 | ... = ... |
+| test.cpp:89:10:89:11 | mc | test.cpp:89:9:89:11 | & ... |
+| test.cpp:112:9:112:11 | arr | test.cpp:112:9:112:11 | array to pointer conversion |
+| test.cpp:119:11:119:13 | arr | test.cpp:119:11:119:13 | array to pointer conversion |
+| test.cpp:119:11:119:13 | array to pointer conversion | test.cpp:119:11:119:17 | access to array |
+| test.cpp:119:11:119:17 | access to array | test.cpp:119:9:119:18 | & ... |
+| test.cpp:134:2:134:14 | ... = ... | test.cpp:135:2:135:4 | ptr |
+| test.cpp:134:8:134:10 | arr | test.cpp:134:8:134:10 | array to pointer conversion |
+| test.cpp:134:8:134:10 | array to pointer conversion | test.cpp:134:8:134:14 | ... + ... |
+| test.cpp:134:8:134:14 | ... + ... | test.cpp:134:2:134:14 | ... = ... |
+| test.cpp:135:2:135:4 | ptr | test.cpp:135:2:135:6 | ... ++ |
+| test.cpp:135:2:135:6 | ... ++ | test.cpp:135:2:135:6 | ... ++ |
+| test.cpp:135:2:135:6 | ... ++ | test.cpp:137:9:137:11 | ptr |
+| test.cpp:170:26:170:41 | (void *)... | test.cpp:170:26:170:41 | (void *)... |
+| test.cpp:170:26:170:41 | (void *)... | test.cpp:171:10:171:23 | pointerToLocal |
+| test.cpp:170:34:170:41 | & ... | test.cpp:170:26:170:41 | (void *)... |
+| test.cpp:170:35:170:41 | myLocal | test.cpp:170:34:170:41 | & ... |
+| test.cpp:176:25:176:34 | array to pointer conversion | test.cpp:176:25:176:34 | array to pointer conversion |
+| test.cpp:176:25:176:34 | array to pointer conversion | test.cpp:177:10:177:23 | pointerToLocal |
+| test.cpp:176:25:176:34 | localArray | test.cpp:176:25:176:34 | array to pointer conversion |
+| test.cpp:177:10:177:23 | pointerToLocal | test.cpp:177:10:177:23 | (void *)... |
+| test.cpp:182:21:182:27 | (reference to) | test.cpp:182:21:182:27 | (reference to) |
+| test.cpp:182:21:182:27 | (reference to) | test.cpp:183:10:183:19 | refToLocal |
+| test.cpp:182:21:182:27 | myLocal | test.cpp:182:21:182:27 | (reference to) |
+| test.cpp:183:10:183:19 | (reference dereference) | test.cpp:183:10:183:19 | (reference to) |
+| test.cpp:183:10:183:19 | refToLocal | test.cpp:183:10:183:19 | (reference dereference) |
+| test.cpp:189:16:189:16 | (reference to) | test.cpp:189:16:189:16 | (reference to) |
+| test.cpp:189:16:189:16 | (reference to) | test.cpp:190:10:190:13 | pRef |
+| test.cpp:189:16:189:16 | p | test.cpp:189:16:189:16 | (reference to) |
+| test.cpp:190:10:190:13 | (reference dereference) | test.cpp:190:10:190:13 | (reference to) |
+| test.cpp:190:10:190:13 | pRef | test.cpp:190:10:190:13 | (reference dereference) |
nodes
| test.cpp:17:9:17:11 | & ... | semmle.label | & ... |
-| test.cpp:17:9:17:11 | StoreValue | semmle.label | StoreValue |
-| test.cpp:17:10:17:11 | Unary | semmle.label | Unary |
| test.cpp:17:10:17:11 | mc | semmle.label | mc |
| test.cpp:23:17:23:19 | & ... | semmle.label | & ... |
-| test.cpp:23:17:23:19 | Store | semmle.label | Store |
-| test.cpp:23:17:23:19 | StoreValue | semmle.label | StoreValue |
-| test.cpp:23:18:23:19 | Unary | semmle.label | Unary |
+| test.cpp:23:17:23:19 | & ... | semmle.label | & ... |
| test.cpp:23:18:23:19 | mc | semmle.label | mc |
-| test.cpp:25:9:25:11 | Load | semmle.label | Load |
-| test.cpp:25:9:25:11 | StoreValue | semmle.label | StoreValue |
| test.cpp:25:9:25:11 | ptr | semmle.label | ptr |
| test.cpp:39:17:39:18 | (reference to) | semmle.label | (reference to) |
-| test.cpp:39:17:39:18 | Store | semmle.label | Store |
-| test.cpp:39:17:39:18 | StoreValue | semmle.label | StoreValue |
-| test.cpp:39:17:39:18 | Unary | semmle.label | Unary |
+| test.cpp:39:17:39:18 | (reference to) | semmle.label | (reference to) |
| test.cpp:39:17:39:18 | mc | semmle.label | mc |
| test.cpp:41:9:41:12 | & ... | semmle.label | & ... |
-| test.cpp:41:9:41:12 | StoreValue | semmle.label | StoreValue |
| test.cpp:41:10:41:12 | (reference dereference) | semmle.label | (reference dereference) |
-| test.cpp:41:10:41:12 | Load | semmle.label | Load |
-| test.cpp:41:10:41:12 | Unary | semmle.label | Unary |
-| test.cpp:41:10:41:12 | Unary | semmle.label | Unary |
| test.cpp:41:10:41:12 | ref | semmle.label | ref |
| test.cpp:47:9:47:10 | (reference to) | semmle.label | (reference to) |
-| test.cpp:47:9:47:10 | StoreValue | semmle.label | StoreValue |
-| test.cpp:47:9:47:10 | Unary | semmle.label | Unary |
| test.cpp:47:9:47:10 | mc | semmle.label | mc |
| test.cpp:54:9:54:15 | & ... | semmle.label | & ... |
-| test.cpp:54:9:54:15 | StoreValue | semmle.label | StoreValue |
-| test.cpp:54:11:54:12 | Unary | semmle.label | Unary |
| test.cpp:54:11:54:12 | mc | semmle.label | mc |
-| test.cpp:54:14:54:14 | Unary | semmle.label | Unary |
| test.cpp:54:14:54:14 | a | semmle.label | a |
-| test.cpp:89:3:89:11 | Store | semmle.label | Store |
+| test.cpp:89:3:89:11 | ... = ... | semmle.label | ... = ... |
| test.cpp:89:9:89:11 | & ... | semmle.label | & ... |
-| test.cpp:89:9:89:11 | StoreValue | semmle.label | StoreValue |
-| test.cpp:89:10:89:11 | Unary | semmle.label | Unary |
| test.cpp:89:10:89:11 | mc | semmle.label | mc |
-| test.cpp:92:9:92:11 | Load | semmle.label | Load |
-| test.cpp:92:9:92:11 | StoreValue | semmle.label | StoreValue |
| test.cpp:92:9:92:11 | ptr | semmle.label | ptr |
-| test.cpp:112:9:112:11 | StoreValue | semmle.label | StoreValue |
-| test.cpp:112:9:112:11 | Unary | semmle.label | Unary |
| test.cpp:112:9:112:11 | arr | semmle.label | arr |
| test.cpp:112:9:112:11 | array to pointer conversion | semmle.label | array to pointer conversion |
| test.cpp:119:9:119:18 | & ... | semmle.label | & ... |
-| test.cpp:119:9:119:18 | StoreValue | semmle.label | StoreValue |
-| test.cpp:119:11:119:13 | Left | semmle.label | Left |
-| test.cpp:119:11:119:13 | Unary | semmle.label | Unary |
| test.cpp:119:11:119:13 | arr | semmle.label | arr |
| test.cpp:119:11:119:13 | array to pointer conversion | semmle.label | array to pointer conversion |
-| test.cpp:119:11:119:17 | Unary | semmle.label | Unary |
| test.cpp:119:11:119:17 | access to array | semmle.label | access to array |
-| test.cpp:134:2:134:14 | Store | semmle.label | Store |
-| test.cpp:134:8:134:10 | Left | semmle.label | Left |
-| test.cpp:134:8:134:10 | Unary | semmle.label | Unary |
+| test.cpp:134:2:134:14 | ... = ... | semmle.label | ... = ... |
| test.cpp:134:8:134:10 | arr | semmle.label | arr |
| test.cpp:134:8:134:10 | array to pointer conversion | semmle.label | array to pointer conversion |
| test.cpp:134:8:134:14 | ... + ... | semmle.label | ... + ... |
-| test.cpp:134:8:134:14 | StoreValue | semmle.label | StoreValue |
-| test.cpp:135:2:135:4 | Left | semmle.label | Left |
-| test.cpp:135:2:135:4 | Load | semmle.label | Load |
| test.cpp:135:2:135:4 | ptr | semmle.label | ptr |
-| test.cpp:135:2:135:6 | PointerAdd | semmle.label | PointerAdd |
-| test.cpp:135:2:135:6 | Store | semmle.label | Store |
-| test.cpp:135:2:135:6 | StoreValue | semmle.label | StoreValue |
-| test.cpp:137:9:137:11 | Load | semmle.label | Load |
-| test.cpp:137:9:137:11 | StoreValue | semmle.label | StoreValue |
+| test.cpp:135:2:135:6 | ... ++ | semmle.label | ... ++ |
+| test.cpp:135:2:135:6 | ... ++ | semmle.label | ... ++ |
| test.cpp:137:9:137:11 | ptr | semmle.label | ptr |
| test.cpp:170:26:170:41 | (void *)... | semmle.label | (void *)... |
-| test.cpp:170:26:170:41 | Store | semmle.label | Store |
-| test.cpp:170:26:170:41 | StoreValue | semmle.label | StoreValue |
+| test.cpp:170:26:170:41 | (void *)... | semmle.label | (void *)... |
| test.cpp:170:34:170:41 | & ... | semmle.label | & ... |
-| test.cpp:170:34:170:41 | Unary | semmle.label | Unary |
-| test.cpp:170:35:170:41 | Unary | semmle.label | Unary |
| test.cpp:170:35:170:41 | myLocal | semmle.label | myLocal |
-| test.cpp:171:10:171:23 | Load | semmle.label | Load |
-| test.cpp:171:10:171:23 | StoreValue | semmle.label | StoreValue |
| test.cpp:171:10:171:23 | pointerToLocal | semmle.label | pointerToLocal |
-| test.cpp:176:25:176:34 | Store | semmle.label | Store |
-| test.cpp:176:25:176:34 | StoreValue | semmle.label | StoreValue |
-| test.cpp:176:25:176:34 | Unary | semmle.label | Unary |
+| test.cpp:176:25:176:34 | array to pointer conversion | semmle.label | array to pointer conversion |
| test.cpp:176:25:176:34 | array to pointer conversion | semmle.label | array to pointer conversion |
| test.cpp:176:25:176:34 | localArray | semmle.label | localArray |
| test.cpp:177:10:177:23 | (void *)... | semmle.label | (void *)... |
-| test.cpp:177:10:177:23 | Load | semmle.label | Load |
-| test.cpp:177:10:177:23 | StoreValue | semmle.label | StoreValue |
-| test.cpp:177:10:177:23 | Unary | semmle.label | Unary |
| test.cpp:177:10:177:23 | pointerToLocal | semmle.label | pointerToLocal |
| test.cpp:182:21:182:27 | (reference to) | semmle.label | (reference to) |
-| test.cpp:182:21:182:27 | Store | semmle.label | Store |
-| test.cpp:182:21:182:27 | StoreValue | semmle.label | StoreValue |
-| test.cpp:182:21:182:27 | Unary | semmle.label | Unary |
+| test.cpp:182:21:182:27 | (reference to) | semmle.label | (reference to) |
| test.cpp:182:21:182:27 | myLocal | semmle.label | myLocal |
| test.cpp:183:10:183:19 | (reference dereference) | semmle.label | (reference dereference) |
| test.cpp:183:10:183:19 | (reference to) | semmle.label | (reference to) |
-| test.cpp:183:10:183:19 | Load | semmle.label | Load |
-| test.cpp:183:10:183:19 | StoreValue | semmle.label | StoreValue |
-| test.cpp:183:10:183:19 | Unary | semmle.label | Unary |
-| test.cpp:183:10:183:19 | Unary | semmle.label | Unary |
| test.cpp:183:10:183:19 | refToLocal | semmle.label | refToLocal |
| test.cpp:189:16:189:16 | (reference to) | semmle.label | (reference to) |
-| test.cpp:189:16:189:16 | Store | semmle.label | Store |
-| test.cpp:189:16:189:16 | StoreValue | semmle.label | StoreValue |
-| test.cpp:189:16:189:16 | Unary | semmle.label | Unary |
+| test.cpp:189:16:189:16 | (reference to) | semmle.label | (reference to) |
| test.cpp:189:16:189:16 | p | semmle.label | p |
| test.cpp:190:10:190:13 | (reference dereference) | semmle.label | (reference dereference) |
| test.cpp:190:10:190:13 | (reference to) | semmle.label | (reference to) |
-| test.cpp:190:10:190:13 | Load | semmle.label | Load |
-| test.cpp:190:10:190:13 | StoreValue | semmle.label | StoreValue |
-| test.cpp:190:10:190:13 | Unary | semmle.label | Unary |
-| test.cpp:190:10:190:13 | Unary | semmle.label | Unary |
| test.cpp:190:10:190:13 | pRef | semmle.label | pRef |
#select
-| test.cpp:17:9:17:11 | StoreValue | test.cpp:17:10:17:11 | mc | test.cpp:17:9:17:11 | StoreValue | May return stack-allocated memory from $@. | test.cpp:17:10:17:11 | mc | mc |
-| test.cpp:25:9:25:11 | StoreValue | test.cpp:23:18:23:19 | mc | test.cpp:25:9:25:11 | StoreValue | May return stack-allocated memory from $@. | test.cpp:23:18:23:19 | mc | mc |
-| test.cpp:41:9:41:12 | StoreValue | test.cpp:39:17:39:18 | mc | test.cpp:41:9:41:12 | StoreValue | May return stack-allocated memory from $@. | test.cpp:39:17:39:18 | mc | mc |
-| test.cpp:47:9:47:10 | StoreValue | test.cpp:47:9:47:10 | mc | test.cpp:47:9:47:10 | StoreValue | May return stack-allocated memory from $@. | test.cpp:47:9:47:10 | mc | mc |
-| test.cpp:54:9:54:15 | StoreValue | test.cpp:54:11:54:12 | mc | test.cpp:54:9:54:15 | StoreValue | May return stack-allocated memory from $@. | test.cpp:54:11:54:12 | mc | mc |
-| test.cpp:92:9:92:11 | StoreValue | test.cpp:89:10:89:11 | mc | test.cpp:92:9:92:11 | StoreValue | May return stack-allocated memory from $@. | test.cpp:89:10:89:11 | mc | mc |
-| test.cpp:112:9:112:11 | StoreValue | test.cpp:112:9:112:11 | arr | test.cpp:112:9:112:11 | StoreValue | May return stack-allocated memory from $@. | test.cpp:112:9:112:11 | arr | arr |
-| test.cpp:119:9:119:18 | StoreValue | test.cpp:119:11:119:13 | arr | test.cpp:119:9:119:18 | StoreValue | May return stack-allocated memory from $@. | test.cpp:119:11:119:13 | arr | arr |
-| test.cpp:137:9:137:11 | StoreValue | test.cpp:134:8:134:10 | arr | test.cpp:137:9:137:11 | StoreValue | May return stack-allocated memory from $@. | test.cpp:134:8:134:10 | arr | arr |
-| test.cpp:171:10:171:23 | StoreValue | test.cpp:170:35:170:41 | myLocal | test.cpp:171:10:171:23 | StoreValue | May return stack-allocated memory from $@. | test.cpp:170:35:170:41 | myLocal | myLocal |
-| test.cpp:177:10:177:23 | StoreValue | test.cpp:176:25:176:34 | localArray | test.cpp:177:10:177:23 | StoreValue | May return stack-allocated memory from $@. | test.cpp:176:25:176:34 | localArray | localArray |
-| test.cpp:183:10:183:19 | StoreValue | test.cpp:182:21:182:27 | myLocal | test.cpp:183:10:183:19 | StoreValue | May return stack-allocated memory from $@. | test.cpp:182:21:182:27 | myLocal | myLocal |
-| test.cpp:190:10:190:13 | StoreValue | test.cpp:189:16:189:16 | p | test.cpp:190:10:190:13 | StoreValue | May return stack-allocated memory from $@. | test.cpp:189:16:189:16 | p | p |
+| test.cpp:17:9:17:11 | CopyValue: & ... | test.cpp:17:10:17:11 | mc | test.cpp:17:9:17:11 | & ... | May return stack-allocated memory from $@. | test.cpp:17:10:17:11 | mc | mc |
+| test.cpp:25:9:25:11 | Load: ptr | test.cpp:23:18:23:19 | mc | test.cpp:25:9:25:11 | ptr | May return stack-allocated memory from $@. | test.cpp:23:18:23:19 | mc | mc |
+| test.cpp:41:9:41:12 | CopyValue: & ... | test.cpp:39:17:39:18 | mc | test.cpp:41:9:41:12 | & ... | May return stack-allocated memory from $@. | test.cpp:39:17:39:18 | mc | mc |
+| test.cpp:47:9:47:10 | CopyValue: (reference to) | test.cpp:47:9:47:10 | mc | test.cpp:47:9:47:10 | (reference to) | May return stack-allocated memory from $@. | test.cpp:47:9:47:10 | mc | mc |
+| test.cpp:54:9:54:15 | CopyValue: & ... | test.cpp:54:11:54:12 | mc | test.cpp:54:9:54:15 | & ... | May return stack-allocated memory from $@. | test.cpp:54:11:54:12 | mc | mc |
+| test.cpp:92:9:92:11 | Load: ptr | test.cpp:89:10:89:11 | mc | test.cpp:92:9:92:11 | ptr | May return stack-allocated memory from $@. | test.cpp:89:10:89:11 | mc | mc |
+| test.cpp:112:9:112:11 | Convert: array to pointer conversion | test.cpp:112:9:112:11 | arr | test.cpp:112:9:112:11 | array to pointer conversion | May return stack-allocated memory from $@. | test.cpp:112:9:112:11 | arr | arr |
+| test.cpp:119:9:119:18 | CopyValue: & ... | test.cpp:119:11:119:13 | arr | test.cpp:119:9:119:18 | & ... | May return stack-allocated memory from $@. | test.cpp:119:11:119:13 | arr | arr |
+| test.cpp:137:9:137:11 | Load: ptr | test.cpp:134:8:134:10 | arr | test.cpp:137:9:137:11 | ptr | May return stack-allocated memory from $@. | test.cpp:134:8:134:10 | arr | arr |
+| test.cpp:171:10:171:23 | Load: pointerToLocal | test.cpp:170:35:170:41 | myLocal | test.cpp:171:10:171:23 | pointerToLocal | May return stack-allocated memory from $@. | test.cpp:170:35:170:41 | myLocal | myLocal |
+| test.cpp:177:10:177:23 | Convert: (void *)... | test.cpp:176:25:176:34 | localArray | test.cpp:177:10:177:23 | (void *)... | May return stack-allocated memory from $@. | test.cpp:176:25:176:34 | localArray | localArray |
+| test.cpp:183:10:183:19 | CopyValue: (reference to) | test.cpp:182:21:182:27 | myLocal | test.cpp:183:10:183:19 | (reference to) | May return stack-allocated memory from $@. | test.cpp:182:21:182:27 | myLocal | myLocal |
+| test.cpp:190:10:190:13 | CopyValue: (reference to) | test.cpp:189:16:189:16 | p | test.cpp:190:10:190:13 | (reference to) | May return stack-allocated memory from $@. | test.cpp:189:16:189:16 | p | p |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp
index b11a136ed24a..b1245c6ae891 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp
@@ -332,4 +332,13 @@ void ptr_diff_case() {
char* admin_begin_pos = strstr(user, "ADMIN");
int offset = admin_begin_pos ? user - admin_begin_pos : 0;
malloc(offset); // GOOD
-}
\ No newline at end of file
+}
+
+void equality_barrier() {
+ int size1 = atoi(getenv("USER"));
+ int size2 = atoi(getenv("USER"));
+
+ if (size1 == size2) {
+ int* a = (int*)malloc(size1 * sizeof(int)); // GOOD
+ }
+}
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test.c b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test.c
index 6999795b004f..29ab7cc8f25c 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test.c
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/test.c
@@ -95,5 +95,12 @@ int main(int argc, char** argv) {
}
}
+ // GOOD: check the user input first
+ int maxConnections3 = atoi(argv[1]);
+ int maxConnections4 = atoi(argv[1]);
+ if (maxConnections3 == maxConnections4) {
+ startServer(maxConnections3 * 1000);
+ }
+
return 0;
}
diff --git a/csharp/.gitignore b/csharp/.gitignore
index 0701c11fe1d8..a030c9444fe9 100644
--- a/csharp/.gitignore
+++ b/csharp/.gitignore
@@ -11,4 +11,7 @@ csharp.log
*.tlog
.vs
*.user
-.vscode/launch.json
\ No newline at end of file
+.vscode/launch.json
+
+extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json
+extractor-pack
\ No newline at end of file
diff --git a/csharp/actions/create-extractor-pack/action.yml b/csharp/actions/create-extractor-pack/action.yml
new file mode 100644
index 000000000000..43b0ec9c6fe5
--- /dev/null
+++ b/csharp/actions/create-extractor-pack/action.yml
@@ -0,0 +1,13 @@
+name: Build C# CodeQL pack
+description: Builds the C# CodeQL pack
+runs:
+ using: composite
+ steps:
+ - name: Setup dotnet
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: 6.0.202
+ - name: Build Extractor
+ shell: bash
+ run: scripts/create-extractor-pack.sh
+ working-directory: csharp
diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs
index 9afe24ed72f4..df362c2a129a 100644
--- a/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs
+++ b/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests/BuildScripts.cs
@@ -403,7 +403,7 @@ private CSharpAutobuilder CreateAutoBuilder(bool isWindows,
actions.GetCurrentDirectory = cwd;
actions.IsWindows = isWindows;
- var options = new AutobuildOptions(actions, Language.CSharp);
+ var options = new CSharpAutobuildOptions(actions);
return new CSharpAutobuilder(actions, options);
}
@@ -576,7 +576,7 @@ private void SkipVsWhere()
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = false;
}
- private void TestAutobuilderScript(Autobuilder autobuilder, int expectedOutput, int commandsRun)
+ private void TestAutobuilderScript(CSharpAutobuilder autobuilder, int expectedOutput, int commandsRun)
{
Assert.Equal(expectedOutput, autobuilder.GetBuildScript().Run(actions, StartCallback, EndCallback));
diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs
index ff6d2c804cc5..71891fa4e8b0 100644
--- a/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs
+++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/CSharpAutobuilder.cs
@@ -4,9 +4,32 @@
namespace Semmle.Autobuild.CSharp
{
- public class CSharpAutobuilder : Autobuilder
+ ///
+ /// Encapsulates C# build options.
+ ///
+ public class CSharpAutobuildOptions : AutobuildOptionsShared
{
- public CSharpAutobuilder(IBuildActions actions, AutobuildOptions options) : base(actions, options) { }
+ private const string extractorOptionPrefix = "CODEQL_EXTRACTOR_CSHARP_OPTION_";
+
+ public bool Buildless { get; }
+
+ public override Language Language => Language.CSharp;
+
+
+ ///
+ /// Reads options from environment variables.
+ /// Throws ArgumentOutOfRangeException for invalid arguments.
+ ///
+ public CSharpAutobuildOptions(IBuildActions actions) : base(actions)
+ {
+ Buildless = actions.GetEnvironmentVariable(lgtmPrefix + "BUILDLESS").AsBool("buildless", false) ||
+ actions.GetEnvironmentVariable(extractorOptionPrefix + "BUILDLESS").AsBool("buildless", false);
+ }
+ }
+
+ public class CSharpAutobuilder : Autobuilder
+ {
+ public CSharpAutobuilder(IBuildActions actions, CSharpAutobuildOptions options) : base(actions, options) { }
public override BuildScript GetBuildScript()
{
diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs
index 163dbfa1464e..394349e2a407 100644
--- a/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs
+++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/DotNetRule.cs
@@ -13,9 +13,9 @@ namespace Semmle.Autobuild.CSharp
/// A build rule where the build command is of the form "dotnet build".
/// Currently unused because the tracer does not work with dotnet.
///
- internal class DotNetRule : IBuildRule
+ internal class DotNetRule : IBuildRule
{
- public BuildScript Analyse(Autobuilder builder, bool auto)
+ public BuildScript Analyse(IAutobuilder builder, bool auto)
{
if (!builder.ProjectsOrSolutionsToBuild.Any())
return BuildScript.Failure;
@@ -24,7 +24,7 @@ public BuildScript Analyse(Autobuilder builder, bool auto)
{
var notDotNetProject = builder.ProjectsOrSolutionsToBuild
.SelectMany(p => Enumerators.Singleton(p).Concat(p.IncludedProjects))
- .OfType()
+ .OfType>()
.FirstOrDefault(p => !p.DotNetProject);
if (notDotNetProject is not null)
{
@@ -56,7 +56,7 @@ public BuildScript Analyse(Autobuilder builder, bool auto)
});
}
- private static BuildScript WithDotNet(Autobuilder builder, Func?, BuildScript> f)
+ private static BuildScript WithDotNet(IAutobuilder builder, Func?, BuildScript> f)
{
var installDir = builder.Actions.PathCombine(builder.Options.RootDirectory, ".dotnet");
var installScript = DownloadDotNet(builder, installDir);
@@ -92,7 +92,7 @@ private static BuildScript WithDotNet(Autobuilder builder, Funcnull when no variables
/// are needed).
///
- public static BuildScript WithDotNet(Autobuilder builder, Func?, BuildScript> f)
+ public static BuildScript WithDotNet(IAutobuilder builder, Func?, BuildScript> f)
=> WithDotNet(builder, (_1, env) => f(env));
///
@@ -100,7 +100,7 @@ public static BuildScript WithDotNet(Autobuilder builder, FuncinstallDir
/// (provided that the script succeeds).
///
- private static BuildScript DownloadDotNet(Autobuilder builder, string installDir)
+ private static BuildScript DownloadDotNet(IAutobuilder builder, string installDir)
{
if (!string.IsNullOrEmpty(builder.Options.DotNetVersion))
// Specific version supplied in configuration: always use that
@@ -137,7 +137,7 @@ private static BuildScript DownloadDotNet(Autobuilder builder, string installDir
///
/// See https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script.
///
- private static BuildScript DownloadDotNetVersion(Autobuilder builder, string path, string version)
+ private static BuildScript DownloadDotNetVersion(IAutobuilder builder, string path, string version)
{
return BuildScript.Bind(GetInstalledSdksScript(builder.Actions), (sdks, sdksRet) =>
{
@@ -233,7 +233,7 @@ private static CommandBuilder GetRestoreCommand(IBuildActions actions, string? d
///
/// Gets the `dotnet build` script.
///
- private static BuildScript GetBuildScript(Autobuilder builder, string? dotNetPath, IDictionary? environment, string projOrSln)
+ private static BuildScript GetBuildScript(IAutobuilder builder, string? dotNetPath, IDictionary? environment, string projOrSln)
{
var build = new CommandBuilder(builder.Actions, null, environment);
var script = build.RunCommand(DotNetCommand(builder.Actions, dotNetPath)).
diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/Program.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/Program.cs
index 2233557af16d..479625c76e33 100644
--- a/csharp/autobuilder/Semmle.Autobuild.CSharp/Program.cs
+++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/Program.cs
@@ -11,7 +11,7 @@ public static int Main()
try
{
var actions = SystemBuildActions.Instance;
- var options = new AutobuildOptions(actions, Language.CSharp);
+ var options = new CSharpAutobuildOptions(actions);
try
{
Console.WriteLine("CodeQL C# autobuilder");
diff --git a/csharp/autobuilder/Semmle.Autobuild.CSharp/StandaloneBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild.CSharp/StandaloneBuildRule.cs
index fa6523e37aed..207ecd70f0ab 100644
--- a/csharp/autobuilder/Semmle.Autobuild.CSharp/StandaloneBuildRule.cs
+++ b/csharp/autobuilder/Semmle.Autobuild.CSharp/StandaloneBuildRule.cs
@@ -6,9 +6,9 @@ namespace Semmle.Autobuild.CSharp
///
/// Build using standalone extraction.
///
- internal class StandaloneBuildRule : IBuildRule
+ internal class StandaloneBuildRule : IBuildRule
{
- public BuildScript Analyse(Autobuilder builder, bool auto)
+ public BuildScript Analyse(IAutobuilder builder, bool auto)
{
BuildScript GetCommand(string? solution)
{
diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs
index ed20bb929ff4..d51612272d05 100644
--- a/csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs
+++ b/csharp/autobuilder/Semmle.Autobuild.Shared/AutobuildOptions.cs
@@ -6,12 +6,12 @@
namespace Semmle.Autobuild.Shared
{
///
- /// Encapsulates build options.
+ /// Encapsulates build options shared between C# and C++.
///
- public class AutobuildOptions
+ public abstract class AutobuildOptionsShared
{
- private const string lgtmPrefix = "LGTM_INDEX_";
- private const string extractorOptionPrefix = "CODEQL_EXTRACTOR_CSHARP_OPTION_";
+ protected const string lgtmPrefix = "LGTM_INDEX_";
+
public int SearchDepth { get; } = 3;
public string RootDirectory { get; }
@@ -25,16 +25,16 @@ public class AutobuildOptions
public string? BuildCommand { get; }
public IEnumerable Solution { get; }
public bool IgnoreErrors { get; }
- public bool Buildless { get; }
+
public bool AllSolutions { get; }
public bool NugetRestore { get; }
- public Language Language { get; }
+ public abstract Language Language { get; }
///
/// Reads options from environment variables.
/// Throws ArgumentOutOfRangeException for invalid arguments.
///
- public AutobuildOptions(IBuildActions actions, Language language)
+ public AutobuildOptionsShared(IBuildActions actions)
{
RootDirectory = actions.GetCurrentDirectory();
VsToolsVersion = actions.GetEnvironmentVariable(lgtmPrefix + "VSTOOLS_VERSION");
@@ -48,12 +48,8 @@ public AutobuildOptions(IBuildActions actions, Language language)
Solution = actions.GetEnvironmentVariable(lgtmPrefix + "SOLUTION").AsListWithExpandedEnvVars(actions, Array.Empty());
IgnoreErrors = actions.GetEnvironmentVariable(lgtmPrefix + "IGNORE_ERRORS").AsBool("ignore_errors", false);
- Buildless = actions.GetEnvironmentVariable(lgtmPrefix + "BUILDLESS").AsBool("buildless", false) ||
- actions.GetEnvironmentVariable(extractorOptionPrefix + "BUILDLESS").AsBool("buildless", false);
AllSolutions = actions.GetEnvironmentVariable(lgtmPrefix + "ALL_SOLUTIONS").AsBool("all_solutions", false);
NugetRestore = actions.GetEnvironmentVariable(lgtmPrefix + "NUGET_RESTORE").AsBool("nuget_restore", true);
-
- Language = language;
}
}
diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs
index d90175d245a9..1ef5ebd815d5 100644
--- a/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs
+++ b/csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs
@@ -9,21 +9,21 @@ namespace Semmle.Autobuild.Shared
///
/// A build rule analyses the files in "builder" and outputs a build script.
///
- public interface IBuildRule
+ public interface IBuildRule where TAutobuildOptions : AutobuildOptionsShared
{
///
/// Analyse the files and produce a build script.
///
/// The files and options relating to the build.
/// Whether this build rule is being automatically applied.
- BuildScript Analyse(Autobuilder builder, bool auto);
+ BuildScript Analyse(IAutobuilder builder, bool auto);
}
///
/// A delegate used to wrap a build script in an environment where an appropriate
/// version of .NET Core is automatically installed.
///
- public delegate BuildScript WithDotNet(Autobuilder builder, Func?, BuildScript> f);
+ public delegate BuildScript WithDotNet(IAutobuilder builder, Func?, BuildScript> f) where TAutobuildOptions : AutobuildOptionsShared;
///
/// Exception indicating that environment variables are missing or invalid.
@@ -33,6 +33,59 @@ public class InvalidEnvironmentException : Exception
public InvalidEnvironmentException(string m) : base(m) { }
}
+ public interface IAutobuilder where TAutobuildOptions : AutobuildOptionsShared
+ {
+ ///
+ /// Full file paths of files found in the project directory, as well as
+ /// their distance from the project root folder. The list is sorted
+ /// by distance in ascending order.
+ ///
+ IEnumerable<(string, int)> Paths { get; }
+
+ ///
+ /// Gets all paths matching a particular filename, as well as
+ /// their distance from the project root folder. The list is sorted
+ /// by distance in ascending order.
+ ///
+ /// The filename to find.
+ /// Possibly empty sequence of paths with the given filename.
+ IEnumerable<(string, int)> GetFilename(string name) =>
+ Paths.Where(p => Actions.GetFileName(p.Item1) == name);
+
+ ///
+ /// List of project/solution files to build.
+ ///
+ IList ProjectsOrSolutionsToBuild { get; }
+
+ ///
+ /// Gets the supplied build configuration.
+ ///
+ TAutobuildOptions Options { get; }
+
+ ///
+ /// The set of build actions used during the autobuilder.
+ /// Could be real system operations, or a stub for testing.
+ ///
+ IBuildActions Actions { get; }
+
+ ///
+ /// Log a given build event to the console.
+ ///
+ /// The format string.
+ /// Inserts to the format string.
+ void Log(Severity severity, string format, params object[] args);
+
+ ///
+ /// Value of CODEQL_EXTRACTOR_